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