1 /*-
2 * Copyright (c) 2012-2022 Hans Petter Selasky
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/types.h>
27 #include <sys/queue.h>
28
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <cuse.h>
34
35 #include "int.h"
36 #include "virtual_oss.h"
37
38 int64_t voss_output_peak[VMAX_CHAN];
39 int64_t voss_input_peak[VMAX_CHAN];
40
41 static int
vctl_open(struct cuse_dev * pdev __unused,int fflags __unused)42 vctl_open(struct cuse_dev *pdev __unused, int fflags __unused)
43 {
44 return (0);
45 }
46
47 static int
vctl_close(struct cuse_dev * pdev __unused,int fflags __unused)48 vctl_close(struct cuse_dev *pdev __unused, int fflags __unused)
49 {
50 return (0);
51 }
52
53 static vprofile_t *
vprofile_by_index(const vprofile_head_t * phead,int index)54 vprofile_by_index(const vprofile_head_t *phead, int index)
55 {
56 vprofile_t *pvp;
57
58 TAILQ_FOREACH(pvp, phead, entry) {
59 if (!index--)
60 return (pvp);
61 }
62 return (NULL);
63 }
64
65 static vmonitor_t *
vmonitor_by_index(int index,vmonitor_head_t * phead)66 vmonitor_by_index(int index, vmonitor_head_t *phead)
67 {
68 vmonitor_t *pvm;
69
70 TAILQ_FOREACH(pvm, phead, entry) {
71 if (!index--)
72 return (pvm);
73 }
74 return (NULL);
75 }
76
77 static int
vctl_ioctl(struct cuse_dev * pdev __unused,int fflags __unused,unsigned long cmd,void * peer_data)78 vctl_ioctl(struct cuse_dev *pdev __unused, int fflags __unused,
79 unsigned long cmd, void *peer_data)
80 {
81 union {
82 int val;
83 struct virtual_oss_io_info io_info;
84 struct virtual_oss_mon_info mon_info;
85 struct virtual_oss_io_peak io_peak;
86 struct virtual_oss_mon_peak mon_peak;
87 struct virtual_oss_compressor out_lim;
88 struct virtual_oss_io_limit io_lim;
89 struct virtual_oss_master_peak master_peak;
90 struct virtual_oss_audio_delay_locator ad_locator;
91 struct virtual_oss_fir_filter fir_filter;
92 struct virtual_oss_system_info sys_info;
93 char options[VIRTUAL_OSS_OPTIONS_MAX];
94 } data;
95
96 vprofile_t *pvp;
97 vmonitor_t *pvm;
98
99 int chan;
100 int len;
101 int error;
102
103 len = IOCPARM_LEN(cmd);
104
105 if (len < 0 || len > (int)sizeof(data))
106 return (CUSE_ERR_INVALID);
107
108 if (cmd & IOC_IN) {
109 error = cuse_copy_in(peer_data, &data, len);
110 if (error)
111 return (error);
112 } else {
113 error = 0;
114 }
115
116 atomic_lock();
117 switch (cmd) {
118 case VIRTUAL_OSS_GET_DEV_INFO:
119 case VIRTUAL_OSS_SET_DEV_INFO:
120 case VIRTUAL_OSS_GET_DEV_PEAK:
121 case VIRTUAL_OSS_SET_DEV_LIMIT:
122 case VIRTUAL_OSS_GET_DEV_LIMIT:
123 case VIRTUAL_OSS_SET_RX_DEV_FIR_FILTER:
124 case VIRTUAL_OSS_GET_RX_DEV_FIR_FILTER:
125 case VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER:
126 case VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER:
127 pvp = vprofile_by_index(&virtual_profile_client_head, data.val);
128 break;
129 case VIRTUAL_OSS_GET_LOOP_INFO:
130 case VIRTUAL_OSS_SET_LOOP_INFO:
131 case VIRTUAL_OSS_GET_LOOP_PEAK:
132 case VIRTUAL_OSS_SET_LOOP_LIMIT:
133 case VIRTUAL_OSS_GET_LOOP_LIMIT:
134 case VIRTUAL_OSS_SET_RX_LOOP_FIR_FILTER:
135 case VIRTUAL_OSS_GET_RX_LOOP_FIR_FILTER:
136 case VIRTUAL_OSS_SET_TX_LOOP_FIR_FILTER:
137 case VIRTUAL_OSS_GET_TX_LOOP_FIR_FILTER:
138 pvp = vprofile_by_index(&virtual_profile_loopback_head, data.val);
139 break;
140 default:
141 pvp = NULL;
142 break;
143 }
144
145 switch (cmd) {
146 case VIRTUAL_OSS_GET_VERSION:
147 data.val = VIRTUAL_OSS_VERSION;
148 break;
149 case VIRTUAL_OSS_GET_DEV_INFO:
150 case VIRTUAL_OSS_GET_LOOP_INFO:
151 if (pvp == NULL ||
152 data.io_info.channel < 0 ||
153 data.io_info.channel >= (int)pvp->channels) {
154 error = CUSE_ERR_INVALID;
155 break;
156 }
157 strlcpy(data.io_info.name, pvp->oss_name, sizeof(data.io_info.name));
158 chan = data.io_info.channel;
159 data.io_info.rx_amp = pvp->rx_shift[chan];
160 data.io_info.tx_amp = pvp->tx_shift[chan];
161 data.io_info.rx_chan = pvp->rx_src[chan];
162 data.io_info.tx_chan = pvp->tx_dst[chan];
163 data.io_info.rx_mute = pvp->rx_mute[chan] ? 1 : 0;
164 data.io_info.tx_mute = pvp->tx_mute[chan] ? 1 : 0;
165 data.io_info.rx_pol = pvp->rx_pol[chan] ? 1 : 0;
166 data.io_info.tx_pol = pvp->tx_pol[chan] ? 1 : 0;
167 data.io_info.bits = pvp->bits;
168 data.io_info.rx_delay = pvp->rec_delay;
169 data.io_info.rx_delay_limit = voss_dsp_sample_rate;
170 break;
171 case VIRTUAL_OSS_SET_DEV_INFO:
172 case VIRTUAL_OSS_SET_LOOP_INFO:
173 if (pvp == NULL ||
174 data.io_info.channel < 0 ||
175 data.io_info.channel >= (int)pvp->channels ||
176 data.io_info.rx_amp < -31 || data.io_info.rx_amp > 31 ||
177 data.io_info.tx_amp < -31 || data.io_info.tx_amp > 31 ||
178 data.io_info.rx_delay < 0 ||
179 data.io_info.rx_delay > (int)voss_dsp_sample_rate) {
180 error = CUSE_ERR_INVALID;
181 break;
182 }
183 chan = data.io_info.channel;
184 pvp->rx_shift[chan] = data.io_info.rx_amp;
185 pvp->tx_shift[chan] = data.io_info.tx_amp;
186 pvp->rx_src[chan] = data.io_info.rx_chan;
187 pvp->tx_dst[chan] = data.io_info.tx_chan;
188 pvp->rx_mute[chan] = data.io_info.rx_mute ? 1 : 0;
189 pvp->tx_mute[chan] = data.io_info.tx_mute ? 1 : 0;
190 pvp->rx_pol[chan] = data.io_info.rx_pol ? 1 : 0;
191 pvp->tx_pol[chan] = data.io_info.tx_pol ? 1 : 0;
192 pvp->rec_delay = data.io_info.rx_delay;
193 break;
194 case VIRTUAL_OSS_GET_INPUT_MON_INFO:
195 pvm = vmonitor_by_index(data.mon_info.number,
196 &virtual_monitor_input);
197 if (pvm == NULL) {
198 error = CUSE_ERR_INVALID;
199 break;
200 }
201 data.mon_info.src_chan = pvm->src_chan;
202 data.mon_info.dst_chan = pvm->dst_chan;
203 data.mon_info.pol = pvm->pol;
204 data.mon_info.mute = pvm->mute;
205 data.mon_info.amp = pvm->shift;
206 data.mon_info.bits = voss_dsp_bits;
207 break;
208 case VIRTUAL_OSS_SET_INPUT_MON_INFO:
209 pvm = vmonitor_by_index(data.mon_info.number,
210 &virtual_monitor_input);
211 if (pvm == NULL ||
212 data.mon_info.amp < -31 ||
213 data.mon_info.amp > 31) {
214 error = CUSE_ERR_INVALID;
215 break;
216 }
217 pvm->src_chan = data.mon_info.src_chan;
218 pvm->dst_chan = data.mon_info.dst_chan;
219 pvm->pol = data.mon_info.pol ? 1 : 0;
220 pvm->mute = data.mon_info.mute ? 1 : 0;
221 pvm->shift = data.mon_info.amp;
222 break;
223 case VIRTUAL_OSS_GET_OUTPUT_MON_INFO:
224 pvm = vmonitor_by_index(data.mon_info.number,
225 &virtual_monitor_output);
226 if (pvm == NULL) {
227 error = CUSE_ERR_INVALID;
228 break;
229 }
230 data.mon_info.src_chan = pvm->src_chan;
231 data.mon_info.dst_chan = pvm->dst_chan;
232 data.mon_info.pol = pvm->pol;
233 data.mon_info.mute = pvm->mute;
234 data.mon_info.amp = pvm->shift;
235 data.mon_info.bits = voss_dsp_bits;
236 break;
237 case VIRTUAL_OSS_SET_OUTPUT_MON_INFO:
238 pvm = vmonitor_by_index(data.mon_info.number,
239 &virtual_monitor_output);
240 if (pvm == NULL ||
241 data.mon_info.amp < -31 ||
242 data.mon_info.amp > 31) {
243 error = CUSE_ERR_INVALID;
244 break;
245 }
246 pvm->src_chan = data.mon_info.src_chan;
247 pvm->dst_chan = data.mon_info.dst_chan;
248 pvm->pol = data.mon_info.pol ? 1 : 0;
249 pvm->mute = data.mon_info.mute ? 1 : 0;
250 pvm->shift = data.mon_info.amp;
251 break;
252 case VIRTUAL_OSS_GET_LOCAL_MON_INFO:
253 pvm = vmonitor_by_index(data.mon_info.number,
254 &virtual_monitor_local);
255 if (pvm == NULL) {
256 error = CUSE_ERR_INVALID;
257 break;
258 }
259 data.mon_info.src_chan = pvm->src_chan;
260 data.mon_info.dst_chan = pvm->dst_chan;
261 data.mon_info.pol = pvm->pol;
262 data.mon_info.mute = pvm->mute;
263 data.mon_info.amp = pvm->shift;
264 data.mon_info.bits = voss_dsp_bits;
265 break;
266 case VIRTUAL_OSS_SET_LOCAL_MON_INFO:
267 pvm = vmonitor_by_index(data.mon_info.number,
268 &virtual_monitor_local);
269 if (pvm == NULL ||
270 data.mon_info.amp < -31 ||
271 data.mon_info.amp > 31) {
272 error = CUSE_ERR_INVALID;
273 break;
274 }
275 pvm->src_chan = data.mon_info.src_chan;
276 pvm->dst_chan = data.mon_info.dst_chan;
277 pvm->pol = data.mon_info.pol ? 1 : 0;
278 pvm->mute = data.mon_info.mute ? 1 : 0;
279 pvm->shift = data.mon_info.amp;
280 break;
281 case VIRTUAL_OSS_GET_DEV_PEAK:
282 case VIRTUAL_OSS_GET_LOOP_PEAK:
283 if (pvp == NULL ||
284 data.io_peak.channel < 0 ||
285 data.io_peak.channel >= (int)pvp->channels) {
286 error = CUSE_ERR_INVALID;
287 break;
288 }
289 strlcpy(data.io_peak.name, pvp->oss_name, sizeof(data.io_peak.name));
290 chan = data.io_peak.channel;
291 data.io_peak.rx_peak_value = pvp->rx_peak_value[chan];
292 pvp->rx_peak_value[chan] = 0;
293 data.io_peak.tx_peak_value = pvp->tx_peak_value[chan];
294 pvp->tx_peak_value[chan] = 0;
295 data.io_peak.bits = pvp->bits;
296 break;
297 case VIRTUAL_OSS_GET_INPUT_MON_PEAK:
298 pvm = vmonitor_by_index(data.mon_peak.number,
299 &virtual_monitor_input);
300 if (pvm == NULL) {
301 error = CUSE_ERR_INVALID;
302 break;
303 }
304 data.mon_peak.peak_value = pvm->peak_value;
305 data.mon_peak.bits = voss_dsp_bits;
306 pvm->peak_value = 0;
307 break;
308 case VIRTUAL_OSS_GET_OUTPUT_MON_PEAK:
309 pvm = vmonitor_by_index(data.mon_peak.number,
310 &virtual_monitor_output);
311 if (pvm == NULL) {
312 error = CUSE_ERR_INVALID;
313 break;
314 }
315 data.mon_peak.peak_value = pvm->peak_value;
316 data.mon_peak.bits = voss_dsp_bits;
317 pvm->peak_value = 0;
318 break;
319 case VIRTUAL_OSS_GET_LOCAL_MON_PEAK:
320 pvm = vmonitor_by_index(data.mon_peak.number,
321 &virtual_monitor_local);
322 if (pvm == NULL) {
323 error = CUSE_ERR_INVALID;
324 break;
325 }
326 data.mon_peak.peak_value = pvm->peak_value;
327 data.mon_peak.bits = voss_dsp_bits;
328 pvm->peak_value = 0;
329 break;
330 case VIRTUAL_OSS_ADD_INPUT_MON:
331 pvm = vmonitor_alloc(&data.val,
332 &virtual_monitor_input);
333 if (pvm == NULL)
334 error = CUSE_ERR_INVALID;
335 break;
336 case VIRTUAL_OSS_ADD_OUTPUT_MON:
337 pvm = vmonitor_alloc(&data.val,
338 &virtual_monitor_output);
339 if (pvm == NULL)
340 error = CUSE_ERR_INVALID;
341 break;
342 case VIRTUAL_OSS_ADD_LOCAL_MON:
343 pvm = vmonitor_alloc(&data.val,
344 &virtual_monitor_local);
345 if (pvm == NULL)
346 error = CUSE_ERR_INVALID;
347 break;
348 case VIRTUAL_OSS_SET_OUTPUT_LIMIT:
349 if (data.out_lim.enabled < 0 ||
350 data.out_lim.enabled > 1 ||
351 data.out_lim.knee < VIRTUAL_OSS_KNEE_MIN ||
352 data.out_lim.knee > VIRTUAL_OSS_KNEE_MAX ||
353 data.out_lim.attack < VIRTUAL_OSS_ATTACK_MIN ||
354 data.out_lim.attack > VIRTUAL_OSS_ATTACK_MAX ||
355 data.out_lim.decay < VIRTUAL_OSS_DECAY_MIN ||
356 data.out_lim.decay > VIRTUAL_OSS_DECAY_MAX ||
357 data.out_lim.gain != 0) {
358 error = CUSE_ERR_INVALID;
359 break;
360 }
361 voss_output_compressor_param.enabled = data.out_lim.enabled;
362 voss_output_compressor_param.knee = data.out_lim.knee;
363 voss_output_compressor_param.attack = data.out_lim.attack;
364 voss_output_compressor_param.decay = data.out_lim.decay;
365 break;
366 case VIRTUAL_OSS_GET_OUTPUT_LIMIT:
367 data.out_lim.enabled = voss_output_compressor_param.enabled;
368 data.out_lim.knee = voss_output_compressor_param.knee;
369 data.out_lim.attack = voss_output_compressor_param.attack;
370 data.out_lim.decay = voss_output_compressor_param.decay;
371 data.out_lim.gain = 1000;
372 for (chan = 0; chan != VMAX_CHAN; chan++) {
373 int gain = voss_output_compressor_gain[chan] * 1000.0;
374 if (data.out_lim.gain > gain)
375 data.out_lim.gain = gain;
376 }
377 break;
378 case VIRTUAL_OSS_SET_DEV_LIMIT:
379 case VIRTUAL_OSS_SET_LOOP_LIMIT:
380 if (pvp == NULL ||
381 data.io_lim.param.enabled < 0 ||
382 data.io_lim.param.enabled > 1 ||
383 data.io_lim.param.knee < VIRTUAL_OSS_KNEE_MIN ||
384 data.io_lim.param.knee > VIRTUAL_OSS_KNEE_MAX ||
385 data.io_lim.param.attack < VIRTUAL_OSS_ATTACK_MIN ||
386 data.io_lim.param.attack > VIRTUAL_OSS_ATTACK_MAX ||
387 data.io_lim.param.decay < VIRTUAL_OSS_DECAY_MIN ||
388 data.io_lim.param.decay > VIRTUAL_OSS_DECAY_MAX ||
389 data.io_lim.param.gain != 0) {
390 error = CUSE_ERR_INVALID;
391 break;
392 }
393 pvp->rx_compressor_param.enabled = data.io_lim.param.enabled;
394 pvp->rx_compressor_param.knee = data.io_lim.param.knee;
395 pvp->rx_compressor_param.attack = data.io_lim.param.attack;
396 pvp->rx_compressor_param.decay = data.io_lim.param.decay;
397 break;
398 case VIRTUAL_OSS_GET_DEV_LIMIT:
399 case VIRTUAL_OSS_GET_LOOP_LIMIT:
400 if (pvp == NULL) {
401 error = CUSE_ERR_INVALID;
402 break;
403 }
404 data.io_lim.param.enabled = pvp->rx_compressor_param.enabled;
405 data.io_lim.param.knee = pvp->rx_compressor_param.knee;
406 data.io_lim.param.attack = pvp->rx_compressor_param.attack;
407 data.io_lim.param.decay = pvp->rx_compressor_param.decay;
408 data.io_lim.param.gain = 1000;
409
410 for (chan = 0; chan != VMAX_CHAN; chan++) {
411 int gain = pvp->rx_compressor_gain[chan] * 1000.0;
412 if (data.io_lim.param.gain > gain)
413 data.io_lim.param.gain = gain;
414 }
415 break;
416 case VIRTUAL_OSS_GET_OUTPUT_PEAK:
417 chan = data.master_peak.channel;
418 if (chan < 0 ||
419 chan >= (int)voss_max_channels) {
420 error = CUSE_ERR_INVALID;
421 break;
422 }
423 data.master_peak.bits = voss_dsp_bits;
424 data.master_peak.peak_value = voss_output_peak[chan];
425 voss_output_peak[chan] = 0;
426 break;
427 case VIRTUAL_OSS_GET_INPUT_PEAK:
428 chan = data.master_peak.channel;
429 if (chan < 0 ||
430 chan >= (int)voss_dsp_max_channels) {
431 error = CUSE_ERR_INVALID;
432 break;
433 }
434 data.master_peak.bits = voss_dsp_bits;
435 data.master_peak.peak_value = voss_input_peak[chan];
436 voss_input_peak[chan] = 0;
437 break;
438
439 case VIRTUAL_OSS_SET_RECORDING:
440 voss_is_recording = data.val ? 1 : 0;
441 break;
442
443 case VIRTUAL_OSS_GET_RECORDING:
444 data.val = voss_is_recording;
445 break;
446
447 case VIRTUAL_OSS_SET_AUDIO_DELAY_LOCATOR:
448 if (data.ad_locator.channel_output < 0 ||
449 data.ad_locator.channel_output >= (int)voss_mix_channels) {
450 error = CUSE_ERR_INVALID;
451 break;
452 }
453 if (data.ad_locator.channel_input < 0 ||
454 data.ad_locator.channel_input >= (int)voss_mix_channels) {
455 error = CUSE_ERR_INVALID;
456 break;
457 }
458 if (data.ad_locator.signal_output_level < 0 ||
459 data.ad_locator.signal_output_level >= 64) {
460 error = CUSE_ERR_INVALID;
461 break;
462 }
463 voss_ad_enabled = (data.ad_locator.locator_enabled != 0);
464 voss_ad_output_signal = data.ad_locator.signal_output_level;
465 voss_ad_output_channel = data.ad_locator.channel_output;
466 voss_ad_input_channel = data.ad_locator.channel_input;
467 break;
468
469 case VIRTUAL_OSS_GET_AUDIO_DELAY_LOCATOR:
470 data.ad_locator.locator_enabled = voss_ad_enabled;
471 data.ad_locator.signal_output_level = voss_ad_output_signal;
472 data.ad_locator.channel_output = voss_ad_output_channel;
473 data.ad_locator.channel_input = voss_ad_input_channel;
474 data.ad_locator.channel_last = voss_mix_channels - 1;
475 data.ad_locator.signal_input_delay = voss_ad_last_delay;
476 data.ad_locator.signal_delay_hz = voss_dsp_sample_rate;
477 break;
478
479 case VIRTUAL_OSS_RST_AUDIO_DELAY_LOCATOR:
480 voss_ad_reset();
481 break;
482
483 case VIRTUAL_OSS_ADD_OPTIONS:
484 data.options[VIRTUAL_OSS_OPTIONS_MAX - 1] = 0;
485 voss_add_options(data.options);
486 break;
487
488 case VIRTUAL_OSS_GET_RX_DEV_FIR_FILTER:
489 case VIRTUAL_OSS_GET_RX_LOOP_FIR_FILTER:
490 if (pvp == NULL ||
491 data.fir_filter.channel < 0 ||
492 data.fir_filter.channel >= (int)pvp->channels) {
493 error = CUSE_ERR_INVALID;
494 } else if (data.fir_filter.filter_data == NULL) {
495 data.fir_filter.filter_size = pvp->rx_filter_size;
496 } else if (data.fir_filter.filter_size != (int)pvp->rx_filter_size) {
497 error = CUSE_ERR_INVALID;
498 } else if (pvp->rx_filter_data[data.fir_filter.channel] == NULL) {
499 error = CUSE_ERR_NO_MEMORY; /* filter disabled */
500 } else {
501 error = cuse_copy_out(pvp->rx_filter_data[data.fir_filter.channel],
502 data.fir_filter.filter_data,
503 sizeof(pvp->rx_filter_data[0][0]) *
504 data.fir_filter.filter_size);
505 }
506 break;
507
508 case VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER:
509 case VIRTUAL_OSS_GET_TX_LOOP_FIR_FILTER:
510 if (pvp == NULL ||
511 data.fir_filter.channel < 0 ||
512 data.fir_filter.channel >= (int)pvp->channels) {
513 error = CUSE_ERR_INVALID;
514 } else if (data.fir_filter.filter_data == NULL) {
515 data.fir_filter.filter_size = pvp->tx_filter_size;
516 } else if (data.fir_filter.filter_size != (int)pvp->tx_filter_size) {
517 error = CUSE_ERR_INVALID;
518 } else if (pvp->tx_filter_data[data.fir_filter.channel] == NULL) {
519 error = CUSE_ERR_NO_MEMORY; /* filter disabled */
520 } else {
521 error = cuse_copy_out(pvp->tx_filter_data[data.fir_filter.channel],
522 data.fir_filter.filter_data,
523 sizeof(pvp->tx_filter_data[0][0]) *
524 data.fir_filter.filter_size);
525 }
526 break;
527
528 case VIRTUAL_OSS_SET_RX_DEV_FIR_FILTER:
529 case VIRTUAL_OSS_SET_RX_LOOP_FIR_FILTER:
530 if (pvp == NULL ||
531 data.fir_filter.channel < 0 ||
532 data.fir_filter.channel >= (int)pvp->channels) {
533 error = CUSE_ERR_INVALID;
534 } else if (data.fir_filter.filter_data == NULL) {
535 free(pvp->rx_filter_data[data.fir_filter.channel]);
536 pvp->rx_filter_data[data.fir_filter.channel] = NULL; /* disable filter */
537 } else if (data.fir_filter.filter_size != (int)pvp->rx_filter_size) {
538 error = CUSE_ERR_INVALID;
539 } else if (pvp->rx_filter_size != 0) {
540 size_t size = sizeof(pvp->rx_filter_data[0][0]) * pvp->rx_filter_size;
541 if (pvp->rx_filter_data[data.fir_filter.channel] == NULL) {
542 pvp->rx_filter_data[data.fir_filter.channel] = malloc(size);
543 if (pvp->rx_filter_data[data.fir_filter.channel] == NULL)
544 error = CUSE_ERR_NO_MEMORY;
545 else
546 memset(pvp->rx_filter_data[data.fir_filter.channel], 0, size);
547 }
548 if (pvp->rx_filter_data[data.fir_filter.channel] != NULL) {
549 error = cuse_copy_in(data.fir_filter.filter_data,
550 pvp->rx_filter_data[data.fir_filter.channel], size);
551 }
552 }
553 break;
554
555 case VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER:
556 case VIRTUAL_OSS_SET_TX_LOOP_FIR_FILTER:
557 if (pvp == NULL ||
558 data.fir_filter.channel < 0 ||
559 data.fir_filter.channel >= (int)pvp->channels) {
560 error = CUSE_ERR_INVALID;
561 } else if (data.fir_filter.filter_data == NULL) {
562 free(pvp->tx_filter_data[data.fir_filter.channel]);
563 pvp->tx_filter_data[data.fir_filter.channel] = NULL; /* disable filter */
564 } else if (data.fir_filter.filter_size != (int)pvp->tx_filter_size) {
565 error = CUSE_ERR_INVALID;
566 } else if (pvp->tx_filter_size != 0) {
567 size_t size = sizeof(pvp->tx_filter_data[0][0]) * pvp->tx_filter_size;
568 if (pvp->tx_filter_data[data.fir_filter.channel] == NULL) {
569 pvp->tx_filter_data[data.fir_filter.channel] = malloc(size);
570 if (pvp->tx_filter_data[data.fir_filter.channel] == NULL)
571 error = CUSE_ERR_NO_MEMORY;
572 else
573 memset(pvp->tx_filter_data[data.fir_filter.channel], 0, size);
574 }
575 if (pvp->tx_filter_data[data.fir_filter.channel] != NULL) {
576 error = cuse_copy_in(data.fir_filter.filter_data,
577 pvp->tx_filter_data[data.fir_filter.channel], size);
578 }
579 }
580 break;
581
582 case VIRTUAL_OSS_GET_SAMPLE_RATE:
583 data.val = voss_dsp_sample_rate;
584 break;
585
586 case VIRTUAL_OSS_GET_SYSTEM_INFO:
587 data.sys_info.tx_jitter_up = voss_jitter_up;
588 data.sys_info.tx_jitter_down = voss_jitter_down;
589 data.sys_info.sample_rate = voss_dsp_sample_rate;
590 data.sys_info.sample_bits = voss_dsp_bits;
591 data.sys_info.sample_channels = voss_mix_channels;
592 strlcpy(data.sys_info.rx_device_name, voss_dsp_rx_device,
593 sizeof(data.sys_info.rx_device_name));
594 strlcpy(data.sys_info.tx_device_name, voss_dsp_tx_device,
595 sizeof(data.sys_info.tx_device_name));
596 break;
597
598 default:
599 error = CUSE_ERR_INVALID;
600 break;
601 }
602 atomic_unlock();
603
604 if (error == 0) {
605 if (cmd & IOC_OUT)
606 error = cuse_copy_out(&data, peer_data, len);
607 }
608 return (error);
609 }
610
611 const struct cuse_methods vctl_methods = {
612 .cm_open = vctl_open,
613 .cm_close = vctl_close,
614 .cm_ioctl = vctl_ioctl,
615 };
616