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