1 /* $NetBSD: ossaudio.c,v 1.30 1999/11/17 00:06:38 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) * 255 / 100) 62 63 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *)); 64 65 static void setblocksize __P((struct file *, struct audio_info *, struct proc *)); 66 67 68 int 69 oss_ioctl_audio(p, uap, retval) 70 struct proc *p; 71 struct oss_sys_ioctl_args /* { 72 syscallarg(int) fd; 73 syscallarg(u_long) com; 74 syscallarg(caddr_t) data; 75 } */ *uap; 76 register_t *retval; 77 { 78 struct file *fp; 79 struct filedesc *fdp; 80 u_long com; 81 struct audio_info tmpinfo; 82 struct audio_offset tmpoffs; 83 struct oss_audio_buf_info bufinfo; 84 struct oss_count_info cntinfo; 85 struct audio_encoding tmpenc; 86 u_int u; 87 int idat, idata; 88 int error = 0; 89 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 90 91 fdp = p->p_fd; 92 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 93 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || 94 (fp->f_iflags & FIF_WANTCLOSE) != 0) 95 return (EBADF); 96 97 FILE_USE(fp); 98 99 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 100 error = EBADF; 101 goto out; 102 } 103 104 com = SCARG(uap, com); 105 DPRINTF(("oss_ioctl_audio: com=%08lx\n", com)); 106 107 retval[0] = 0; 108 109 ioctlf = fp->f_ops->fo_ioctl; 110 switch (com) { 111 case OSS_SNDCTL_DSP_RESET: 112 error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p); 113 if (error) 114 goto out; 115 break; 116 case OSS_SNDCTL_DSP_SYNC: 117 case OSS_SNDCTL_DSP_POST: 118 error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p); 119 if (error) 120 goto out; 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, (caddr_t)&tmpinfo, p); 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_GETINFO, (caddr_t)&tmpinfo, p); 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, (caddr_t)&tmpinfo, p); 152 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 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_GETINFO, (caddr_t)&tmpinfo, p); 162 if (error) 163 goto out; 164 setblocksize(fp, &tmpinfo, p); 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, (caddr_t)&tmpinfo, p); 229 /* fall into ... */ 230 case OSS_SOUND_PCM_READ_BITS: 231 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 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, (caddr_t)&tmpinfo, p); 281 /* fall into ... */ 282 case OSS_SOUND_PCM_READ_CHANNELS: 283 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 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_GETINFO, (caddr_t)&tmpinfo, p); 300 setblocksize(fp, &tmpinfo, p); 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, (caddr_t)&tmpinfo, p); 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, (caddr_t)&tmpinfo, p); 332 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 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, (caddr_t)&tmpenc, p) == 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 case OSS_SNDCTL_DSP_GETISPACE: 398 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 399 if (error) 400 goto out; 401 setblocksize(fp, &tmpinfo, p); 402 bufinfo.fragsize = tmpinfo.blocksize; 403 bufinfo.fragments = /* XXX */ 404 bufinfo.fragstotal = tmpinfo.play.buffer_size / bufinfo.fragsize; 405 bufinfo.bytes = tmpinfo.play.buffer_size; 406 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n", 407 bufinfo.fragsize, bufinfo.fragments, 408 bufinfo.fragstotal, bufinfo.bytes)); 409 error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); 410 if (error) 411 goto out; 412 break; 413 case OSS_SNDCTL_DSP_NONBLOCK: 414 idat = 1; 415 error = ioctlf(fp, FIONBIO, (caddr_t)&idat, p); 416 if (error) 417 goto out; 418 break; 419 case OSS_SNDCTL_DSP_GETCAPS: 420 error = ioctlf(fp, AUDIO_GETPROPS, (caddr_t)&idata, p); 421 if (error) 422 goto out; 423 idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */ 424 if (idata & AUDIO_PROP_FULLDUPLEX) 425 idat |= OSS_DSP_CAP_DUPLEX; 426 if (idata & AUDIO_PROP_MMAP) 427 idat |= OSS_DSP_CAP_MMAP; 428 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat)); 429 error = copyout(&idat, SCARG(uap, data), sizeof idat); 430 if (error) 431 goto out; 432 break; 433 #if 0 434 case OSS_SNDCTL_DSP_GETTRIGGER: 435 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 436 if (error) 437 goto out; 438 idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) | 439 (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT); 440 error = copyout(&idat, SCARG(uap, data), sizeof idat); 441 if (error) 442 goto out; 443 break; 444 case OSS_SNDCTL_DSP_SETTRIGGER: 445 AUDIO_INITINFO(&tmpinfo); 446 error = copyin(SCARG(uap, data), &idat, sizeof idat); 447 if (error) 448 goto out; 449 tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0; 450 tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0; 451 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 452 error = copyout(&idat, SCARG(uap, data), sizeof idat); 453 if (error) 454 goto out; 455 break; 456 #else 457 case OSS_SNDCTL_DSP_GETTRIGGER: 458 case OSS_SNDCTL_DSP_SETTRIGGER: 459 /* XXX Do nothing for now. */ 460 idat = OSS_PCM_ENABLE_OUTPUT; 461 error = copyout(&idat, SCARG(uap, data), sizeof idat); 462 goto out; 463 #endif 464 case OSS_SNDCTL_DSP_GETIPTR: 465 error = ioctlf(fp, AUDIO_GETIOFFS, (caddr_t)&tmpoffs, p); 466 if (error) 467 goto out; 468 cntinfo.bytes = tmpoffs.samples; 469 cntinfo.blocks = tmpoffs.deltablks; 470 cntinfo.ptr = tmpoffs.offset; 471 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 472 if (error) 473 goto out; 474 break; 475 case OSS_SNDCTL_DSP_GETOPTR: 476 error = ioctlf(fp, AUDIO_GETOOFFS, (caddr_t)&tmpoffs, p); 477 if (error) 478 goto out; 479 cntinfo.bytes = tmpoffs.samples; 480 cntinfo.blocks = tmpoffs.deltablks; 481 cntinfo.ptr = tmpoffs.offset; 482 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 483 if (error) 484 goto out; 485 break; 486 case OSS_SNDCTL_DSP_MAPINBUF: 487 case OSS_SNDCTL_DSP_MAPOUTBUF: 488 case OSS_SNDCTL_DSP_SETSYNCRO: 489 case OSS_SNDCTL_DSP_SETDUPLEX: 490 case OSS_SNDCTL_DSP_PROFILE: 491 error = EINVAL; 492 goto out; 493 default: 494 error = EINVAL; 495 goto out; 496 } 497 498 out: 499 FILE_UNUSE(fp, p); 500 return error; 501 } 502 503 /* If the NetBSD mixer device should have more than 32 devices 504 * some will not be available to Linux */ 505 #define NETBSD_MAXDEVS 64 506 struct audiodevinfo { 507 int done; 508 dev_t dev; 509 int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], 510 rdevmap[NETBSD_MAXDEVS]; 511 u_long devmask, recmask, stereomask; 512 u_long caps, source; 513 }; 514 515 /* 516 * Collect the audio device information to allow faster 517 * emulation of the Linux mixer ioctls. Cache the information 518 * to eliminate the overhead of repeating all the ioctls needed 519 * to collect the information. 520 */ 521 static struct audiodevinfo * 522 getdevinfo(fp, p) 523 struct file *fp; 524 struct proc *p; 525 { 526 mixer_devinfo_t mi; 527 int i; 528 static struct { 529 char *name; 530 int code; 531 } *dp, devs[] = { 532 { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, 533 { AudioNline, OSS_SOUND_MIXER_LINE }, 534 { AudioNcd, OSS_SOUND_MIXER_CD }, 535 { AudioNdac, OSS_SOUND_MIXER_PCM }, 536 { AudioNrecord, OSS_SOUND_MIXER_IMIX }, 537 { AudioNmaster, OSS_SOUND_MIXER_VOLUME }, 538 { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, 539 { AudioNbass, OSS_SOUND_MIXER_BASS }, 540 { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, 541 /* { AudioNheadphone, ?? },*/ 542 { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, 543 { AudioNinput, OSS_SOUND_MIXER_IGAIN }, 544 /* { AudioNmaster, OSS_SOUND_MIXER_SPEAKER },*/ 545 /* { AudioNstereo, ?? },*/ 546 /* { AudioNmono, ?? },*/ 547 { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, 548 /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ 549 { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, 550 /* { AudioNmixerout, ?? },*/ 551 { 0, -1 } 552 }; 553 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) = 554 fp->f_ops->fo_ioctl; 555 struct vnode *vp; 556 struct vattr va; 557 static struct audiodevinfo devcache = { 0 }; 558 struct audiodevinfo *di = &devcache; 559 560 /* Figure out what device it is so we can check if the 561 * cached data is valid. 562 */ 563 vp = (struct vnode *)fp->f_data; 564 if (vp->v_type != VCHR) 565 return 0; 566 if (VOP_GETATTR(vp, &va, p->p_ucred, p)) 567 return 0; 568 if (di->done && di->dev == va.va_rdev) 569 return di; 570 571 di->done = 1; 572 di->dev = va.va_rdev; 573 di->devmask = 0; 574 di->recmask = 0; 575 di->stereomask = 0; 576 di->source = -1; 577 di->caps = 0; 578 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 579 di->devmap[i] = -1; 580 for(i = 0; i < NETBSD_MAXDEVS; i++) 581 di->rdevmap[i] = -1; 582 for(i = 0; i < NETBSD_MAXDEVS; i++) { 583 mi.index = i; 584 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0) 585 break; 586 switch(mi.type) { 587 case AUDIO_MIXER_VALUE: 588 for(dp = devs; dp->name; dp++) 589 if (strcmp(dp->name, mi.label.name) == 0) 590 break; 591 if (dp->code >= 0) { 592 di->devmap[dp->code] = i; 593 di->rdevmap[i] = dp->code; 594 di->devmask |= 1 << dp->code; 595 if (mi.un.v.num_channels == 2) 596 di->stereomask |= 1 << dp->code; 597 } 598 break; 599 case AUDIO_MIXER_ENUM: 600 if (strcmp(mi.label.name, AudioNsource) == 0) { 601 int j; 602 di->source = i; 603 for(j = 0; j < mi.un.e.num_mem; j++) 604 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 605 di->caps = OSS_SOUND_CAP_EXCL_INPUT; 606 } 607 break; 608 case AUDIO_MIXER_SET: 609 if (strcmp(mi.label.name, AudioNsource) == 0) { 610 int j; 611 di->source = i; 612 for(j = 0; j < mi.un.s.num_mem; j++) { 613 int k, mask = mi.un.s.member[j].mask; 614 if (mask) { 615 for(k = 0; !(mask & 1); mask >>= 1, k++) 616 ; 617 di->recmask |= 1 << di->rdevmap[k]; 618 } 619 } 620 } 621 break; 622 } 623 } 624 return di; 625 } 626 627 int 628 oss_ioctl_mixer(p, uap, retval) 629 struct proc *p; 630 struct oss_sys_ioctl_args /* { 631 syscallarg(int) fd; 632 syscallarg(u_long) com; 633 syscallarg(caddr_t) data; 634 } */ *uap; 635 register_t *retval; 636 { 637 struct file *fp; 638 struct filedesc *fdp; 639 u_long com; 640 struct audiodevinfo *di; 641 mixer_ctrl_t mc; 642 struct oss_mixer_info omi; 643 struct audio_device adev; 644 int idat; 645 int i; 646 int error; 647 int l, r, n; 648 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 649 650 fdp = p->p_fd; 651 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 652 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || 653 (fp->f_iflags & FIF_WANTCLOSE) != 0) 654 return (EBADF); 655 656 FILE_USE(fp); 657 658 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 659 error = EBADF; 660 goto out; 661 } 662 663 com = SCARG(uap, com); 664 DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com)); 665 666 retval[0] = 0; 667 668 di = getdevinfo(fp, p); 669 if (di == 0) { 670 error = EINVAL; 671 goto out; 672 } 673 674 ioctlf = fp->f_ops->fo_ioctl; 675 switch (com) { 676 case OSS_SOUND_MIXER_INFO: 677 case OSS_SOUND_OLD_MIXER_INFO: 678 error = ioctlf(fp, AUDIO_GETDEV, (caddr_t)&adev, p); 679 if (error) 680 return (error); 681 omi.modify_counter = 1; 682 strncpy(omi.id, adev.name, sizeof omi.id); 683 strncpy(omi.name, adev.name, sizeof omi.name); 684 return copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com)); 685 case OSS_SOUND_MIXER_READ_RECSRC: 686 if (di->source == -1) { 687 error = EINVAL; 688 goto out; 689 } 690 mc.dev = di->source; 691 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 692 mc.type = AUDIO_MIXER_ENUM; 693 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 694 if (error) 695 goto out; 696 idat = 1 << di->rdevmap[mc.un.ord]; 697 } else { 698 int k; 699 unsigned int mask; 700 mc.type = AUDIO_MIXER_SET; 701 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 702 if (error) 703 goto out; 704 idat = 0; 705 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 706 if (mask & 1) 707 idat |= 1 << di->rdevmap[k]; 708 } 709 break; 710 case OSS_SOUND_MIXER_READ_DEVMASK: 711 idat = di->devmask; 712 break; 713 case OSS_SOUND_MIXER_READ_RECMASK: 714 idat = di->recmask; 715 break; 716 case OSS_SOUND_MIXER_READ_STEREODEVS: 717 idat = di->stereomask; 718 break; 719 case OSS_SOUND_MIXER_READ_CAPS: 720 idat = di->caps; 721 break; 722 case OSS_SOUND_MIXER_WRITE_RECSRC: 723 case OSS_SOUND_MIXER_WRITE_R_RECSRC: 724 if (di->source == -1) { 725 error = EINVAL; 726 goto out; 727 } 728 mc.dev = di->source; 729 error = copyin(SCARG(uap, data), &idat, sizeof idat); 730 if (error) 731 goto out; 732 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 733 mc.type = AUDIO_MIXER_ENUM; 734 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 735 if (idat & (1 << i)) 736 break; 737 if (i >= OSS_SOUND_MIXER_NRDEVICES || 738 di->devmap[i] == -1) { 739 error = EINVAL; 740 goto out; 741 } 742 mc.un.ord = di->devmap[i]; 743 } else { 744 mc.type = AUDIO_MIXER_SET; 745 mc.un.mask = 0; 746 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { 747 if (idat & (1 << i)) { 748 if (di->devmap[i] == -1) { 749 error = EINVAL; 750 goto out; 751 } 752 mc.un.mask |= 1 << di->devmap[i]; 753 } 754 } 755 } 756 error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 757 goto out; 758 default: 759 if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && 760 com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { 761 n = OSS_GET_DEV(com); 762 if (di->devmap[n] == -1) { 763 error = EINVAL; 764 goto out; 765 } 766 doread: 767 mc.dev = di->devmap[n]; 768 mc.type = AUDIO_MIXER_VALUE; 769 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 770 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 771 if (error) 772 goto out; 773 if (mc.un.value.num_channels != 2) { 774 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 775 } else { 776 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 777 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 778 } 779 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 780 DPRINTF(("OSS_MIXER_READ n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 781 n, di->devmap[n], l, r, idat)); 782 break; 783 } else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com && 784 com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) || 785 (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 786 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) { 787 n = OSS_GET_DEV(com); 788 if (di->devmap[n] == -1) { 789 error = EINVAL; 790 goto out; 791 } 792 error = copyin(SCARG(uap, data), &idat, sizeof idat); 793 if (error) 794 goto out; 795 l = FROM_OSSVOL( idat & 0xff); 796 r = FROM_OSSVOL((idat >> 8) & 0xff); 797 mc.dev = di->devmap[n]; 798 mc.type = AUDIO_MIXER_VALUE; 799 if (di->stereomask & (1<<n)) { 800 mc.un.value.num_channels = 2; 801 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 802 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 803 } else { 804 mc.un.value.num_channels = 1; 805 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 806 } 807 DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 808 n, di->devmap[n], l, r, idat)); 809 error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 810 if (error) 811 goto out; 812 if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 813 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { 814 error = 0; 815 goto out; 816 } 817 goto doread; 818 } else { 819 #ifdef AUDIO_DEBUG 820 printf("oss_audio: unknown mixer ioctl %04lx\n", com); 821 #endif 822 error = EINVAL; 823 goto out; 824 } 825 } 826 error = copyout(&idat, SCARG(uap, data), sizeof idat); 827 out: 828 FILE_UNUSE(fp, p); 829 return error; 830 } 831 832 /* Sequencer emulation */ 833 int 834 oss_ioctl_sequencer(p, uap, retval) 835 struct proc *p; 836 struct oss_sys_ioctl_args /* { 837 syscallarg(int) fd; 838 syscallarg(u_long) com; 839 syscallarg(caddr_t) data; 840 } */ *uap; 841 register_t *retval; 842 { 843 struct file *fp; 844 struct filedesc *fdp; 845 u_long com; 846 int idat, idat1; 847 struct synth_info si; 848 struct oss_synth_info osi; 849 struct oss_seq_event_rec oser; 850 int error; 851 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 852 853 fdp = p->p_fd; 854 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 855 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || 856 (fp->f_iflags & FIF_WANTCLOSE) != 0) 857 return (EBADF); 858 859 FILE_USE(fp); 860 861 if ((fp->f_flag & (FREAD | FWRITE)) == 0) { 862 error = EBADF; 863 goto out; 864 } 865 866 com = SCARG(uap, com); 867 DPRINTF(("oss_ioctl_sequencer: com=%08lx\n", com)); 868 869 retval[0] = 0; 870 871 ioctlf = fp->f_ops->fo_ioctl; 872 switch (com) { 873 case OSS_SEQ_RESET: 874 error = ioctlf(fp, SEQUENCER_RESET, (caddr_t)&idat, p); 875 goto out; 876 case OSS_SEQ_SYNC: 877 error = ioctlf(fp, SEQUENCER_SYNC, (caddr_t)&idat, p); 878 goto out; 879 case OSS_SYNTH_INFO: 880 error = copyin(SCARG(uap, data), &osi, sizeof osi); 881 if (error) 882 goto out; 883 si.device = osi.device; 884 error = ioctlf(fp, SEQUENCER_INFO, (caddr_t)&si, p); 885 if (error) 886 goto out; 887 strncpy(osi.name, si.name, sizeof osi.name); 888 osi.device = si.device; 889 switch(si.synth_type) { 890 case SYNTH_TYPE_FM: 891 osi.synth_type = OSS_SYNTH_TYPE_FM; break; 892 case SYNTH_TYPE_SAMPLE: 893 osi.synth_type = OSS_SYNTH_TYPE_SAMPLE; break; 894 case SYNTH_TYPE_MIDI: 895 osi.synth_type = OSS_SYNTH_TYPE_MIDI; break; 896 default: 897 osi.synth_type = 0; break; 898 } 899 switch(si.synth_subtype) { 900 case SYNTH_SUB_FM_TYPE_ADLIB: 901 osi.synth_subtype = OSS_FM_TYPE_ADLIB; break; 902 case SYNTH_SUB_FM_TYPE_OPL3: 903 osi.synth_subtype = OSS_FM_TYPE_OPL3; break; 904 case SYNTH_SUB_MIDI_TYPE_MPU401: 905 osi.synth_subtype = OSS_MIDI_TYPE_MPU401; break; 906 case SYNTH_SUB_SAMPLE_TYPE_BASIC: 907 osi.synth_subtype = OSS_SAMPLE_TYPE_BASIC; break; 908 default: 909 osi.synth_subtype = 0; break; 910 } 911 osi.perc_mode = 0; 912 osi.nr_voices = si.nr_voices; 913 osi.nr_drums = 0; 914 osi.instr_bank_size = si.instr_bank_size; 915 osi.capabilities = 0; 916 if (si.capabilities & SYNTH_CAP_OPL3) 917 osi.capabilities |= OSS_SYNTH_CAP_OPL3; 918 if (si.capabilities & SYNTH_CAP_INPUT) 919 osi.capabilities |= OSS_SYNTH_CAP_INPUT; 920 error = copyout(&osi, SCARG(uap, data), sizeof osi); 921 goto out; 922 case OSS_SEQ_CTRLRATE: 923 error = copyin(SCARG(uap, data), &idat, sizeof idat); 924 if (error) 925 goto out; 926 error = ioctlf(fp, SEQUENCER_CTRLRATE, (caddr_t)&idat, p); 927 if (error) 928 goto out; 929 retval[0] = idat; 930 break; 931 case OSS_SEQ_GETOUTCOUNT: 932 error = ioctlf(fp, SEQUENCER_GETOUTCOUNT, (caddr_t)&idat, p); 933 if (error) 934 goto out; 935 retval[0] = idat; 936 break; 937 case OSS_SEQ_GETINCOUNT: 938 error = ioctlf(fp, SEQUENCER_GETINCOUNT, (caddr_t)&idat, p); 939 if (error) 940 goto out; 941 retval[0] = idat; 942 break; 943 case OSS_SEQ_NRSYNTHS: 944 error = ioctlf(fp, SEQUENCER_NRSYNTHS, (caddr_t)&idat, p); 945 if (error) 946 goto out; 947 retval[0] = idat; 948 break; 949 case OSS_SEQ_NRMIDIS: 950 error = ioctlf(fp, SEQUENCER_NRMIDIS, (caddr_t)&idat, p); 951 if (error) 952 goto out; 953 retval[0] = idat; 954 break; 955 case OSS_SEQ_THRESHOLD: 956 error = copyin(SCARG(uap, data), &idat, sizeof idat); 957 if (error) 958 goto out; 959 error = ioctlf(fp, SEQUENCER_THRESHOLD, (caddr_t)&idat, p); 960 goto out; 961 case OSS_MEMAVL: 962 error = copyin(SCARG(uap, data), &idat, sizeof idat); 963 if (error) 964 goto out; 965 error = ioctlf(fp, SEQUENCER_MEMAVL, (caddr_t)&idat, p); 966 if (error) 967 goto out; 968 retval[0] = idat; 969 break; 970 case OSS_SEQ_PANIC: 971 error = ioctlf(fp, SEQUENCER_PANIC, (caddr_t)&idat, p); 972 goto out; 973 case OSS_SEQ_OUTOFBAND: 974 error = copyin(SCARG(uap, data), &oser, sizeof oser); 975 if (error) 976 goto out; 977 error = ioctlf(fp, SEQUENCER_OUTOFBAND, (caddr_t)&oser, p); 978 if (error) 979 goto out; 980 break; 981 case OSS_SEQ_GETTIME: 982 error = ioctlf(fp, SEQUENCER_GETTIME, (caddr_t)&idat, p); 983 if (error) 984 goto out; 985 retval[0] = idat; 986 break; 987 case OSS_TMR_TIMEBASE: 988 error = copyin(SCARG(uap, data), &idat, sizeof idat); 989 if (error) 990 goto out; 991 error = ioctlf(fp, SEQUENCER_TMR_TIMEBASE, (caddr_t)&idat, p); 992 if (error) 993 goto out; 994 retval[0] = idat; 995 break; 996 case OSS_TMR_START: 997 error = ioctlf(fp, SEQUENCER_TMR_START, (caddr_t)&idat, p); 998 goto out; 999 case OSS_TMR_STOP: 1000 error = ioctlf(fp, SEQUENCER_TMR_STOP, (caddr_t)&idat, p); 1001 goto out; 1002 case OSS_TMR_CONTINUE: 1003 error = ioctlf(fp, SEQUENCER_TMR_CONTINUE, (caddr_t)&idat, p); 1004 goto out; 1005 case OSS_TMR_TEMPO: 1006 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1007 if (error) 1008 goto out; 1009 error = ioctlf(fp, SEQUENCER_TMR_TEMPO, (caddr_t)&idat, p); 1010 if (error) 1011 goto out; 1012 retval[0] = idat; 1013 break; 1014 case OSS_TMR_SOURCE: 1015 error = copyin(SCARG(uap, data), &idat1, sizeof idat); 1016 if (error) 1017 goto out; 1018 idat = 0; 1019 if (idat1 & OSS_TMR_INTERNAL) idat |= SEQUENCER_TMR_INTERNAL; 1020 error = ioctlf(fp, SEQUENCER_TMR_SOURCE, (caddr_t)&idat, p); 1021 if (error) 1022 goto out; 1023 idat1 = idat; 1024 if (idat1 & SEQUENCER_TMR_INTERNAL) idat |= OSS_TMR_INTERNAL; 1025 retval[0] = idat; 1026 break; 1027 case OSS_TMR_METRONOME: 1028 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1029 if (error) 1030 goto out; 1031 error = ioctlf(fp, SEQUENCER_TMR_METRONOME, (caddr_t)&idat, p); 1032 goto out; 1033 case OSS_TMR_SELECT: 1034 error = copyin(SCARG(uap, data), &idat, sizeof idat); 1035 if (error) 1036 goto out; 1037 retval[0] = idat; 1038 error = ioctlf(fp, SEQUENCER_TMR_SELECT, (caddr_t)&idat, p); 1039 goto out; 1040 default: 1041 error = EINVAL; 1042 goto out; 1043 } 1044 1045 error = copyout(&idat, SCARG(uap, data), sizeof idat); 1046 out: 1047 FILE_UNUSE(fp, p); 1048 return error; 1049 } 1050 1051 /* 1052 * Check that the blocksize is a power of 2 as OSS wants. 1053 * If not, set it to be. 1054 */ 1055 static void setblocksize(fp, info, p) 1056 struct file *fp; 1057 struct audio_info *info; 1058 struct proc *p; 1059 { 1060 struct audio_info set; 1061 int s; 1062 1063 if (info->blocksize & (info->blocksize-1)) { 1064 for(s = 32; s < info->blocksize; s <<= 1) 1065 ; 1066 AUDIO_INITINFO(&set); 1067 set.blocksize = s; 1068 fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, (caddr_t)&set, p); 1069 fp->f_ops->fo_ioctl(fp, AUDIO_GETINFO, (caddr_t)info, p); 1070 } 1071 } 1072