1 /* $NetBSD: ossaudio.c,v 1.64 2008/11/13 10:05:52 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.64 2008/11/13 10:05:52 ad Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/proc.h> 34 #include <sys/systm.h> 35 #include <sys/file.h> 36 #include <sys/vnode.h> 37 #include <sys/filedesc.h> 38 #include <sys/ioctl.h> 39 #include <sys/mount.h> 40 #include <sys/kernel.h> 41 #include <sys/audioio.h> 42 #include <sys/midiio.h> 43 #include <sys/kauth.h> 44 #include <sys/syscallargs.h> 45 #include <sys/module.h> 46 47 #include <compat/ossaudio/ossaudio.h> 48 #include <compat/ossaudio/ossaudiovar.h> 49 50 MODULE(MODULE_CLASS_MISC, compat_ossaudio, NULL); 51 52 #ifdef AUDIO_DEBUG 53 #define DPRINTF(x) if (ossdebug) printf x 54 int ossdebug = 0; 55 #else 56 #define DPRINTF(x) 57 #endif 58 59 #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) 60 #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) 61 62 static struct audiodevinfo *getdevinfo(file_t *); 63 static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); 64 static int enum_to_ord(struct audiodevinfo *di, int enm); 65 static int enum_to_mask(struct audiodevinfo *di, int enm); 66 67 static void setblocksize(file_t *, struct audio_info *); 68 69 static int 70 compat_ossaudio_modcmd(modcmd_t cmd, void *arg) 71 { 72 73 switch (cmd) { 74 case MODULE_CMD_INIT: 75 case MODULE_CMD_FINI: 76 return 0; 77 default: 78 return ENOTTY; 79 } 80 } 81 82 int 83 oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t *retval) 84 { 85 /* { 86 syscallarg(int) fd; 87 syscallarg(u_long) com; 88 syscallarg(void *) data; 89 } */ 90 file_t *fp; 91 u_long com; 92 struct audio_info tmpinfo; 93 struct audio_offset tmpoffs; 94 struct oss_audio_buf_info bufinfo; 95 struct oss_count_info cntinfo; 96 struct audio_encoding tmpenc; 97 u_int u; 98 int idat, idata; 99 int error = 0; 100 int (*ioctlf)(file_t *, u_long, void *); 101 102 if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) 103 return (EBADF); 104 105 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 106 error = EBADF; 107 goto out; 108 } 109 110 com = SCARG(uap, com); 111 DPRINTF(("oss_ioctl_audio: com=%08lx\n", com)); 112 113 retval[0] = 0; 114 115 ioctlf = fp->f_ops->fo_ioctl; 116 switch (com) { 117 case OSS_SNDCTL_DSP_RESET: 118 error = ioctlf(fp, AUDIO_FLUSH, NULL); 119 if (error) 120 goto out; 121 break; 122 case OSS_SNDCTL_DSP_SYNC: 123 error = ioctlf(fp, AUDIO_DRAIN, NULL); 124 if (error) 125 goto out; 126 break; 127 case OSS_SNDCTL_DSP_POST: 128 /* This call is merely advisory, and may be a nop. */ 129 break; 130 case OSS_SNDCTL_DSP_SPEED: 131 AUDIO_INITINFO(&tmpinfo); 132 error = copyin(SCARG(uap, data), &idat, sizeof idat); 133 if (error) 134 goto out; 135 tmpinfo.play.sample_rate = 136 tmpinfo.record.sample_rate = idat; 137 error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 138 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n", 139 idat, error)); 140 if (error) 141 goto out; 142 /* fall into ... */ 143 case OSS_SOUND_PCM_READ_RATE: 144 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 145 if (error) 146 goto out; 147 idat = tmpinfo.play.sample_rate; 148 error = copyout(&idat, SCARG(uap, data), sizeof idat); 149 if (error) 150 goto out; 151 break; 152 case OSS_SNDCTL_DSP_STEREO: 153 AUDIO_INITINFO(&tmpinfo); 154 error = copyin(SCARG(uap, data), &idat, sizeof idat); 155 if (error) 156 goto out; 157 tmpinfo.play.channels = 158 tmpinfo.record.channels = idat ? 2 : 1; 159 (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 160 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 161 if (error) 162 goto out; 163 idat = tmpinfo.play.channels - 1; 164 error = copyout(&idat, SCARG(uap, data), sizeof idat); 165 if (error) 166 goto out; 167 break; 168 case OSS_SNDCTL_DSP_GETBLKSIZE: 169 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 170 if (error) 171 goto out; 172 setblocksize(fp, &tmpinfo); 173 idat = tmpinfo.blocksize; 174 error = copyout(&idat, SCARG(uap, data), sizeof idat); 175 if (error) 176 goto out; 177 break; 178 case OSS_SNDCTL_DSP_SETFMT: 179 AUDIO_INITINFO(&tmpinfo); 180 error = copyin(SCARG(uap, data), &idat, sizeof idat); 181 if (error) 182 goto out; 183 switch (idat) { 184 case OSS_AFMT_MU_LAW: 185 tmpinfo.play.precision = 186 tmpinfo.record.precision = 8; 187 tmpinfo.play.encoding = 188 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; 189 break; 190 case OSS_AFMT_A_LAW: 191 tmpinfo.play.precision = 192 tmpinfo.record.precision = 8; 193 tmpinfo.play.encoding = 194 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; 195 break; 196 case OSS_AFMT_U8: 197 tmpinfo.play.precision = 198 tmpinfo.record.precision = 8; 199 tmpinfo.play.encoding = 200 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; 201 break; 202 case OSS_AFMT_S8: 203 tmpinfo.play.precision = 204 tmpinfo.record.precision = 8; 205 tmpinfo.play.encoding = 206 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; 207 break; 208 case OSS_AFMT_S16_LE: 209 tmpinfo.play.precision = 210 tmpinfo.record.precision = 16; 211 tmpinfo.play.encoding = 212 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; 213 break; 214 case OSS_AFMT_S16_BE: 215 tmpinfo.play.precision = 216 tmpinfo.record.precision = 16; 217 tmpinfo.play.encoding = 218 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; 219 break; 220 case OSS_AFMT_U16_LE: 221 tmpinfo.play.precision = 222 tmpinfo.record.precision = 16; 223 tmpinfo.play.encoding = 224 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; 225 break; 226 case OSS_AFMT_U16_BE: 227 tmpinfo.play.precision = 228 tmpinfo.record.precision = 16; 229 tmpinfo.play.encoding = 230 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; 231 break; 232 default: 233 error = EINVAL; 234 goto out; 235 } 236 (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 237 /* fall into ... */ 238 case OSS_SOUND_PCM_READ_BITS: 239 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 240 if (error) 241 goto out; 242 switch (tmpinfo.play.encoding) { 243 case AUDIO_ENCODING_ULAW: 244 idat = OSS_AFMT_MU_LAW; 245 break; 246 case AUDIO_ENCODING_ALAW: 247 idat = OSS_AFMT_A_LAW; 248 break; 249 case AUDIO_ENCODING_SLINEAR_LE: 250 if (tmpinfo.play.precision == 16) 251 idat = OSS_AFMT_S16_LE; 252 else 253 idat = OSS_AFMT_S8; 254 break; 255 case AUDIO_ENCODING_SLINEAR_BE: 256 if (tmpinfo.play.precision == 16) 257 idat = OSS_AFMT_S16_BE; 258 else 259 idat = OSS_AFMT_S8; 260 break; 261 case AUDIO_ENCODING_ULINEAR_LE: 262 if (tmpinfo.play.precision == 16) 263 idat = OSS_AFMT_U16_LE; 264 else 265 idat = OSS_AFMT_U8; 266 break; 267 case AUDIO_ENCODING_ULINEAR_BE: 268 if (tmpinfo.play.precision == 16) 269 idat = OSS_AFMT_U16_BE; 270 else 271 idat = OSS_AFMT_U8; 272 break; 273 case AUDIO_ENCODING_ADPCM: 274 idat = OSS_AFMT_IMA_ADPCM; 275 break; 276 } 277 error = copyout(&idat, SCARG(uap, data), sizeof idat); 278 if (error) 279 goto out; 280 break; 281 case OSS_SNDCTL_DSP_CHANNELS: 282 AUDIO_INITINFO(&tmpinfo); 283 error = copyin(SCARG(uap, data), &idat, sizeof idat); 284 if (error) 285 goto out; 286 tmpinfo.play.channels = 287 tmpinfo.record.channels = idat; 288 (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 289 /* fall into ... */ 290 case OSS_SOUND_PCM_READ_CHANNELS: 291 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 292 if (error) 293 goto out; 294 idat = tmpinfo.play.channels; 295 error = copyout(&idat, SCARG(uap, data), sizeof idat); 296 if (error) 297 goto out; 298 break; 299 case OSS_SOUND_PCM_WRITE_FILTER: 300 case OSS_SOUND_PCM_READ_FILTER: 301 error = EINVAL; /* XXX unimplemented */ 302 goto out; 303 case OSS_SNDCTL_DSP_SUBDIVIDE: 304 error = copyin(SCARG(uap, data), &idat, sizeof idat); 305 if (error) 306 goto out; 307 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 308 setblocksize(fp, &tmpinfo); 309 if (error) 310 goto out; 311 if (idat == 0) 312 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 313 idat = (tmpinfo.play.buffer_size / idat) & -4; 314 AUDIO_INITINFO(&tmpinfo); 315 tmpinfo.blocksize = idat; 316 error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 317 if (error) 318 goto out; 319 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 320 error = copyout(&idat, SCARG(uap, data), sizeof idat); 321 if (error) 322 goto out; 323 break; 324 case OSS_SNDCTL_DSP_SETFRAGMENT: 325 AUDIO_INITINFO(&tmpinfo); 326 error = copyin(SCARG(uap, data), &idat, sizeof idat); 327 if (error) 328 goto out; 329 if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) { 330 error = EINVAL; 331 goto out; 332 } 333 tmpinfo.blocksize = 1 << (idat & 0xffff); 334 tmpinfo.hiwat = (idat >> 16) & 0x7fff; 335 DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n", 336 tmpinfo.blocksize, tmpinfo.hiwat)); 337 if (tmpinfo.hiwat == 0) /* 0 means set to max */ 338 tmpinfo.hiwat = 65536; 339 (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 340 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 341 if (error) 342 goto out; 343 u = tmpinfo.blocksize; 344 for(idat = 0; u > 1; idat++, u >>= 1) 345 ; 346 idat |= (tmpinfo.hiwat & 0x7fff) << 16; 347 error = copyout(&idat, SCARG(uap, data), sizeof idat); 348 if (error) 349 goto out; 350 break; 351 case OSS_SNDCTL_DSP_GETFMTS: 352 for(idat = 0, tmpenc.index = 0; 353 ioctlf(fp, AUDIO_GETENC, &tmpenc) == 0; 354 tmpenc.index++) { 355 switch(tmpenc.encoding) { 356 case AUDIO_ENCODING_ULAW: 357 idat |= OSS_AFMT_MU_LAW; 358 break; 359 case AUDIO_ENCODING_ALAW: 360 idat |= OSS_AFMT_A_LAW; 361 break; 362 case AUDIO_ENCODING_SLINEAR: 363 idat |= OSS_AFMT_S8; 364 break; 365 case AUDIO_ENCODING_SLINEAR_LE: 366 if (tmpenc.precision == 16) 367 idat |= OSS_AFMT_S16_LE; 368 else 369 idat |= OSS_AFMT_S8; 370 break; 371 case AUDIO_ENCODING_SLINEAR_BE: 372 if (tmpenc.precision == 16) 373 idat |= OSS_AFMT_S16_BE; 374 else 375 idat |= OSS_AFMT_S8; 376 break; 377 case AUDIO_ENCODING_ULINEAR: 378 idat |= OSS_AFMT_U8; 379 break; 380 case AUDIO_ENCODING_ULINEAR_LE: 381 if (tmpenc.precision == 16) 382 idat |= OSS_AFMT_U16_LE; 383 else 384 idat |= OSS_AFMT_U8; 385 break; 386 case AUDIO_ENCODING_ULINEAR_BE: 387 if (tmpenc.precision == 16) 388 idat |= OSS_AFMT_U16_BE; 389 else 390 idat |= OSS_AFMT_U8; 391 break; 392 case AUDIO_ENCODING_ADPCM: 393 idat |= OSS_AFMT_IMA_ADPCM; 394 break; 395 default: 396 break; 397 } 398 } 399 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat)); 400 error = copyout(&idat, SCARG(uap, data), sizeof idat); 401 if (error) 402 goto out; 403 break; 404 case OSS_SNDCTL_DSP_GETOSPACE: 405 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 406 if (error) 407 goto out; 408 setblocksize(fp, &tmpinfo); 409 bufinfo.fragsize = tmpinfo.blocksize; 410 bufinfo.fragments = tmpinfo.hiwat - 411 (tmpinfo.play.seek + tmpinfo.blocksize - 1) / 412 tmpinfo.blocksize; 413 bufinfo.fragstotal = tmpinfo.hiwat; 414 bufinfo.bytes = 415 tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek; 416 error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); 417 if (error) 418 goto out; 419 break; 420 case OSS_SNDCTL_DSP_GETISPACE: 421 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 422 if (error) 423 goto out; 424 setblocksize(fp, &tmpinfo); 425 bufinfo.fragsize = tmpinfo.blocksize; 426 bufinfo.fragments = tmpinfo.hiwat - 427 (tmpinfo.record.seek + tmpinfo.blocksize - 1) / 428 tmpinfo.blocksize; 429 bufinfo.fragstotal = tmpinfo.hiwat; 430 bufinfo.bytes = 431 tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek; 432 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n", 433 bufinfo.fragsize, bufinfo.fragments, 434 bufinfo.fragstotal, bufinfo.bytes)); 435 error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); 436 if (error) 437 goto out; 438 break; 439 case OSS_SNDCTL_DSP_NONBLOCK: 440 idat = 1; 441 error = ioctlf(fp, FIONBIO, &idat); 442 if (error) 443 goto out; 444 break; 445 case OSS_SNDCTL_DSP_GETCAPS: 446 error = ioctlf(fp, AUDIO_GETPROPS, &idata); 447 if (error) 448 goto out; 449 idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */ 450 if (idata & AUDIO_PROP_FULLDUPLEX) 451 idat |= OSS_DSP_CAP_DUPLEX; 452 if (idata & AUDIO_PROP_MMAP) 453 idat |= OSS_DSP_CAP_MMAP; 454 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat)); 455 error = copyout(&idat, SCARG(uap, data), sizeof idat); 456 if (error) 457 goto out; 458 break; 459 #if 0 460 case OSS_SNDCTL_DSP_GETTRIGGER: 461 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 462 if (error) 463 goto out; 464 idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) | 465 (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT); 466 error = copyout(&idat, SCARG(uap, data), sizeof idat); 467 if (error) 468 goto out; 469 break; 470 case OSS_SNDCTL_DSP_SETTRIGGER: 471 (void) ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo, p); 472 error = copyin(SCARG(uap, data), &idat, sizeof idat); 473 if (error) 474 goto out; 475 tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0; 476 tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0; 477 (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); 478 error = copyout(&idat, SCARG(uap, data), sizeof idat); 479 if (error) 480 goto out; 481 break; 482 #else 483 case OSS_SNDCTL_DSP_GETTRIGGER: 484 case OSS_SNDCTL_DSP_SETTRIGGER: 485 /* XXX Do nothing for now. */ 486 idat = OSS_PCM_ENABLE_OUTPUT; 487 error = copyout(&idat, SCARG(uap, data), sizeof idat); 488 goto out; 489 #endif 490 case OSS_SNDCTL_DSP_GETIPTR: 491 error = ioctlf(fp, AUDIO_GETIOFFS, &tmpoffs); 492 if (error) 493 goto out; 494 cntinfo.bytes = tmpoffs.samples; 495 cntinfo.blocks = tmpoffs.deltablks; 496 cntinfo.ptr = tmpoffs.offset; 497 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 498 if (error) 499 goto out; 500 break; 501 case OSS_SNDCTL_DSP_GETOPTR: 502 error = ioctlf(fp, AUDIO_GETOOFFS, &tmpoffs); 503 if (error) 504 goto out; 505 cntinfo.bytes = tmpoffs.samples; 506 cntinfo.blocks = tmpoffs.deltablks; 507 cntinfo.ptr = tmpoffs.offset; 508 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 509 if (error) 510 goto out; 511 break; 512 case OSS_SNDCTL_DSP_SETDUPLEX: 513 idat = 1; 514 error = ioctlf(fp, AUDIO_SETFD, &idat); 515 goto out; 516 case OSS_SNDCTL_DSP_MAPINBUF: 517 case OSS_SNDCTL_DSP_MAPOUTBUF: 518 case OSS_SNDCTL_DSP_SETSYNCRO: 519 error = EINVAL; 520 goto out; 521 case OSS_SNDCTL_DSP_GETODELAY: 522 error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); 523 if (error) 524 goto out; 525 idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; 526 error = copyout(&idat, SCARG(uap, data), sizeof idat); 527 if (error) 528 goto out; 529 break; 530 case OSS_SNDCTL_DSP_PROFILE: 531 /* This gives just a hint to the driver, 532 * implementing it as a NOP is ok 533 */ 534 break; 535 default: 536 error = EINVAL; 537 goto out; 538 } 539 540 out: 541 fd_putfile(SCARG(uap, fd)); 542 return error; 543 } 544 545 /* If the NetBSD mixer device should have more than 32 devices 546 * some will not be available to Linux */ 547 #define NETBSD_MAXDEVS 64 548 struct audiodevinfo { 549 int done; 550 dev_t dev; 551 int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], 552 rdevmap[NETBSD_MAXDEVS]; 553 char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; 554 int enum2opaque[NETBSD_MAXDEVS]; 555 u_long devmask, recmask, stereomask; 556 u_long caps, source; 557 }; 558 559 static int 560 opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) 561 { 562 int i, o; 563 564 for (i = 0; i < NETBSD_MAXDEVS; i++) { 565 o = di->enum2opaque[i]; 566 if (o == opq) 567 break; 568 if (o == -1 && label != NULL && 569 !strncmp(di->names[i], label->name, sizeof di->names[i])) { 570 di->enum2opaque[i] = opq; 571 break; 572 } 573 } 574 if (i >= NETBSD_MAXDEVS) 575 i = -1; 576 /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ 577 return (i); 578 } 579 580 static int 581 enum_to_ord(struct audiodevinfo *di, int enm) 582 { 583 if (enm >= NETBSD_MAXDEVS) 584 return (-1); 585 586 /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ 587 return (di->enum2opaque[enm]); 588 } 589 590 static int 591 enum_to_mask(struct audiodevinfo *di, int enm) 592 { 593 int m; 594 if (enm >= NETBSD_MAXDEVS) 595 return (0); 596 597 m = di->enum2opaque[enm]; 598 if (m == -1) 599 m = 0; 600 /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ 601 return (m); 602 } 603 604 /* 605 * Collect the audio device information to allow faster 606 * emulation of the Linux mixer ioctls. Cache the information 607 * to eliminate the overhead of repeating all the ioctls needed 608 * to collect the information. 609 */ 610 static struct audiodevinfo * 611 getdevinfo(file_t *fp) 612 { 613 mixer_devinfo_t mi; 614 int i, j, e; 615 static const struct { 616 const char *name; 617 int code; 618 } *dp, devs[] = { 619 { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, 620 { AudioNline, OSS_SOUND_MIXER_LINE }, 621 { AudioNcd, OSS_SOUND_MIXER_CD }, 622 { AudioNdac, OSS_SOUND_MIXER_PCM }, 623 { AudioNaux, OSS_SOUND_MIXER_LINE1 }, 624 { AudioNrecord, OSS_SOUND_MIXER_IMIX }, 625 { AudioNmaster, OSS_SOUND_MIXER_VOLUME }, 626 { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, 627 { AudioNbass, OSS_SOUND_MIXER_BASS }, 628 { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, 629 /* { AudioNheadphone, ?? },*/ 630 { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, 631 { AudioNinput, OSS_SOUND_MIXER_IGAIN }, 632 /* { AudioNmaster, OSS_SOUND_MIXER_SPEAKER },*/ 633 /* { AudioNstereo, ?? },*/ 634 /* { AudioNmono, ?? },*/ 635 { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, 636 /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ 637 { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, 638 /* { AudioNmixerout, ?? },*/ 639 { 0, -1 } 640 }; 641 int (*ioctlf)(file_t *, u_long, void *) = fp->f_ops->fo_ioctl; 642 struct vnode *vp; 643 struct vattr va; 644 static struct audiodevinfo devcache; 645 struct audiodevinfo *di = &devcache; 646 int mlen, dlen; 647 648 /* 649 * Figure out what device it is so we can check if the 650 * cached data is valid. 651 */ 652 vp = fp->f_data; 653 if (vp->v_type != VCHR) 654 return 0; 655 if (VOP_GETATTR(vp, &va, kauth_cred_get())) 656 return 0; 657 if (di->done && di->dev == va.va_rdev) 658 return di; 659 660 di->done = 1; 661 di->dev = va.va_rdev; 662 di->devmask = 0; 663 di->recmask = 0; 664 di->stereomask = 0; 665 di->source = ~0; 666 di->caps = 0; 667 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 668 di->devmap[i] = -1; 669 for(i = 0; i < NETBSD_MAXDEVS; i++) { 670 di->rdevmap[i] = -1; 671 di->names[i][0] = '\0'; 672 di->enum2opaque[i] = -1; 673 } 674 for(i = 0; i < NETBSD_MAXDEVS; i++) { 675 mi.index = i; 676 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) 677 break; 678 switch(mi.type) { 679 case AUDIO_MIXER_VALUE: 680 for(dp = devs; dp->name; dp++) { 681 if (strcmp(dp->name, mi.label.name) == 0) 682 break; 683 dlen = strlen(dp->name); 684 mlen = strlen(mi.label.name); 685 if (dlen < mlen 686 && mi.label.name[mlen-dlen-1] == '.' 687 && strcmp(dp->name, mi.label.name + mlen - dlen) == 0) 688 break; 689 } 690 if (dp->code >= 0) { 691 di->devmap[dp->code] = i; 692 di->rdevmap[i] = dp->code; 693 di->devmask |= 1 << dp->code; 694 if (mi.un.v.num_channels == 2) 695 di->stereomask |= 1 << dp->code; 696 strncpy(di->names[i], mi.label.name, 697 sizeof di->names[i]); 698 } 699 break; 700 } 701 } 702 for(i = 0; i < NETBSD_MAXDEVS; i++) { 703 mi.index = i; 704 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) 705 break; 706 if (strcmp(mi.label.name, AudioNsource) != 0) 707 continue; 708 di->source = i; 709 switch(mi.type) { 710 case AUDIO_MIXER_ENUM: 711 for(j = 0; j < mi.un.e.num_mem; j++) { 712 e = opaque_to_enum(di, 713 &mi.un.e.member[j].label, 714 mi.un.e.member[j].ord); 715 if (e >= 0) 716 di->recmask |= 1 << di->rdevmap[e]; 717 } 718 di->caps = OSS_SOUND_CAP_EXCL_INPUT; 719 break; 720 case AUDIO_MIXER_SET: 721 for(j = 0; j < mi.un.s.num_mem; j++) { 722 e = opaque_to_enum(di, 723 &mi.un.s.member[j].label, 724 mi.un.s.member[j].mask); 725 if (e >= 0) 726 di->recmask |= 1 << di->rdevmap[e]; 727 } 728 break; 729 } 730 } 731 return di; 732 } 733 734 int 735 oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_t *retval) 736 { 737 /* { 738 syscallarg(int) fd; 739 syscallarg(u_long) com; 740 syscallarg(void *) data; 741 } */ 742 file_t *fp; 743 u_long com; 744 struct audiodevinfo *di; 745 mixer_ctrl_t mc; 746 struct oss_mixer_info omi; 747 struct audio_device adev; 748 int idat; 749 int i; 750 int error; 751 int l, r, n, e; 752 int (*ioctlf)(file_t *, u_long, void *); 753 754 if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) 755 return (EBADF); 756 757 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 758 error = EBADF; 759 goto out; 760 } 761 762 com = SCARG(uap, com); 763 DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com)); 764 765 retval[0] = 0; 766 767 di = getdevinfo(fp); 768 if (di == 0) { 769 error = EINVAL; 770 goto out; 771 } 772 773 ioctlf = fp->f_ops->fo_ioctl; 774 switch (com) { 775 case OSS_GET_VERSION: 776 idat = OSS_SOUND_VERSION; 777 break; 778 case OSS_SOUND_MIXER_INFO: 779 case OSS_SOUND_OLD_MIXER_INFO: 780 error = ioctlf(fp, AUDIO_GETDEV, &adev); 781 if (error) 782 goto out; 783 omi.modify_counter = 1; 784 strncpy(omi.id, adev.name, sizeof omi.id); 785 strncpy(omi.name, adev.name, sizeof omi.name); 786 error = copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com)); 787 goto out; 788 case OSS_SOUND_MIXER_READ_RECSRC: 789 if (di->source == -1) { 790 error = EINVAL; 791 goto out; 792 } 793 mc.dev = di->source; 794 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 795 mc.type = AUDIO_MIXER_ENUM; 796 error = ioctlf(fp, AUDIO_MIXER_READ, &mc); 797 if (error) 798 goto out; 799 e = opaque_to_enum(di, NULL, mc.un.ord); 800 if (e >= 0) 801 idat = 1 << di->rdevmap[e]; 802 } else { 803 mc.type = AUDIO_MIXER_SET; 804 error = ioctlf(fp, AUDIO_MIXER_READ, &mc); 805 if (error) 806 goto out; 807 e = opaque_to_enum(di, NULL, mc.un.mask); 808 if (e >= 0) 809 idat = 1 << di->rdevmap[e]; 810 } 811 break; 812 case OSS_SOUND_MIXER_READ_DEVMASK: 813 idat = di->devmask; 814 break; 815 case OSS_SOUND_MIXER_READ_RECMASK: 816 idat = di->recmask; 817 break; 818 case OSS_SOUND_MIXER_READ_STEREODEVS: 819 idat = di->stereomask; 820 break; 821 case OSS_SOUND_MIXER_READ_CAPS: 822 idat = di->caps; 823 break; 824 case OSS_SOUND_MIXER_WRITE_RECSRC: 825 case OSS_SOUND_MIXER_WRITE_R_RECSRC: 826 if (di->source == -1) { 827 error = EINVAL; 828 goto out; 829 } 830 mc.dev = di->source; 831 error = copyin(SCARG(uap, data), &idat, sizeof idat); 832 if (error) 833 goto out; 834 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 835 mc.type = AUDIO_MIXER_ENUM; 836 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 837 if (idat & (1 << i)) 838 break; 839 if (i >= OSS_SOUND_MIXER_NRDEVICES || 840 di->devmap[i] == -1) { 841 error = EINVAL; 842 goto out; 843 } 844 mc.un.ord = enum_to_ord(di, di->devmap[i]); 845 } else { 846 mc.type = AUDIO_MIXER_SET; 847 mc.un.mask = 0; 848 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { 849 if (idat & (1 << i)) { 850 if (di->devmap[i] == -1) { 851 error = EINVAL; 852 goto out; 853 } 854 mc.un.mask |= enum_to_mask(di, di->devmap[i]); 855 } 856 } 857 } 858 error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); 859 goto out; 860 default: 861 if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && 862 com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { 863 n = OSS_GET_DEV(com); 864 if (di->devmap[n] == -1) { 865 error = EINVAL; 866 goto out; 867 } 868 doread: 869 mc.dev = di->devmap[n]; 870 mc.type = AUDIO_MIXER_VALUE; 871 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 872 error = ioctlf(fp, AUDIO_MIXER_READ, &mc); 873 if (error) 874 goto out; 875 if (mc.un.value.num_channels != 2) { 876 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 877 } else { 878 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 879 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 880 } 881 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 882 DPRINTF(("OSS_MIXER_READ n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 883 n, di->devmap[n], l, r, idat)); 884 break; 885 } else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com && 886 com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) || 887 (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 888 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) { 889 n = OSS_GET_DEV(com); 890 if (di->devmap[n] == -1) { 891 error = EINVAL; 892 goto out; 893 } 894 error = copyin(SCARG(uap, data), &idat, sizeof idat); 895 if (error) 896 goto out; 897 l = FROM_OSSVOL( idat & 0xff); 898 r = FROM_OSSVOL((idat >> 8) & 0xff); 899 mc.dev = di->devmap[n]; 900 mc.type = AUDIO_MIXER_VALUE; 901 if (di->stereomask & (1<<n)) { 902 mc.un.value.num_channels = 2; 903 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 904 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 905 } else { 906 mc.un.value.num_channels = 1; 907 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 908 } 909 DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 910 n, di->devmap[n], l, r, idat)); 911 error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); 912 if (error) 913 goto out; 914 if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 915 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { 916 error = 0; 917 goto out; 918 } 919 goto doread; 920 } else { 921 #ifdef AUDIO_DEBUG 922 printf("oss_audio: unknown mixer ioctl %04lx\n", com); 923 #endif 924 error = EINVAL; 925 goto out; 926 } 927 } 928 error = copyout(&idat, SCARG(uap, data), sizeof idat); 929 out: 930 fd_putfile(SCARG(uap, fd)); 931 return error; 932 } 933 934 /* Sequencer emulation */ 935 int 936 oss_ioctl_sequencer(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t *retval) 937 { 938 /* { 939 syscallarg(int) fd; 940 syscallarg(u_long) com; 941 syscallarg(void *) data; 942 } */ 943 file_t *fp; 944 u_long com; 945 int idat, idat1; 946 struct synth_info si; 947 struct oss_synth_info osi; 948 struct oss_seq_event_rec oser; 949 int error; 950 int (*ioctlf)(file_t *, u_long, void *); 951 952 if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) 953 return (EBADF); 954 955 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 956 error = EBADF; 957 goto out; 958 } 959 960 com = SCARG(uap, com); 961 DPRINTF(("oss_ioctl_sequencer: com=%08lx\n", com)); 962 963 retval[0] = 0; 964 965 ioctlf = fp->f_ops->fo_ioctl; 966 switch (com) { 967 case OSS_SEQ_RESET: 968 error = ioctlf(fp, SEQUENCER_RESET, &idat); 969 goto out; 970 case OSS_SEQ_SYNC: 971 error = ioctlf(fp, SEQUENCER_SYNC, &idat); 972 goto out; 973 case OSS_SYNTH_INFO: 974 error = copyin(SCARG(uap, data), &osi, sizeof osi); 975 if (error) 976 goto out; 977 si.device = osi.device; 978 error = ioctlf(fp, SEQUENCER_INFO, &si); 979 if (error) 980 goto out; 981 strncpy(osi.name, si.name, sizeof osi.name); 982 osi.device = si.device; 983 switch(si.synth_type) { 984 case SYNTH_TYPE_FM: 985 osi.synth_type = OSS_SYNTH_TYPE_FM; break; 986 case SYNTH_TYPE_SAMPLE: 987 osi.synth_type = OSS_SYNTH_TYPE_SAMPLE; break; 988 case SYNTH_TYPE_MIDI: 989 osi.synth_type = OSS_SYNTH_TYPE_MIDI; break; 990 default: 991 osi.synth_type = 0; break; 992 } 993 switch(si.synth_subtype) { 994 case SYNTH_SUB_FM_TYPE_ADLIB: 995 osi.synth_subtype = OSS_FM_TYPE_ADLIB; break; 996 case SYNTH_SUB_FM_TYPE_OPL3: 997 osi.synth_subtype = OSS_FM_TYPE_OPL3; break; 998 case SYNTH_SUB_MIDI_TYPE_MPU401: 999 osi.synth_subtype = OSS_MIDI_TYPE_MPU401; break; 1000 case SYNTH_SUB_SAMPLE_TYPE_BASIC: 1001 osi.synth_subtype = OSS_SAMPLE_TYPE_BASIC; break; 1002 default: 1003 osi.synth_subtype = 0; break; 1004 } 1005 osi.perc_mode = 0; 1006 osi.nr_voices = si.nr_voices; 1007 osi.nr_drums = 0; 1008 osi.instr_bank_size = si.instr_bank_size; 1009 osi.capabilities = 0; 1010 if (si.capabilities & SYNTH_CAP_OPL3) 1011 osi.capabilities |= OSS_SYNTH_CAP_OPL3; 1012 if (si.capabilities & SYNTH_CAP_INPUT) 1013 osi.capabilities |= OSS_SYNTH_CAP_INPUT; 1014 error = copyout(&osi, SCARG(uap, data), sizeof osi); 1015 goto out; 1016 case OSS_SEQ_CTRLRATE: 1017 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1018 if (error) 1019 goto out; 1020 error = ioctlf(fp, SEQUENCER_CTRLRATE, &idat); 1021 if (error) 1022 goto out; 1023 retval[0] = idat; 1024 break; 1025 case OSS_SEQ_GETOUTCOUNT: 1026 error = ioctlf(fp, SEQUENCER_GETOUTCOUNT, &idat); 1027 if (error) 1028 goto out; 1029 retval[0] = idat; 1030 break; 1031 case OSS_SEQ_GETINCOUNT: 1032 error = ioctlf(fp, SEQUENCER_GETINCOUNT, &idat); 1033 if (error) 1034 goto out; 1035 retval[0] = idat; 1036 break; 1037 case OSS_SEQ_NRSYNTHS: 1038 error = ioctlf(fp, SEQUENCER_NRSYNTHS, &idat); 1039 if (error) 1040 goto out; 1041 retval[0] = idat; 1042 break; 1043 case OSS_SEQ_NRMIDIS: 1044 error = ioctlf(fp, SEQUENCER_NRMIDIS, &idat); 1045 if (error) 1046 goto out; 1047 retval[0] = idat; 1048 break; 1049 case OSS_SEQ_THRESHOLD: 1050 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1051 if (error) 1052 goto out; 1053 error = ioctlf(fp, SEQUENCER_THRESHOLD, &idat); 1054 goto out; 1055 case OSS_MEMAVL: 1056 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1057 if (error) 1058 goto out; 1059 error = ioctlf(fp, SEQUENCER_MEMAVL, &idat); 1060 if (error) 1061 goto out; 1062 retval[0] = idat; 1063 break; 1064 case OSS_SEQ_PANIC: 1065 error = ioctlf(fp, SEQUENCER_PANIC, &idat); 1066 goto out; 1067 case OSS_SEQ_OUTOFBAND: 1068 error = copyin(SCARG(uap, data), &oser, sizeof oser); 1069 if (error) 1070 goto out; 1071 error = ioctlf(fp, SEQUENCER_OUTOFBAND, &oser); 1072 if (error) 1073 goto out; 1074 break; 1075 case OSS_SEQ_GETTIME: 1076 error = ioctlf(fp, SEQUENCER_GETTIME, &idat); 1077 if (error) 1078 goto out; 1079 retval[0] = idat; 1080 break; 1081 case OSS_TMR_TIMEBASE: 1082 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1083 if (error) 1084 goto out; 1085 error = ioctlf(fp, SEQUENCER_TMR_TIMEBASE, &idat); 1086 if (error) 1087 goto out; 1088 retval[0] = idat; 1089 break; 1090 case OSS_TMR_START: 1091 error = ioctlf(fp, SEQUENCER_TMR_START, &idat); 1092 goto out; 1093 case OSS_TMR_STOP: 1094 error = ioctlf(fp, SEQUENCER_TMR_STOP, &idat); 1095 goto out; 1096 case OSS_TMR_CONTINUE: 1097 error = ioctlf(fp, SEQUENCER_TMR_CONTINUE, &idat); 1098 goto out; 1099 case OSS_TMR_TEMPO: 1100 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1101 if (error) 1102 goto out; 1103 error = ioctlf(fp, SEQUENCER_TMR_TEMPO, &idat); 1104 if (error) 1105 goto out; 1106 retval[0] = idat; 1107 break; 1108 case OSS_TMR_SOURCE: 1109 error = copyin(SCARG(uap, data), &idat1, sizeof idat); 1110 if (error) 1111 goto out; 1112 idat = 0; 1113 if (idat1 & OSS_TMR_INTERNAL) idat |= SEQUENCER_TMR_INTERNAL; 1114 error = ioctlf(fp, SEQUENCER_TMR_SOURCE, &idat); 1115 if (error) 1116 goto out; 1117 idat1 = idat; 1118 if (idat1 & SEQUENCER_TMR_INTERNAL) idat |= OSS_TMR_INTERNAL; 1119 retval[0] = idat; 1120 break; 1121 case OSS_TMR_METRONOME: 1122 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1123 if (error) 1124 goto out; 1125 error = ioctlf(fp, SEQUENCER_TMR_METRONOME, &idat); 1126 goto out; 1127 case OSS_TMR_SELECT: 1128 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1129 if (error) 1130 goto out; 1131 retval[0] = idat; 1132 error = ioctlf(fp, SEQUENCER_TMR_SELECT, &idat); 1133 goto out; 1134 default: 1135 error = EINVAL; 1136 goto out; 1137 } 1138 1139 error = copyout(&idat, SCARG(uap, data), sizeof idat); 1140 out: 1141 fd_putfile(SCARG(uap, fd)); 1142 return error; 1143 } 1144 1145 /* 1146 * Check that the blocksize is a power of 2 as OSS wants. 1147 * If not, set it to be. 1148 */ 1149 static void 1150 setblocksize(file_t *fp, struct audio_info *info) 1151 { 1152 struct audio_info set; 1153 int s; 1154 1155 if (info->blocksize & (info->blocksize-1)) { 1156 for(s = 32; s < info->blocksize; s <<= 1) 1157 ; 1158 AUDIO_INITINFO(&set); 1159 set.blocksize = s; 1160 fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, &set); 1161 fp->f_ops->fo_ioctl(fp, AUDIO_GETBUFINFO, info); 1162 } 1163 } 1164