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