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