1 /* $NetBSD: ossaudio.c,v 1.7 1997/04/06 23:49:32 augustss Exp $ */ 2 #include <sys/param.h> 3 #include <sys/proc.h> 4 #include <sys/systm.h> 5 #include <sys/file.h> 6 #include <sys/vnode.h> 7 #include <sys/filedesc.h> 8 #include <sys/ioctl.h> 9 #include <sys/mount.h> 10 #include <sys/audioio.h> 11 12 #include <sys/syscallargs.h> 13 14 #include <compat/ossaudio/ossaudio.h> 15 #include <compat/ossaudio/ossaudiovar.h> 16 17 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *)); 18 19 int 20 oss_ioctl_audio(p, uap, retval) 21 register struct proc *p; 22 register struct oss_sys_ioctl_args /* { 23 syscallarg(int) fd; 24 syscallarg(u_long) com; 25 syscallarg(caddr_t) data; 26 } */ *uap; 27 register_t *retval; 28 { 29 register struct file *fp; 30 register struct filedesc *fdp; 31 u_long com; 32 struct audio_info tmpinfo; 33 int idat; 34 int error; 35 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 36 37 fdp = p->p_fd; 38 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 39 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 40 return (EBADF); 41 42 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 43 return (EBADF); 44 45 ioctlf = fp->f_ops->fo_ioctl; 46 47 com = SCARG(uap, com); 48 retval[0] = 0; 49 50 switch (com) { 51 case OSS_SNDCTL_DSP_RESET: 52 error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p); 53 if (error) 54 return error; 55 break; 56 case OSS_SNDCTL_DSP_SYNC: 57 case OSS_SNDCTL_DSP_POST: 58 error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p); 59 if (error) 60 return error; 61 break; 62 case OSS_SNDCTL_DSP_SPEED: 63 AUDIO_INITINFO(&tmpinfo); 64 error = copyin(SCARG(uap, data), &idat, sizeof idat); 65 if (error) 66 return error; 67 tmpinfo.play.sample_rate = 68 tmpinfo.record.sample_rate = idat; 69 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 70 /* fall into ... */ 71 case OSS_SOUND_PCM_READ_RATE: 72 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 73 if (error) 74 return error; 75 idat = tmpinfo.play.sample_rate; 76 error = copyout(&idat, SCARG(uap, data), sizeof idat); 77 if (error) 78 return error; 79 break; 80 case OSS_SNDCTL_DSP_STEREO: 81 AUDIO_INITINFO(&tmpinfo); 82 error = copyin(SCARG(uap, data), &idat, sizeof idat); 83 if (error) 84 return error; 85 tmpinfo.play.channels = 86 tmpinfo.record.channels = idat ? 2 : 1; 87 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 88 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 89 if (error) 90 return error; 91 idat = tmpinfo.play.channels - 1; 92 error = copyout(&idat, SCARG(uap, data), sizeof idat); 93 if (error) 94 return error; 95 break; 96 case OSS_SNDCTL_DSP_GETBLKSIZE: 97 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 98 if (error) 99 return error; 100 idat = tmpinfo.blocksize; 101 error = copyout(&idat, SCARG(uap, data), sizeof idat); 102 if (error) 103 return error; 104 break; 105 case OSS_SNDCTL_DSP_SETFMT: 106 AUDIO_INITINFO(&tmpinfo); 107 error = copyin(SCARG(uap, data), &idat, sizeof idat); 108 if (error) 109 return error; 110 switch (idat) { 111 case OSS_AFMT_MU_LAW: 112 tmpinfo.play.precision = 113 tmpinfo.record.precision = 8; 114 tmpinfo.play.encoding = 115 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; 116 break; 117 case OSS_AFMT_A_LAW: 118 tmpinfo.play.precision = 119 tmpinfo.record.precision = 8; 120 tmpinfo.play.encoding = 121 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; 122 break; 123 case OSS_AFMT_U8: 124 tmpinfo.play.precision = 125 tmpinfo.record.precision = 8; 126 tmpinfo.play.encoding = 127 tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR; 128 break; 129 case OSS_AFMT_S16_LE: 130 tmpinfo.play.precision = 131 tmpinfo.record.precision = 16; 132 tmpinfo.play.encoding = 133 tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR; 134 break; 135 default: 136 return EINVAL; 137 } 138 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 139 /* fall into ... */ 140 case OSS_SOUND_PCM_READ_BITS: 141 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 142 if (error) 143 return error; 144 switch (tmpinfo.play.encoding) { 145 case AUDIO_ENCODING_ULAW: 146 idat = OSS_AFMT_MU_LAW; 147 break; 148 case AUDIO_ENCODING_ALAW: 149 idat = OSS_AFMT_A_LAW; 150 break; 151 case AUDIO_ENCODING_PCM16: 152 idat = OSS_AFMT_S16_LE; 153 break; 154 case AUDIO_ENCODING_PCM8: 155 idat = OSS_AFMT_U8; 156 break; 157 case AUDIO_ENCODING_ADPCM: 158 idat = OSS_AFMT_IMA_ADPCM; 159 break; 160 } 161 error = copyout(&idat, SCARG(uap, data), sizeof idat); 162 if (error) 163 return error; 164 break; 165 case OSS_SNDCTL_DSP_CHANNELS: 166 AUDIO_INITINFO(&tmpinfo); 167 error = copyin(SCARG(uap, data), &idat, sizeof idat); 168 if (error) 169 return error; 170 tmpinfo.play.channels = 171 tmpinfo.record.channels = idat; 172 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 173 /* fall into ... */ 174 case OSS_SOUND_PCM_READ_CHANNELS: 175 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 176 if (error) 177 return error; 178 idat = tmpinfo.play.channels; 179 error = copyout(&idat, SCARG(uap, data), sizeof idat); 180 if (error) 181 return error; 182 break; 183 case OSS_SOUND_PCM_WRITE_FILTER: 184 case OSS_SOUND_PCM_READ_FILTER: 185 return EINVAL; /* XXX unimplemented */ 186 case OSS_SNDCTL_DSP_SUBDIVIDE: 187 return EINVAL; /* XXX unimplemented */ 188 case OSS_SNDCTL_DSP_SETFRAGMENT: 189 AUDIO_INITINFO(&tmpinfo); 190 error = copyin(SCARG(uap, data), &idat, sizeof idat); 191 if (error) 192 return error; 193 if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) 194 return EINVAL; 195 tmpinfo.blocksize = 1 << (idat & 0xffff); 196 tmpinfo.hiwat = (idat >> 16) & 0xffff; 197 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 198 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 199 if (error) 200 return error; 201 idat = tmpinfo.blocksize; 202 error = copyout(&idat, SCARG(uap, data), sizeof idat); 203 if (error) 204 return error; 205 break; 206 case OSS_SNDCTL_DSP_GETFMTS: 207 idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE; 208 error = copyout(&idat, SCARG(uap, data), sizeof idat); 209 if (error) 210 return error; 211 break; 212 case OSS_SNDCTL_DSP_GETOSPACE: 213 case OSS_SNDCTL_DSP_GETISPACE: 214 case OSS_SNDCTL_DSP_NONBLOCK: 215 return EINVAL; /* XXX unimplemented */ 216 case OSS_SNDCTL_DSP_GETCAPS: 217 idat = 0; /* XXX full duplex info should be set */ 218 error = copyout(&idat, SCARG(uap, data), sizeof idat); 219 if (error) 220 return error; 221 break; 222 case OSS_SNDCTL_DSP_GETTRIGGER: 223 case OSS_SNDCTL_DSP_SETTRIGGER: 224 case OSS_SNDCTL_DSP_GETIPTR: 225 case OSS_SNDCTL_DSP_GETOPTR: 226 case OSS_SNDCTL_DSP_MAPINBUF: 227 case OSS_SNDCTL_DSP_MAPOUTBUF: 228 case OSS_SNDCTL_DSP_SETSYNCRO: 229 case OSS_SNDCTL_DSP_SETDUPLEX: 230 case OSS_SNDCTL_DSP_PROFILE: 231 return EINVAL; /* XXX unimplemented */ 232 default: 233 return EINVAL; 234 } 235 236 return 0; 237 } 238 239 /* If the NetBSD mixer device should have more than 32 devices 240 * some will not be available to Linux */ 241 #define NETBSD_MAXDEVS 32 242 struct audiodevinfo { 243 int done; 244 dev_t dev; 245 int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], 246 rdevmap[NETBSD_MAXDEVS]; 247 u_long devmask, recmask, stereomask; 248 u_long caps, source; 249 }; 250 251 /* 252 * Collect the audio device information to allow faster 253 * emulation of the Linux mixer ioctls. Cache the information 254 * to eliminate the overhead of repeating all the ioctls needed 255 * to collect the information. 256 */ 257 static struct audiodevinfo * 258 getdevinfo(fp, p) 259 struct file *fp; 260 struct proc *p; 261 { 262 mixer_devinfo_t mi; 263 int i; 264 static struct { 265 char *name; 266 int code; 267 } *dp, devs[] = { 268 { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, 269 { AudioNline, OSS_SOUND_MIXER_LINE }, 270 { AudioNcd, OSS_SOUND_MIXER_CD }, 271 { AudioNdac, OSS_SOUND_MIXER_PCM }, 272 { AudioNrecord, OSS_SOUND_MIXER_IMIX }, 273 { AudioNvolume, OSS_SOUND_MIXER_VOLUME }, 274 { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, 275 { AudioNbass, OSS_SOUND_MIXER_BASS }, 276 { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, 277 /* { AudioNheadphone, ?? },*/ 278 { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, 279 { AudioNinput, OSS_SOUND_MIXER_IGAIN }, 280 /* { AudioNmaster, OSS_SOUND_MIXER_MASTER },*/ 281 /* { AudioNstereo, ?? },*/ 282 /* { AudioNmono, ?? },*/ 283 /* { AudioNsource, ?? },*/ 284 { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, 285 /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ 286 /* { AudioNmidi, ?? },*/ 287 /* { AudioNmixerout, ?? },*/ 288 { 0, -1 } 289 }; 290 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) = 291 fp->f_ops->fo_ioctl; 292 struct vnode *vp; 293 struct vattr va; 294 static struct audiodevinfo devcache = { 0 }; 295 struct audiodevinfo *di = &devcache; 296 297 /* Figure out what device it is so we can check if the 298 * cached data is valid. 299 */ 300 vp = (struct vnode *)fp->f_data; 301 if (vp->v_type != VCHR) 302 return 0; 303 if (VOP_GETATTR(vp, &va, p->p_ucred, p)) 304 return 0; 305 if (di->done && di->dev == va.va_rdev) 306 return di; 307 308 di->done = 1; 309 di->dev = va.va_rdev; 310 di->devmask = 0; 311 di->recmask = 0; 312 di->stereomask = 0; 313 di->source = -1; 314 di->caps = 0; 315 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 316 di->devmap[i] = -1; 317 for(i = 0; i < NETBSD_MAXDEVS; i++) 318 di->rdevmap[i] = -1; 319 for(i = 0; i < NETBSD_MAXDEVS; i++) { 320 mi.index = i; 321 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0) 322 break; 323 switch(mi.type) { 324 case AUDIO_MIXER_VALUE: 325 for(dp = devs; dp->name; dp++) 326 if (strcmp(dp->name, mi.label.name) == 0) 327 break; 328 if (dp->code >= 0) { 329 di->devmap[dp->code] = i; 330 di->rdevmap[i] = dp->code; 331 di->devmask |= 1 << dp->code; 332 if (mi.un.v.num_channels == 2) 333 di->stereomask |= 1 << dp->code; 334 } 335 break; 336 case AUDIO_MIXER_ENUM: 337 if (strcmp(mi.label.name, AudioNsource) == 0) { 338 int j; 339 di->source = i; 340 for(j = 0; j < mi.un.e.num_mem; j++) { 341 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 342 } 343 di->caps = OSS_SOUND_CAP_EXCL_INPUT; 344 } 345 break; 346 case AUDIO_MIXER_SET: 347 if (strcmp(mi.label.name, AudioNsource) == 0) { 348 int j; 349 di->source = i; 350 for(j = 0; j < mi.un.s.num_mem; j++) { 351 int k, mask = mi.un.s.member[j].mask; 352 if (mask) { 353 for(k = 0; !(mask & (1<<k)); k++) 354 ; 355 di->recmask |= 1 << di->rdevmap[k]; 356 } 357 } 358 } 359 break; 360 } 361 } 362 return di; 363 } 364 365 int 366 oss_ioctl_mixer(p, uap, retval) 367 register struct proc *p; 368 register struct oss_sys_ioctl_args /* { 369 syscallarg(int) fd; 370 syscallarg(u_long) com; 371 syscallarg(caddr_t) data; 372 } */ *uap; 373 register_t *retval; 374 { 375 struct file *fp; 376 struct filedesc *fdp; 377 u_long com; 378 struct audiodevinfo *di; 379 mixer_ctrl_t mc; 380 int idat; 381 int i; 382 int error; 383 int l, r, n; 384 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 385 386 fdp = p->p_fd; 387 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 388 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 389 return (EBADF); 390 391 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 392 return (EBADF); 393 394 com = SCARG(uap, com); 395 retval[0] = 0; 396 397 di = getdevinfo(fp, p); 398 if (di == 0) 399 return EINVAL; 400 401 ioctlf = fp->f_ops->fo_ioctl; 402 switch (com) { 403 case OSS_SOUND_MIXER_READ_RECSRC: 404 if (di->source == -1) 405 return EINVAL; 406 mc.dev = di->source; 407 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 408 mc.type = AUDIO_MIXER_ENUM; 409 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 410 if (error) 411 return error; 412 idat = 1 << di->rdevmap[mc.un.ord]; 413 } else { 414 int k; 415 unsigned int mask; 416 mc.type = AUDIO_MIXER_SET; 417 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 418 if (error) 419 return error; 420 idat = 0; 421 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 422 idat |= 1 << di->rdevmap[k]; 423 } 424 break; 425 case OSS_SOUND_MIXER_READ_DEVMASK: 426 idat = di->devmask; 427 break; 428 case OSS_SOUND_MIXER_READ_RECMASK: 429 idat = di->recmask; 430 break; 431 case OSS_SOUND_MIXER_READ_STEREODEVS: 432 idat = di->stereomask; 433 break; 434 case OSS_SOUND_MIXER_READ_CAPS: 435 idat = di->caps; 436 break; 437 case OSS_SOUND_MIXER_WRITE_RECSRC: 438 if (di->source == -1) 439 return EINVAL; 440 mc.dev = di->source; 441 error = copyin(SCARG(uap, data), &idat, sizeof idat); 442 if (error) 443 return error; 444 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 445 mc.type = AUDIO_MIXER_ENUM; 446 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 447 if (idat & (1 << i)) 448 break; 449 if (i >= OSS_SOUND_MIXER_NRDEVICES || 450 di->devmap[i] == -1) 451 return EINVAL; 452 mc.un.ord = di->devmap[i]; 453 } else { 454 mc.type = AUDIO_MIXER_SET; 455 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 456 if (idat & (1 << i)) { 457 if (di->devmap[i] == -1) 458 return EINVAL; 459 mc.un.mask |= 1 << di->devmap[i]; 460 } 461 } 462 return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 463 default: 464 if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && 465 com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { 466 n = OSS_GET_DEV(com); 467 if (di->devmap[n] == -1) 468 return EINVAL; 469 mc.dev = di->devmap[n]; 470 mc.type = AUDIO_MIXER_VALUE; 471 doread: 472 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 473 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 474 if (error) 475 return error; 476 if (mc.type != AUDIO_MIXER_VALUE) 477 return EINVAL; 478 if (mc.un.value.num_channels != 2) { 479 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 480 } else { 481 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 482 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 483 } 484 l = l * 100 / 255; 485 r = r * 100 / 255; 486 idat = l | (r << 8); 487 break; 488 } else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 489 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { 490 n = OSS_GET_DEV(com); 491 if (di->devmap[n] == -1) 492 return EINVAL; 493 error = copyin(SCARG(uap, data), &idat, sizeof idat); 494 if (error) 495 return error; 496 l = (idat & 0xff) * 255 / 100; 497 r = (idat >> 8) * 255 / 100; 498 mc.dev = di->devmap[n]; 499 mc.type = AUDIO_MIXER_VALUE; 500 if (di->stereomask & (1<<n)) { 501 mc.un.value.num_channels = 2; 502 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 503 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 504 } else { 505 mc.un.value.num_channels = 1; 506 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 507 } 508 error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 509 if (error) 510 return error; 511 goto doread; 512 } else 513 return EINVAL; 514 } 515 return copyout(&idat, SCARG(uap, data), sizeof idat); 516 } 517 518 /* XXX hook for sequencer emulation */ 519 int 520 oss_ioctl_sequencer(p, uap, retval) 521 register struct proc *p; 522 register struct oss_sys_ioctl_args /* { 523 syscallarg(int) fd; 524 syscallarg(u_long) com; 525 syscallarg(caddr_t) data; 526 } */ *uap; 527 register_t *retval; 528 { 529 register struct file *fp; 530 register struct filedesc *fdp; 531 #if 0 532 u_long com; 533 int idat; 534 int error; 535 #endif 536 537 fdp = p->p_fd; 538 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 539 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 540 return (EBADF); 541 542 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 543 return (EBADF); 544 545 #if 0 546 com = SCARG(uap, com); 547 #endif 548 retval[0] = 0; 549 550 return EINVAL; 551 } 552