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 42 vctl_open(struct cuse_dev *pdev __unused, int fflags __unused) 43 { 44 return (0); 45 } 46 47 static int 48 vctl_close(struct cuse_dev *pdev __unused, int fflags __unused) 49 { 50 return (0); 51 } 52 53 static vprofile_t * 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 * 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 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