dice-proc.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * dice_proc.c - a part of driver for Dice based devices
  4. *
  5. * Copyright (c) Clemens Ladisch
  6. * Copyright (c) 2014 Takashi Sakamoto
  7. */
  8. #include "dice.h"
  9. static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
  10. unsigned int offset_q, unsigned int quadlets)
  11. {
  12. unsigned int i;
  13. int err;
  14. err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
  15. DICE_PRIVATE_SPACE + 4 * offset_q,
  16. buffer, 4 * quadlets, 0);
  17. if (err < 0)
  18. return err;
  19. for (i = 0; i < quadlets; ++i)
  20. be32_to_cpus(&((u32 *)buffer)[i]);
  21. return 0;
  22. }
  23. static const char *str_from_array(const char *const strs[], unsigned int count,
  24. unsigned int i)
  25. {
  26. if (i < count)
  27. return strs[i];
  28. return "(unknown)";
  29. }
  30. static void dice_proc_fixup_string(char *s, unsigned int size)
  31. {
  32. unsigned int i;
  33. for (i = 0; i < size; i += 4)
  34. cpu_to_le32s((u32 *)(s + i));
  35. for (i = 0; i < size - 2; ++i) {
  36. if (s[i] == '\0')
  37. return;
  38. if (s[i] == '\\' && s[i + 1] == '\\') {
  39. s[i + 2] = '\0';
  40. return;
  41. }
  42. }
  43. s[size - 1] = '\0';
  44. }
  45. static void dice_proc_read(struct snd_info_entry *entry,
  46. struct snd_info_buffer *buffer)
  47. {
  48. static const char *const section_names[5] = {
  49. "global", "tx", "rx", "ext_sync", "unused2"
  50. };
  51. static const char *const clock_sources[] = {
  52. "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
  53. "wc", "arx1", "arx2", "arx3", "arx4", "internal"
  54. };
  55. static const char *const rates[] = {
  56. "32000", "44100", "48000", "88200", "96000", "176400", "192000",
  57. "any low", "any mid", "any high", "none"
  58. };
  59. struct snd_dice *dice = entry->private_data;
  60. u32 sections[ARRAY_SIZE(section_names) * 2];
  61. struct {
  62. u32 number;
  63. u32 size;
  64. } tx_rx_header;
  65. union {
  66. struct {
  67. u32 owner_hi, owner_lo;
  68. u32 notification;
  69. char nick_name[NICK_NAME_SIZE];
  70. u32 clock_select;
  71. u32 enable;
  72. u32 status;
  73. u32 extended_status;
  74. u32 sample_rate;
  75. u32 version;
  76. u32 clock_caps;
  77. char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
  78. } global;
  79. struct {
  80. u32 iso;
  81. u32 number_audio;
  82. u32 number_midi;
  83. u32 speed;
  84. char names[TX_NAMES_SIZE];
  85. u32 ac3_caps;
  86. u32 ac3_enable;
  87. } tx;
  88. struct {
  89. u32 iso;
  90. u32 seq_start;
  91. u32 number_audio;
  92. u32 number_midi;
  93. char names[RX_NAMES_SIZE];
  94. u32 ac3_caps;
  95. u32 ac3_enable;
  96. } rx;
  97. struct {
  98. u32 clock_source;
  99. u32 locked;
  100. u32 rate;
  101. u32 adat_user_data;
  102. } ext_sync;
  103. } buf;
  104. unsigned int quadlets, stream, i;
  105. if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
  106. return;
  107. snd_iprintf(buffer, "sections:\n");
  108. for (i = 0; i < ARRAY_SIZE(section_names); ++i)
  109. snd_iprintf(buffer, " %s: offset %u, size %u\n",
  110. section_names[i],
  111. sections[i * 2], sections[i * 2 + 1]);
  112. quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
  113. if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
  114. return;
  115. snd_iprintf(buffer, "global:\n");
  116. snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
  117. buf.global.owner_hi >> 16,
  118. buf.global.owner_hi & 0xffff, buf.global.owner_lo);
  119. snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
  120. dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
  121. snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
  122. snd_iprintf(buffer, " clock select: %s %s\n",
  123. str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
  124. buf.global.clock_select & CLOCK_SOURCE_MASK),
  125. str_from_array(rates, ARRAY_SIZE(rates),
  126. (buf.global.clock_select & CLOCK_RATE_MASK)
  127. >> CLOCK_RATE_SHIFT));
  128. snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
  129. snd_iprintf(buffer, " status: %slocked %s\n",
  130. buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
  131. str_from_array(rates, ARRAY_SIZE(rates),
  132. (buf.global.status &
  133. STATUS_NOMINAL_RATE_MASK)
  134. >> CLOCK_RATE_SHIFT));
  135. snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
  136. snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
  137. if (quadlets >= 90) {
  138. snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
  139. (buf.global.version >> 24) & 0xff,
  140. (buf.global.version >> 16) & 0xff,
  141. (buf.global.version >> 8) & 0xff,
  142. (buf.global.version >> 0) & 0xff);
  143. snd_iprintf(buffer, " clock caps:");
  144. for (i = 0; i <= 6; ++i)
  145. if (buf.global.clock_caps & (1 << i))
  146. snd_iprintf(buffer, " %s", rates[i]);
  147. for (i = 0; i <= 12; ++i)
  148. if (buf.global.clock_caps & (1 << (16 + i)))
  149. snd_iprintf(buffer, " %s", clock_sources[i]);
  150. snd_iprintf(buffer, "\n");
  151. dice_proc_fixup_string(buf.global.clock_source_names,
  152. CLOCK_SOURCE_NAMES_SIZE);
  153. snd_iprintf(buffer, " clock source names: %s\n",
  154. buf.global.clock_source_names);
  155. }
  156. if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
  157. return;
  158. quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
  159. for (stream = 0; stream < tx_rx_header.number; ++stream) {
  160. if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
  161. stream * tx_rx_header.size,
  162. quadlets) < 0)
  163. break;
  164. snd_iprintf(buffer, "tx %u:\n", stream);
  165. snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
  166. snd_iprintf(buffer, " audio channels: %u\n",
  167. buf.tx.number_audio);
  168. snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
  169. snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
  170. if (quadlets >= 68) {
  171. dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
  172. snd_iprintf(buffer, " names: %s\n", buf.tx.names);
  173. }
  174. if (quadlets >= 70) {
  175. snd_iprintf(buffer, " ac3 caps: %08x\n",
  176. buf.tx.ac3_caps);
  177. snd_iprintf(buffer, " ac3 enable: %08x\n",
  178. buf.tx.ac3_enable);
  179. }
  180. }
  181. if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
  182. return;
  183. quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
  184. for (stream = 0; stream < tx_rx_header.number; ++stream) {
  185. if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
  186. stream * tx_rx_header.size,
  187. quadlets) < 0)
  188. break;
  189. snd_iprintf(buffer, "rx %u:\n", stream);
  190. snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
  191. snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
  192. snd_iprintf(buffer, " audio channels: %u\n",
  193. buf.rx.number_audio);
  194. snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
  195. if (quadlets >= 68) {
  196. dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
  197. snd_iprintf(buffer, " names: %s\n", buf.rx.names);
  198. }
  199. if (quadlets >= 70) {
  200. snd_iprintf(buffer, " ac3 caps: %08x\n",
  201. buf.rx.ac3_caps);
  202. snd_iprintf(buffer, " ac3 enable: %08x\n",
  203. buf.rx.ac3_enable);
  204. }
  205. }
  206. quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
  207. if (quadlets >= 4) {
  208. if (dice_proc_read_mem(dice, &buf.ext_sync,
  209. sections[6], 4) < 0)
  210. return;
  211. snd_iprintf(buffer, "ext status:\n");
  212. snd_iprintf(buffer, " clock source: %s\n",
  213. str_from_array(clock_sources,
  214. ARRAY_SIZE(clock_sources),
  215. buf.ext_sync.clock_source));
  216. snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
  217. snd_iprintf(buffer, " rate: %s\n",
  218. str_from_array(rates, ARRAY_SIZE(rates),
  219. buf.ext_sync.rate));
  220. snd_iprintf(buffer, " adat user data: ");
  221. if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
  222. snd_iprintf(buffer, "-\n");
  223. else
  224. snd_iprintf(buffer, "%x\n",
  225. buf.ext_sync.adat_user_data);
  226. }
  227. }
  228. static void dice_proc_read_formation(struct snd_info_entry *entry,
  229. struct snd_info_buffer *buffer)
  230. {
  231. static const char *const rate_labels[] = {
  232. [SND_DICE_RATE_MODE_LOW] = "low",
  233. [SND_DICE_RATE_MODE_MIDDLE] = "middle",
  234. [SND_DICE_RATE_MODE_HIGH] = "high",
  235. };
  236. struct snd_dice *dice = entry->private_data;
  237. int i, j;
  238. snd_iprintf(buffer, "Output stream from unit:\n");
  239. for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
  240. snd_iprintf(buffer, "\t%s", rate_labels[i]);
  241. snd_iprintf(buffer, "\tMIDI\n");
  242. for (i = 0; i < MAX_STREAMS; ++i) {
  243. snd_iprintf(buffer, "Tx %u:", i);
  244. for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
  245. snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);
  246. snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);
  247. }
  248. snd_iprintf(buffer, "Input stream to unit:\n");
  249. for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
  250. snd_iprintf(buffer, "\t%s", rate_labels[i]);
  251. snd_iprintf(buffer, "\n");
  252. for (i = 0; i < MAX_STREAMS; ++i) {
  253. snd_iprintf(buffer, "Rx %u:", i);
  254. for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
  255. snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);
  256. snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);
  257. }
  258. }
  259. static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
  260. const char *name,
  261. void (*op)(struct snd_info_entry *entry,
  262. struct snd_info_buffer *buffer))
  263. {
  264. struct snd_info_entry *entry;
  265. entry = snd_info_create_card_entry(dice->card, name, root);
  266. if (entry)
  267. snd_info_set_text_ops(entry, dice, op);
  268. }
  269. void snd_dice_create_proc(struct snd_dice *dice)
  270. {
  271. struct snd_info_entry *root;
  272. /*
  273. * All nodes are automatically removed at snd_card_disconnect(),
  274. * by following to link list.
  275. */
  276. root = snd_info_create_card_entry(dice->card, "firewire",
  277. dice->card->proc_root);
  278. if (!root)
  279. return;
  280. root->mode = S_IFDIR | 0555;
  281. add_node(dice, root, "dice", dice_proc_read);
  282. add_node(dice, root, "formation", dice_proc_read_formation);
  283. }