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