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
12static 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
30static 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
39static 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
57static 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;
102			u32 seq_start;
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);
207		snd_iprintf(buffer, "  sequence start: %u\n", buf.rx.seq_start);
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
246void 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}
253