1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * fireworks_proc.c - a part of driver for Fireworks based devices |
4 | * |
5 | * Copyright (c) 2009-2010 Clemens Ladisch |
6 | * Copyright (c) 2013-2014 Takashi Sakamoto |
7 | */ |
8 | |
9 | #include "./fireworks.h" |
10 | |
11 | static inline const char* |
12 | get_phys_name(struct snd_efw_phys_grp *grp, bool input) |
13 | { |
14 | static const char *const ch_type[] = { |
15 | "Analog" , "S/PDIF" , "ADAT" , "S/PDIF or ADAT" , "Mirroring" , |
16 | "Headphones" , "I2S" , "Guitar" , "Pirzo Guitar" , "Guitar String" , |
17 | }; |
18 | |
19 | if (grp->type < ARRAY_SIZE(ch_type)) |
20 | return ch_type[grp->type]; |
21 | else if (input) |
22 | return "Input" ; |
23 | else |
24 | return "Output" ; |
25 | } |
26 | |
27 | static void |
28 | proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer) |
29 | { |
30 | struct snd_efw *efw = entry->private_data; |
31 | unsigned short i; |
32 | struct snd_efw_hwinfo *hwinfo; |
33 | |
34 | hwinfo = kmalloc(size: sizeof(struct snd_efw_hwinfo), GFP_KERNEL); |
35 | if (hwinfo == NULL) |
36 | return; |
37 | |
38 | if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0) |
39 | goto end; |
40 | |
41 | snd_iprintf(buffer, "guid_hi: 0x%X\n" , hwinfo->guid_hi); |
42 | snd_iprintf(buffer, "guid_lo: 0x%X\n" , hwinfo->guid_lo); |
43 | snd_iprintf(buffer, "type: 0x%X\n" , hwinfo->type); |
44 | snd_iprintf(buffer, "version: 0x%X\n" , hwinfo->version); |
45 | snd_iprintf(buffer, "vendor_name: %s\n" , hwinfo->vendor_name); |
46 | snd_iprintf(buffer, "model_name: %s\n" , hwinfo->model_name); |
47 | |
48 | snd_iprintf(buffer, "dsp_version: 0x%X\n" , hwinfo->dsp_version); |
49 | snd_iprintf(buffer, "arm_version: 0x%X\n" , hwinfo->arm_version); |
50 | snd_iprintf(buffer, "fpga_version: 0x%X\n" , hwinfo->fpga_version); |
51 | |
52 | snd_iprintf(buffer, "flags: 0x%X\n" , hwinfo->flags); |
53 | |
54 | snd_iprintf(buffer, "max_sample_rate: 0x%X\n" , hwinfo->max_sample_rate); |
55 | snd_iprintf(buffer, "min_sample_rate: 0x%X\n" , hwinfo->min_sample_rate); |
56 | snd_iprintf(buffer, "supported_clock: 0x%X\n" , |
57 | hwinfo->supported_clocks); |
58 | |
59 | snd_iprintf(buffer, "phys out: 0x%X\n" , hwinfo->phys_out); |
60 | snd_iprintf(buffer, "phys in: 0x%X\n" , hwinfo->phys_in); |
61 | |
62 | snd_iprintf(buffer, "phys in grps: 0x%X\n" , |
63 | hwinfo->phys_in_grp_count); |
64 | for (i = 0; i < hwinfo->phys_in_grp_count; i++) { |
65 | snd_iprintf(buffer, |
66 | "phys in grp[%d]: type 0x%X, count 0x%X\n" , |
67 | i, hwinfo->phys_out_grps[i].type, |
68 | hwinfo->phys_out_grps[i].count); |
69 | } |
70 | |
71 | snd_iprintf(buffer, "phys out grps: 0x%X\n" , |
72 | hwinfo->phys_out_grp_count); |
73 | for (i = 0; i < hwinfo->phys_out_grp_count; i++) { |
74 | snd_iprintf(buffer, |
75 | "phys out grps[%d]: type 0x%X, count 0x%X\n" , |
76 | i, hwinfo->phys_out_grps[i].type, |
77 | hwinfo->phys_out_grps[i].count); |
78 | } |
79 | |
80 | snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n" , |
81 | hwinfo->amdtp_rx_pcm_channels); |
82 | snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n" , |
83 | hwinfo->amdtp_tx_pcm_channels); |
84 | snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n" , |
85 | hwinfo->amdtp_rx_pcm_channels_2x); |
86 | snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n" , |
87 | hwinfo->amdtp_tx_pcm_channels_2x); |
88 | snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n" , |
89 | hwinfo->amdtp_rx_pcm_channels_4x); |
90 | snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n" , |
91 | hwinfo->amdtp_tx_pcm_channels_4x); |
92 | |
93 | snd_iprintf(buffer, "midi out ports: 0x%X\n" , hwinfo->midi_out_ports); |
94 | snd_iprintf(buffer, "midi in ports: 0x%X\n" , hwinfo->midi_in_ports); |
95 | |
96 | snd_iprintf(buffer, "mixer playback channels: 0x%X\n" , |
97 | hwinfo->mixer_playback_channels); |
98 | snd_iprintf(buffer, "mixer capture channels: 0x%X\n" , |
99 | hwinfo->mixer_capture_channels); |
100 | end: |
101 | kfree(objp: hwinfo); |
102 | } |
103 | |
104 | static void |
105 | proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer) |
106 | { |
107 | struct snd_efw *efw = entry->private_data; |
108 | enum snd_efw_clock_source clock_source; |
109 | unsigned int sampling_rate; |
110 | |
111 | if (snd_efw_command_get_clock_source(efw, source: &clock_source) < 0) |
112 | return; |
113 | |
114 | if (snd_efw_command_get_sampling_rate(efw, rate: &sampling_rate) < 0) |
115 | return; |
116 | |
117 | snd_iprintf(buffer, "Clock Source: %d\n" , clock_source); |
118 | snd_iprintf(buffer, "Sampling Rate: %d\n" , sampling_rate); |
119 | } |
120 | |
121 | /* |
122 | * NOTE: |
123 | * dB = 20 * log10(linear / 0x01000000) |
124 | * -144.0 dB when linear is 0 |
125 | */ |
126 | static void |
127 | proc_read_phys_meters(struct snd_info_entry *entry, |
128 | struct snd_info_buffer *buffer) |
129 | { |
130 | struct snd_efw *efw = entry->private_data; |
131 | struct snd_efw_phys_meters *meters; |
132 | unsigned int g, c, m, max, size; |
133 | const char *name; |
134 | u32 *linear; |
135 | int err; |
136 | |
137 | size = sizeof(struct snd_efw_phys_meters) + |
138 | (efw->phys_in + efw->phys_out) * sizeof(u32); |
139 | meters = kzalloc(size, GFP_KERNEL); |
140 | if (meters == NULL) |
141 | return; |
142 | |
143 | err = snd_efw_command_get_phys_meters(efw, meters, len: size); |
144 | if (err < 0) |
145 | goto end; |
146 | |
147 | snd_iprintf(buffer, "Physical Meters:\n" ); |
148 | |
149 | m = 0; |
150 | max = min(efw->phys_out, meters->out_meters); |
151 | linear = meters->values; |
152 | snd_iprintf(buffer, " %d Outputs:\n" , max); |
153 | for (g = 0; g < efw->phys_out_grp_count; g++) { |
154 | name = get_phys_name(grp: &efw->phys_out_grps[g], input: false); |
155 | for (c = 0; c < efw->phys_out_grps[g].count; c++) { |
156 | if (m < max) |
157 | snd_iprintf(buffer, "\t%s [%d]: %d\n" , |
158 | name, c, linear[m++]); |
159 | } |
160 | } |
161 | |
162 | m = 0; |
163 | max = min(efw->phys_in, meters->in_meters); |
164 | linear = meters->values + meters->out_meters; |
165 | snd_iprintf(buffer, " %d Inputs:\n" , max); |
166 | for (g = 0; g < efw->phys_in_grp_count; g++) { |
167 | name = get_phys_name(grp: &efw->phys_in_grps[g], input: true); |
168 | for (c = 0; c < efw->phys_in_grps[g].count; c++) |
169 | if (m < max) |
170 | snd_iprintf(buffer, "\t%s [%d]: %d\n" , |
171 | name, c, linear[m++]); |
172 | } |
173 | end: |
174 | kfree(objp: meters); |
175 | } |
176 | |
177 | static void |
178 | proc_read_queues_state(struct snd_info_entry *entry, |
179 | struct snd_info_buffer *buffer) |
180 | { |
181 | struct snd_efw *efw = entry->private_data; |
182 | unsigned int consumed; |
183 | |
184 | if (efw->pull_ptr > efw->push_ptr) |
185 | consumed = snd_efw_resp_buf_size - |
186 | (unsigned int)(efw->pull_ptr - efw->push_ptr); |
187 | else |
188 | consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr); |
189 | |
190 | snd_iprintf(buffer, "%d/%d\n" , |
191 | consumed, snd_efw_resp_buf_size); |
192 | } |
193 | |
194 | static void |
195 | add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, |
196 | void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b)) |
197 | { |
198 | struct snd_info_entry *entry; |
199 | |
200 | entry = snd_info_create_card_entry(card: efw->card, name, parent: root); |
201 | if (entry) |
202 | snd_info_set_text_ops(entry, private_data: efw, read: op); |
203 | } |
204 | |
205 | void snd_efw_proc_init(struct snd_efw *efw) |
206 | { |
207 | struct snd_info_entry *root; |
208 | |
209 | /* |
210 | * All nodes are automatically removed at snd_card_disconnect(), |
211 | * by following to link list. |
212 | */ |
213 | root = snd_info_create_card_entry(card: efw->card, name: "firewire" , |
214 | parent: efw->card->proc_root); |
215 | if (root == NULL) |
216 | return; |
217 | root->mode = S_IFDIR | 0555; |
218 | |
219 | add_node(efw, root, name: "clock" , op: proc_read_clock); |
220 | add_node(efw, root, name: "firmware" , op: proc_read_hwinfo); |
221 | add_node(efw, root, name: "meters" , op: proc_read_phys_meters); |
222 | add_node(efw, root, name: "queues" , op: proc_read_queues_state); |
223 | } |
224 | |