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