1 /* $NetBSD: ossaudio.c,v 1.23 1997/10/19 07:41:52 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/audioio.h> 45 46 #include <sys/syscallargs.h> 47 48 #include <compat/ossaudio/ossaudio.h> 49 #include <compat/ossaudio/ossaudiovar.h> 50 51 #ifdef AUDIO_DEBUG 52 #define DPRINTF(x) if (ossdebug) printf x 53 int ossdebug = 0; 54 #else 55 #define DPRINTF(x) 56 #endif 57 58 #define TO_OSSVOL(x) ((x) * 100 / 255) 59 #define FROM_OSSVOL(x) ((x) * 255 / 100) 60 61 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *)); 62 63 static void setblocksize __P((struct file *, struct audio_info *, struct proc *)); 64 65 66 int 67 oss_ioctl_audio(p, uap, retval) 68 struct proc *p; 69 struct oss_sys_ioctl_args /* { 70 syscallarg(int) fd; 71 syscallarg(u_long) com; 72 syscallarg(caddr_t) data; 73 } */ *uap; 74 register_t *retval; 75 { 76 struct file *fp; 77 struct filedesc *fdp; 78 u_long com; 79 struct audio_info tmpinfo; 80 struct audio_offset tmpoffs; 81 struct oss_audio_buf_info bufinfo; 82 struct oss_count_info cntinfo; 83 struct audio_encoding tmpenc; 84 u_int u; 85 int idat, idata; 86 int error; 87 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 88 89 fdp = p->p_fd; 90 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 91 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 92 return (EBADF); 93 94 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 95 return (EBADF); 96 97 ioctlf = fp->f_ops->fo_ioctl; 98 99 com = SCARG(uap, com); 100 retval[0] = 0; 101 102 DPRINTF(("oss_sys_ioctl: com=%08lx\n", com)); 103 switch (com) { 104 case OSS_SNDCTL_DSP_RESET: 105 error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p); 106 if (error) 107 return error; 108 break; 109 case OSS_SNDCTL_DSP_SYNC: 110 case OSS_SNDCTL_DSP_POST: 111 error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p); 112 if (error) 113 return error; 114 break; 115 case OSS_SNDCTL_DSP_SPEED: 116 AUDIO_INITINFO(&tmpinfo); 117 error = copyin(SCARG(uap, data), &idat, sizeof idat); 118 if (error) 119 return error; 120 tmpinfo.play.sample_rate = 121 tmpinfo.record.sample_rate = idat; 122 error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 123 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n", 124 idat, error)); 125 if (error) 126 return error; 127 /* fall into ... */ 128 case OSS_SOUND_PCM_READ_RATE: 129 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 130 if (error) 131 return error; 132 idat = tmpinfo.play.sample_rate; 133 error = copyout(&idat, SCARG(uap, data), sizeof idat); 134 if (error) 135 return error; 136 break; 137 case OSS_SNDCTL_DSP_STEREO: 138 AUDIO_INITINFO(&tmpinfo); 139 error = copyin(SCARG(uap, data), &idat, sizeof idat); 140 if (error) 141 return error; 142 tmpinfo.play.channels = 143 tmpinfo.record.channels = idat ? 2 : 1; 144 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 145 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 146 if (error) 147 return error; 148 idat = tmpinfo.play.channels - 1; 149 error = copyout(&idat, SCARG(uap, data), sizeof idat); 150 if (error) 151 return error; 152 break; 153 case OSS_SNDCTL_DSP_GETBLKSIZE: 154 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 155 if (error) 156 return error; 157 setblocksize(fp, &tmpinfo, p); 158 idat = tmpinfo.blocksize; 159 error = copyout(&idat, SCARG(uap, data), sizeof idat); 160 if (error) 161 return error; 162 break; 163 case OSS_SNDCTL_DSP_SETFMT: 164 AUDIO_INITINFO(&tmpinfo); 165 error = copyin(SCARG(uap, data), &idat, sizeof idat); 166 if (error) 167 return error; 168 switch (idat) { 169 case OSS_AFMT_MU_LAW: 170 tmpinfo.play.precision = 171 tmpinfo.record.precision = 8; 172 tmpinfo.play.encoding = 173 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; 174 break; 175 case OSS_AFMT_A_LAW: 176 tmpinfo.play.precision = 177 tmpinfo.record.precision = 8; 178 tmpinfo.play.encoding = 179 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; 180 break; 181 case OSS_AFMT_U8: 182 tmpinfo.play.precision = 183 tmpinfo.record.precision = 8; 184 tmpinfo.play.encoding = 185 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; 186 break; 187 case OSS_AFMT_S8: 188 tmpinfo.play.precision = 189 tmpinfo.record.precision = 8; 190 tmpinfo.play.encoding = 191 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; 192 break; 193 case OSS_AFMT_S16_LE: 194 tmpinfo.play.precision = 195 tmpinfo.record.precision = 16; 196 tmpinfo.play.encoding = 197 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; 198 break; 199 case OSS_AFMT_S16_BE: 200 tmpinfo.play.precision = 201 tmpinfo.record.precision = 16; 202 tmpinfo.play.encoding = 203 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; 204 break; 205 case OSS_AFMT_U16_LE: 206 tmpinfo.play.precision = 207 tmpinfo.record.precision = 16; 208 tmpinfo.play.encoding = 209 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; 210 break; 211 case OSS_AFMT_U16_BE: 212 tmpinfo.play.precision = 213 tmpinfo.record.precision = 16; 214 tmpinfo.play.encoding = 215 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; 216 break; 217 default: 218 return EINVAL; 219 } 220 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 221 /* fall into ... */ 222 case OSS_SOUND_PCM_READ_BITS: 223 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 224 if (error) 225 return error; 226 switch (tmpinfo.play.encoding) { 227 case AUDIO_ENCODING_ULAW: 228 idat = OSS_AFMT_MU_LAW; 229 break; 230 case AUDIO_ENCODING_ALAW: 231 idat = OSS_AFMT_A_LAW; 232 break; 233 case AUDIO_ENCODING_SLINEAR_LE: 234 if (tmpinfo.play.precision == 16) 235 idat = OSS_AFMT_S16_LE; 236 else 237 idat = OSS_AFMT_S8; 238 break; 239 case AUDIO_ENCODING_SLINEAR_BE: 240 if (tmpinfo.play.precision == 16) 241 idat = OSS_AFMT_S16_BE; 242 else 243 idat = OSS_AFMT_S8; 244 break; 245 case AUDIO_ENCODING_ULINEAR_LE: 246 if (tmpinfo.play.precision == 16) 247 idat = OSS_AFMT_U16_LE; 248 else 249 idat = OSS_AFMT_U8; 250 break; 251 case AUDIO_ENCODING_ULINEAR_BE: 252 if (tmpinfo.play.precision == 16) 253 idat = OSS_AFMT_U16_BE; 254 else 255 idat = OSS_AFMT_U8; 256 break; 257 case AUDIO_ENCODING_ADPCM: 258 idat = OSS_AFMT_IMA_ADPCM; 259 break; 260 } 261 error = copyout(&idat, SCARG(uap, data), sizeof idat); 262 if (error) 263 return error; 264 break; 265 case OSS_SNDCTL_DSP_CHANNELS: 266 AUDIO_INITINFO(&tmpinfo); 267 error = copyin(SCARG(uap, data), &idat, sizeof idat); 268 if (error) 269 return error; 270 tmpinfo.play.channels = 271 tmpinfo.record.channels = idat; 272 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 273 /* fall into ... */ 274 case OSS_SOUND_PCM_READ_CHANNELS: 275 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 276 if (error) 277 return error; 278 idat = tmpinfo.play.channels; 279 error = copyout(&idat, SCARG(uap, data), sizeof idat); 280 if (error) 281 return error; 282 break; 283 case OSS_SOUND_PCM_WRITE_FILTER: 284 case OSS_SOUND_PCM_READ_FILTER: 285 return EINVAL; /* XXX unimplemented */ 286 case OSS_SNDCTL_DSP_SUBDIVIDE: 287 error = copyin(SCARG(uap, data), &idat, sizeof idat); 288 if (error) 289 return error; 290 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 291 setblocksize(fp, &tmpinfo, p); 292 if (error) 293 return error; 294 if (idat == 0) 295 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 296 idat = (tmpinfo.play.buffer_size / idat) & -4; 297 AUDIO_INITINFO(&tmpinfo); 298 tmpinfo.blocksize = idat; 299 error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 300 if (error) 301 return error; 302 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 303 error = copyout(&idat, SCARG(uap, data), sizeof idat); 304 if (error) 305 return error; 306 break; 307 case OSS_SNDCTL_DSP_SETFRAGMENT: 308 AUDIO_INITINFO(&tmpinfo); 309 error = copyin(SCARG(uap, data), &idat, sizeof idat); 310 if (error) 311 return error; 312 if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) 313 return EINVAL; 314 tmpinfo.blocksize = 1 << (idat & 0xffff); 315 tmpinfo.hiwat = (idat >> 16) & 0xffff; 316 DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n", 317 tmpinfo.blocksize, tmpinfo.hiwat)); 318 if (tmpinfo.hiwat == 0) /* 0 means set to max */ 319 tmpinfo.hiwat = 65536; 320 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 321 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 322 if (error) 323 return error; 324 u = tmpinfo.blocksize; 325 for(idat = 0; u; idat++, u >>= 1) 326 ; 327 idat |= (tmpinfo.hiwat & 0xffff) << 16; 328 error = copyout(&idat, SCARG(uap, data), sizeof idat); 329 if (error) 330 return error; 331 break; 332 case OSS_SNDCTL_DSP_GETFMTS: 333 for(idat = 0, tmpenc.index = 0; 334 ioctlf(fp, AUDIO_GETENC, (caddr_t)&tmpenc, p) == 0; 335 tmpenc.index++) { 336 if (tmpenc.flags & AUDIO_ENCODINGFLAG_EMULATED) 337 continue; /* Don't report emulated modes */ 338 switch(tmpenc.encoding) { 339 case AUDIO_ENCODING_ULAW: 340 idat |= OSS_AFMT_MU_LAW; 341 break; 342 case AUDIO_ENCODING_ALAW: 343 idat |= OSS_AFMT_A_LAW; 344 break; 345 case AUDIO_ENCODING_SLINEAR: 346 idat |= OSS_AFMT_S8; 347 break; 348 case AUDIO_ENCODING_SLINEAR_LE: 349 if (tmpenc.precision == 16) 350 idat |= OSS_AFMT_S16_LE; 351 else 352 idat |= OSS_AFMT_S8; 353 break; 354 case AUDIO_ENCODING_SLINEAR_BE: 355 if (tmpenc.precision == 16) 356 idat |= OSS_AFMT_S16_BE; 357 else 358 idat |= OSS_AFMT_S8; 359 break; 360 case AUDIO_ENCODING_ULINEAR: 361 idat |= OSS_AFMT_U8; 362 break; 363 case AUDIO_ENCODING_ULINEAR_LE: 364 if (tmpenc.precision == 16) 365 idat |= OSS_AFMT_U16_LE; 366 else 367 idat |= OSS_AFMT_U8; 368 break; 369 case AUDIO_ENCODING_ULINEAR_BE: 370 if (tmpenc.precision == 16) 371 idat |= OSS_AFMT_U16_BE; 372 else 373 idat |= OSS_AFMT_U8; 374 break; 375 case AUDIO_ENCODING_ADPCM: 376 idat |= OSS_AFMT_IMA_ADPCM; 377 break; 378 default: 379 break; 380 } 381 } 382 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat)); 383 error = copyout(&idat, SCARG(uap, data), sizeof idat); 384 if (error) 385 return error; 386 break; 387 case OSS_SNDCTL_DSP_GETOSPACE: 388 case OSS_SNDCTL_DSP_GETISPACE: 389 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 390 if (error) 391 return error; 392 setblocksize(fp, &tmpinfo, p); 393 bufinfo.fragsize = tmpinfo.blocksize; 394 bufinfo.fragments = /* XXX */ 395 bufinfo.fragstotal = tmpinfo.play.buffer_size / bufinfo.fragsize; 396 bufinfo.bytes = tmpinfo.play.buffer_size; 397 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n", 398 bufinfo.fragsize, bufinfo.fragments, 399 bufinfo.fragstotal, bufinfo.bytes)); 400 error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); 401 if (error) 402 return error; 403 break; 404 case OSS_SNDCTL_DSP_NONBLOCK: 405 idat = 1; 406 error = ioctlf(fp, FIONBIO, (caddr_t)&idat, p); 407 if (error) 408 return error; 409 break; 410 case OSS_SNDCTL_DSP_GETCAPS: 411 error = ioctlf(fp, AUDIO_GETPROPS, (caddr_t)&idata, p); 412 if (error) 413 return error; 414 idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */ 415 if (idata & AUDIO_PROP_FULLDUPLEX) 416 idat |= OSS_DSP_CAP_DUPLEX; 417 if (idata & AUDIO_PROP_MMAP) 418 idat |= OSS_DSP_CAP_MMAP; 419 DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat)); 420 error = copyout(&idat, SCARG(uap, data), sizeof idat); 421 if (error) 422 return error; 423 break; 424 #if 0 425 case OSS_SNDCTL_DSP_GETTRIGGER: 426 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 427 if (error) 428 return error; 429 idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) | 430 (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT); 431 error = copyout(&idat, SCARG(uap, data), sizeof idat); 432 if (error) 433 return error; 434 break; 435 case OSS_SNDCTL_DSP_SETTRIGGER: 436 AUDIO_INITINFO(&tmpinfo); 437 error = copyin(SCARG(uap, data), &idat, sizeof idat); 438 if (error) 439 return error; 440 tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0; 441 tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0; 442 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 443 error = copyout(&idat, SCARG(uap, data), sizeof idat); 444 if (error) 445 return error; 446 break; 447 #else 448 case OSS_SNDCTL_DSP_GETTRIGGER: 449 case OSS_SNDCTL_DSP_SETTRIGGER: 450 /* XXX Do nothing for now. */ 451 idat = OSS_PCM_ENABLE_OUTPUT; 452 return copyout(&idat, SCARG(uap, data), sizeof idat); 453 #endif 454 case OSS_SNDCTL_DSP_GETIPTR: 455 error = ioctlf(fp, AUDIO_GETIOFFS, (caddr_t)&tmpoffs, p); 456 if (error) 457 return error; 458 cntinfo.bytes = tmpoffs.samples; 459 cntinfo.blocks = tmpoffs.deltablks; 460 cntinfo.ptr = tmpoffs.offset; 461 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 462 if (error) 463 return error; 464 break; 465 case OSS_SNDCTL_DSP_GETOPTR: 466 error = ioctlf(fp, AUDIO_GETOOFFS, (caddr_t)&tmpoffs, p); 467 if (error) 468 return error; 469 cntinfo.bytes = tmpoffs.samples; 470 cntinfo.blocks = tmpoffs.deltablks; 471 cntinfo.ptr = tmpoffs.offset; 472 error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); 473 if (error) 474 return error; 475 break; 476 case OSS_SNDCTL_DSP_MAPINBUF: 477 case OSS_SNDCTL_DSP_MAPOUTBUF: 478 case OSS_SNDCTL_DSP_SETSYNCRO: 479 case OSS_SNDCTL_DSP_SETDUPLEX: 480 case OSS_SNDCTL_DSP_PROFILE: 481 return EINVAL; /* XXX unimplemented */ 482 default: 483 return EINVAL; 484 } 485 486 return 0; 487 } 488 489 /* If the NetBSD mixer device should have more than 32 devices 490 * some will not be available to Linux */ 491 #define NETBSD_MAXDEVS 64 492 struct audiodevinfo { 493 int done; 494 dev_t dev; 495 int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], 496 rdevmap[NETBSD_MAXDEVS]; 497 u_long devmask, recmask, stereomask; 498 u_long caps, source; 499 }; 500 501 /* 502 * Collect the audio device information to allow faster 503 * emulation of the Linux mixer ioctls. Cache the information 504 * to eliminate the overhead of repeating all the ioctls needed 505 * to collect the information. 506 */ 507 static struct audiodevinfo * 508 getdevinfo(fp, p) 509 struct file *fp; 510 struct proc *p; 511 { 512 mixer_devinfo_t mi; 513 int i; 514 static struct { 515 char *name; 516 int code; 517 } *dp, devs[] = { 518 { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, 519 { AudioNline, OSS_SOUND_MIXER_LINE }, 520 { AudioNcd, OSS_SOUND_MIXER_CD }, 521 { AudioNdac, OSS_SOUND_MIXER_PCM }, 522 { AudioNrecord, OSS_SOUND_MIXER_IMIX }, 523 { AudioNmaster, OSS_SOUND_MIXER_VOLUME }, 524 { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, 525 { AudioNbass, OSS_SOUND_MIXER_BASS }, 526 { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, 527 /* { AudioNheadphone, ?? },*/ 528 { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, 529 { AudioNinput, OSS_SOUND_MIXER_IGAIN }, 530 /* { AudioNmaster, OSS_SOUND_MIXER_SPEAKER },*/ 531 /* { AudioNstereo, ?? },*/ 532 /* { AudioNmono, ?? },*/ 533 { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, 534 /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ 535 { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, 536 /* { AudioNmixerout, ?? },*/ 537 { 0, -1 } 538 }; 539 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) = 540 fp->f_ops->fo_ioctl; 541 struct vnode *vp; 542 struct vattr va; 543 static struct audiodevinfo devcache = { 0 }; 544 struct audiodevinfo *di = &devcache; 545 546 /* Figure out what device it is so we can check if the 547 * cached data is valid. 548 */ 549 vp = (struct vnode *)fp->f_data; 550 if (vp->v_type != VCHR) 551 return 0; 552 if (VOP_GETATTR(vp, &va, p->p_ucred, p)) 553 return 0; 554 if (di->done && di->dev == va.va_rdev) 555 return di; 556 557 di->done = 1; 558 di->dev = va.va_rdev; 559 di->devmask = 0; 560 di->recmask = 0; 561 di->stereomask = 0; 562 di->source = -1; 563 di->caps = 0; 564 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 565 di->devmap[i] = -1; 566 for(i = 0; i < NETBSD_MAXDEVS; i++) 567 di->rdevmap[i] = -1; 568 for(i = 0; i < NETBSD_MAXDEVS; i++) { 569 mi.index = i; 570 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0) 571 break; 572 switch(mi.type) { 573 case AUDIO_MIXER_VALUE: 574 for(dp = devs; dp->name; dp++) 575 if (strcmp(dp->name, mi.label.name) == 0) 576 break; 577 if (dp->code >= 0) { 578 di->devmap[dp->code] = i; 579 di->rdevmap[i] = dp->code; 580 di->devmask |= 1 << dp->code; 581 if (mi.un.v.num_channels == 2) 582 di->stereomask |= 1 << dp->code; 583 } 584 break; 585 case AUDIO_MIXER_ENUM: 586 if (strcmp(mi.label.name, AudioNsource) == 0) { 587 int j; 588 di->source = i; 589 for(j = 0; j < mi.un.e.num_mem; j++) 590 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 591 di->caps = OSS_SOUND_CAP_EXCL_INPUT; 592 } 593 break; 594 case AUDIO_MIXER_SET: 595 if (strcmp(mi.label.name, AudioNsource) == 0) { 596 int j; 597 di->source = i; 598 for(j = 0; j < mi.un.s.num_mem; j++) { 599 int k, mask = mi.un.s.member[j].mask; 600 if (mask) { 601 for(k = 0; !(mask & 1); mask >>= 1, k++) 602 ; 603 di->recmask |= 1 << di->rdevmap[k]; 604 } 605 } 606 } 607 break; 608 } 609 } 610 return di; 611 } 612 613 int 614 oss_ioctl_mixer(p, uap, retval) 615 struct proc *p; 616 struct oss_sys_ioctl_args /* { 617 syscallarg(int) fd; 618 syscallarg(u_long) com; 619 syscallarg(caddr_t) data; 620 } */ *uap; 621 register_t *retval; 622 { 623 struct file *fp; 624 struct filedesc *fdp; 625 u_long com; 626 struct audiodevinfo *di; 627 mixer_ctrl_t mc; 628 int idat; 629 int i; 630 int error; 631 int l, r, n; 632 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 633 634 fdp = p->p_fd; 635 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 636 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 637 return (EBADF); 638 639 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 640 return (EBADF); 641 642 com = SCARG(uap, com); 643 retval[0] = 0; 644 645 di = getdevinfo(fp, p); 646 if (di == 0) 647 return EINVAL; 648 649 ioctlf = fp->f_ops->fo_ioctl; 650 switch (com) { 651 case OSS_SOUND_MIXER_READ_RECSRC: 652 if (di->source == -1) 653 return EINVAL; 654 mc.dev = di->source; 655 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 656 mc.type = AUDIO_MIXER_ENUM; 657 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 658 if (error) 659 return error; 660 idat = 1 << di->rdevmap[mc.un.ord]; 661 } else { 662 int k; 663 unsigned int mask; 664 mc.type = AUDIO_MIXER_SET; 665 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 666 if (error) 667 return error; 668 idat = 0; 669 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 670 if (mask & 1) 671 idat |= 1 << di->rdevmap[k]; 672 } 673 break; 674 case OSS_SOUND_MIXER_READ_DEVMASK: 675 idat = di->devmask; 676 break; 677 case OSS_SOUND_MIXER_READ_RECMASK: 678 idat = di->recmask; 679 break; 680 case OSS_SOUND_MIXER_READ_STEREODEVS: 681 idat = di->stereomask; 682 break; 683 case OSS_SOUND_MIXER_READ_CAPS: 684 idat = di->caps; 685 break; 686 case OSS_SOUND_MIXER_WRITE_RECSRC: 687 case OSS_SOUND_MIXER_WRITE_R_RECSRC: 688 if (di->source == -1) 689 return EINVAL; 690 mc.dev = di->source; 691 error = copyin(SCARG(uap, data), &idat, sizeof idat); 692 if (error) 693 return error; 694 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 695 mc.type = AUDIO_MIXER_ENUM; 696 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 697 if (idat & (1 << i)) 698 break; 699 if (i >= OSS_SOUND_MIXER_NRDEVICES || 700 di->devmap[i] == -1) 701 return EINVAL; 702 mc.un.ord = di->devmap[i]; 703 } else { 704 mc.type = AUDIO_MIXER_SET; 705 mc.un.mask = 0; 706 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { 707 if (idat & (1 << i)) { 708 if (di->devmap[i] == -1) 709 return EINVAL; 710 mc.un.mask |= 1 << di->devmap[i]; 711 } 712 } 713 } 714 return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 715 default: 716 if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && 717 com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { 718 n = OSS_GET_DEV(com); 719 if (di->devmap[n] == -1) 720 return EINVAL; 721 doread: 722 mc.dev = di->devmap[n]; 723 mc.type = AUDIO_MIXER_VALUE; 724 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 725 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 726 if (error) 727 return error; 728 if (mc.un.value.num_channels != 2) { 729 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 730 } else { 731 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 732 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 733 } 734 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 735 DPRINTF(("OSS_MIXER_READ n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 736 n, di->devmap[n], l, r, idat)); 737 break; 738 } else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com && 739 com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) || 740 (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 741 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) { 742 n = OSS_GET_DEV(com); 743 if (di->devmap[n] == -1) 744 return EINVAL; 745 error = copyin(SCARG(uap, data), &idat, sizeof idat); 746 if (error) 747 return error; 748 l = FROM_OSSVOL( idat & 0xff); 749 r = FROM_OSSVOL((idat >> 8) & 0xff); 750 mc.dev = di->devmap[n]; 751 mc.type = AUDIO_MIXER_VALUE; 752 if (di->stereomask & (1<<n)) { 753 mc.un.value.num_channels = 2; 754 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 755 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 756 } else { 757 mc.un.value.num_channels = 1; 758 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 759 } 760 DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", 761 n, di->devmap[n], l, r, idat)); 762 error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 763 if (error) 764 return error; 765 if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 766 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) 767 return 0; 768 goto doread; 769 } else { 770 #ifdef AUDIO_DEBUG 771 printf("oss_audio: unknown mixer ioctl %04lx\n", com); 772 #endif 773 return EINVAL; 774 } 775 } 776 return copyout(&idat, SCARG(uap, data), sizeof idat); 777 } 778 779 /* XXX hook for sequencer emulation */ 780 int 781 oss_ioctl_sequencer(p, uap, retval) 782 struct proc *p; 783 struct oss_sys_ioctl_args /* { 784 syscallarg(int) fd; 785 syscallarg(u_long) com; 786 syscallarg(caddr_t) data; 787 } */ *uap; 788 register_t *retval; 789 { 790 struct file *fp; 791 struct filedesc *fdp; 792 #if 0 793 u_long com; 794 int idat; 795 int error; 796 #endif 797 798 fdp = p->p_fd; 799 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 800 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 801 return (EBADF); 802 803 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 804 return (EBADF); 805 806 #if 0 807 com = SCARG(uap, com); 808 #endif 809 retval[0] = 0; 810 811 return EINVAL; 812 } 813 814 /* 815 * Check that the blocksize is a power of 2 as OSS wants. 816 * If not, set it to be. 817 */ 818 static void setblocksize(fp, info, p) 819 struct file *fp; 820 struct audio_info *info; 821 struct proc *p; 822 { 823 struct audio_info set; 824 int s; 825 826 if (info->blocksize & (info->blocksize-1)) { 827 for(s = 32; s < info->blocksize; s <<= 1) 828 ; 829 AUDIO_INITINFO(&set); 830 set.blocksize = s; 831 fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, (caddr_t)&set, p); 832 fp->f_ops->fo_ioctl(fp, AUDIO_GETINFO, (caddr_t)info, p); 833 } 834 } 835