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