int oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(u_long) com; syscallarg(void *) data; } */ file_t *fp; u_long com; struct audiodevinfo *di; mixer_ctrl_t mc; struct oss_mixer_info omi; struct audio_device adev; int idat; int i; int error; int l, r, n, e; int (*ioctlf)(file_t *, u_long, void *); if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) return (EBADF); if ((fp->f_flag & (FREAD | FWRITE)) == 0) { error = EBADF; goto out; } com = SCARG(uap, com); DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com)); retval[0] = 0; di = getdevinfo(fp); if (di == 0) { error = EINVAL; goto out; } ioctlf = fp->f_ops->fo_ioctl; switch (com) { case OSS_GET_VERSION: idat = OSS_SOUND_VERSION; break; case OSS_SOUND_MIXER_INFO: case OSS_SOUND_OLD_MIXER_INFO: error = ioctlf(fp, AUDIO_GETDEV, &adev); if (error) goto out; omi.modify_counter = 1; strncpy(omi.id, adev.name, sizeof omi.id); strncpy(omi.name, adev.name, sizeof omi.name); error = copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com)); goto out; case OSS_SOUND_MIXER_READ_RECSRC: if (di->source == -1) { error = EINVAL; goto out; } mc.dev = di->source; if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { mc.type = AUDIO_MIXER_ENUM; error = ioctlf(fp, AUDIO_MIXER_READ, &mc); if (error) goto out; e = opaque_to_enum(di, NULL, mc.un.ord); if (e >= 0) idat = 1 << di->rdevmap[e]; } else { mc.type = AUDIO_MIXER_SET; error = ioctlf(fp, AUDIO_MIXER_READ, &mc); if (error) goto out; e = opaque_to_enum(di, NULL, mc.un.mask); if (e >= 0) idat = 1 << di->rdevmap[e]; } break; case OSS_SOUND_MIXER_READ_DEVMASK: idat = di->devmask; break; case OSS_SOUND_MIXER_READ_RECMASK: idat = di->recmask; break; case OSS_SOUND_MIXER_READ_STEREODEVS: idat = di->stereomask; break; case OSS_SOUND_MIXER_READ_CAPS: idat = di->caps; break; case OSS_SOUND_MIXER_WRITE_RECSRC: case OSS_SOUND_MIXER_WRITE_R_RECSRC: if (di->source == -1) { error = EINVAL; goto out; } mc.dev = di->source; error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { mc.type = AUDIO_MIXER_ENUM; for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) if (idat & (1 << i)) break; if (i >= OSS_SOUND_MIXER_NRDEVICES || di->devmap[i] == -1) { error = EINVAL; goto out; } mc.un.ord = enum_to_ord(di, di->devmap[i]); } else { mc.type = AUDIO_MIXER_SET; mc.un.mask = 0; for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { if (idat & (1 << i)) { if (di->devmap[i] == -1) { error = EINVAL; goto out; } mc.un.mask |= enum_to_mask(di, di->devmap[i]); } } } error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); goto out; default: if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { n = OSS_GET_DEV(com); if (di->devmap[n] == -1) { error = EINVAL; goto out; } doread: mc.dev = di->devmap[n]; mc.type = AUDIO_MIXER_VALUE; mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; error = ioctlf(fp, AUDIO_MIXER_READ, &mc); if (error) goto out; if (mc.un.value.num_channels != 2) { l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; } else { l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; } idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); DPRINTF(("OSS_MIXER_READ n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", n, di->devmap[n], l, r, idat)); break; } else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com && com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) || (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) { n = OSS_GET_DEV(com); if (di->devmap[n] == -1) { error = EINVAL; goto out; } error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; l = FROM_OSSVOL( idat & 0xff); r = FROM_OSSVOL((idat >> 8) & 0xff); mc.dev = di->devmap[n]; mc.type = AUDIO_MIXER_VALUE; if (di->stereomask & (1<<n)) { mc.un.value.num_channels = 2; mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; } else { mc.un.value.num_channels = 1; mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; } DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n", n, di->devmap[n], l, r, idat)); error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); if (error) goto out; if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { error = 0; goto out; } goto doread; } else {
int mixer_ioctl(int fd, unsigned long com, void *argp) { struct audiodevinfo *di; struct mixer_info *omi; struct audio_device adev; mixer_ctrl_t mc; int idat = 0; int i; int retval; int l, r, n, error, e; di = getdevinfo(fd); if (di == 0) return -1; switch (com) { case OSS_GETVERSION: idat = SOUND_VERSION; break; case SOUND_MIXER_INFO: case SOUND_OLD_MIXER_INFO: error = ioctl(fd, AUDIO_GETDEV, &adev); if (error) return (error); omi = argp; if (com == SOUND_MIXER_INFO) omi->modify_counter = 1; strncpy(omi->id, adev.name, sizeof omi->id); strncpy(omi->name, adev.name, sizeof omi->name); return 0; case SOUND_MIXER_READ_RECSRC: if (di->recsource == -1) return EINVAL; mc.dev = di->recsource; if (di->caps & SOUND_CAP_EXCL_INPUT) { mc.type = AUDIO_MIXER_ENUM; retval = ioctl(fd, AUDIO_MIXER_READ, &mc); if (retval < 0) return retval; e = opaque_to_enum(di, NULL, mc.un.ord); if (e >= 0) idat = 1 << di->rdevmap[e]; } else { mc.type = AUDIO_MIXER_SET; retval = ioctl(fd, AUDIO_MIXER_READ, &mc); if (retval < 0) return retval; e = opaque_to_enum(di, NULL, mc.un.mask); if (e >= 0) idat = 1 << di->rdevmap[e]; } break; case SOUND_MIXER_READ_DEVMASK: idat = di->devmask; break; case SOUND_MIXER_READ_RECMASK: idat = di->recmask; break; case SOUND_MIXER_READ_STEREODEVS: idat = di->stereomask; break; case SOUND_MIXER_READ_CAPS: idat = di->caps; break; case SOUND_MIXER_WRITE_RECSRC: case SOUND_MIXER_WRITE_R_RECSRC: if (di->recsource == -1) return EINVAL; mc.dev = di->recsource; idat = INTARG; if (di->caps & SOUND_CAP_EXCL_INPUT) { mc.type = AUDIO_MIXER_ENUM; for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (idat & (1 << i)) break; if (i >= SOUND_MIXER_NRDEVICES || di->devmap[i] == -1) return EINVAL; mc.un.ord = enum_to_ord(di, di->devmap[i]); } else { mc.type = AUDIO_MIXER_SET; mc.un.mask = 0; for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (idat & (1 << i)) { if (di->devmap[i] == -1) return EINVAL; mc.un.mask |= enum_to_mask(di, di->devmap[i]); } } } return ioctl(fd, AUDIO_MIXER_WRITE, &mc); default: if (MIXER_READ(SOUND_MIXER_FIRST) <= com && com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { n = GET_DEV(com); if (di->devmap[n] == -1) return EINVAL; mc.dev = di->devmap[n]; mc.type = AUDIO_MIXER_VALUE; doread: mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; retval = ioctl(fd, AUDIO_MIXER_READ, &mc); if (retval < 0) return retval; if (mc.type != AUDIO_MIXER_VALUE) return EINVAL; if (mc.un.value.num_channels != 2) { l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; } else { l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; } idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); break; } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { n = GET_DEV(com); if (di->devmap[n] == -1) return EINVAL; idat = INTARG; l = FROM_OSSVOL( idat & 0xff); r = FROM_OSSVOL((idat >> 8) & 0xff); mc.dev = di->devmap[n]; mc.type = AUDIO_MIXER_VALUE; if (di->stereomask & (1<<n)) { mc.un.value.num_channels = 2; mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; } else { mc.un.value.num_channels = 1; mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; } retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); if (retval < 0) return retval; if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) return 0; goto doread; } else {
/* * Collect the audio device information to allow faster * emulation of the Linux mixer ioctls. Cache the information * to eliminate the overhead of repeating all the ioctls needed * to collect the information. */ static struct audiodevinfo * getdevinfo(file_t *fp) { mixer_devinfo_t mi; int i, j, e; static const struct { const char *name; int code; } *dp, devs[] = { { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, { AudioNline, OSS_SOUND_MIXER_LINE }, { AudioNcd, OSS_SOUND_MIXER_CD }, { AudioNdac, OSS_SOUND_MIXER_PCM }, { AudioNaux, OSS_SOUND_MIXER_LINE1 }, { AudioNrecord, OSS_SOUND_MIXER_IMIX }, { AudioNmaster, OSS_SOUND_MIXER_VOLUME }, { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, { AudioNbass, OSS_SOUND_MIXER_BASS }, { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, /* { AudioNheadphone, ?? },*/ { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, { AudioNinput, OSS_SOUND_MIXER_IGAIN }, /* { AudioNmaster, OSS_SOUND_MIXER_SPEAKER },*/ /* { AudioNstereo, ?? },*/ /* { AudioNmono, ?? },*/ { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, /* { AudioNmixerout, ?? },*/ { 0, -1 } }; int (*ioctlf)(file_t *, u_long, void *) = fp->f_ops->fo_ioctl; struct vnode *vp; struct vattr va; static struct audiodevinfo devcache; struct audiodevinfo *di = &devcache; int mlen, dlen; /* * Figure out what device it is so we can check if the * cached data is valid. */ vp = fp->f_data; if (vp->v_type != VCHR) return 0; if (VOP_GETATTR(vp, &va, kauth_cred_get())) return 0; if (di->done && di->dev == va.va_rdev) return di; di->done = 1; di->dev = va.va_rdev; di->devmask = 0; di->recmask = 0; di->stereomask = 0; di->source = ~0; di->caps = 0; for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) di->devmap[i] = -1; for(i = 0; i < NETBSD_MAXDEVS; i++) { di->rdevmap[i] = -1; di->names[i][0] = '\0'; di->enum2opaque[i] = -1; } for(i = 0; i < NETBSD_MAXDEVS; i++) { mi.index = i; if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) break; switch(mi.type) { case AUDIO_MIXER_VALUE: for(dp = devs; dp->name; dp++) { if (strcmp(dp->name, mi.label.name) == 0) break; dlen = strlen(dp->name); mlen = strlen(mi.label.name); if (dlen < mlen && mi.label.name[mlen-dlen-1] == '.' && strcmp(dp->name, mi.label.name + mlen - dlen) == 0) break; } if (dp->code >= 0) { di->devmap[dp->code] = i; di->rdevmap[i] = dp->code; di->devmask |= 1 << dp->code; if (mi.un.v.num_channels == 2) di->stereomask |= 1 << dp->code; strncpy(di->names[i], mi.label.name, sizeof di->names[i]); } break; } } for(i = 0; i < NETBSD_MAXDEVS; i++) { mi.index = i; if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) break; if (strcmp(mi.label.name, AudioNsource) != 0) continue; di->source = i; switch(mi.type) { case AUDIO_MIXER_ENUM: for(j = 0; j < mi.un.e.num_mem; j++) { e = opaque_to_enum(di, &mi.un.e.member[j].label, mi.un.e.member[j].ord); if (e >= 0) di->recmask |= 1 << di->rdevmap[e]; } di->caps = OSS_SOUND_CAP_EXCL_INPUT; break; case AUDIO_MIXER_SET: for(j = 0; j < mi.un.s.num_mem; j++) { e = opaque_to_enum(di, &mi.un.s.member[j].label, mi.un.s.member[j].mask); if (e >= 0) di->recmask |= 1 << di->rdevmap[e]; } break; } } return di; }
/* * Collect the audio device information to allow faster * emulation of the Linux mixer ioctls. Cache the information * to eliminate the overhead of repeating all the ioctls needed * to collect the information. */ static struct audiodevinfo * getdevinfo(int fd) { mixer_devinfo_t mi, cl; int i, j, e; static struct { char *name; int code; } *dp, devs[] = { { AudioNmicrophone, SOUND_MIXER_MIC }, { AudioNline, SOUND_MIXER_LINE }, { AudioNcd, SOUND_MIXER_CD }, { AudioNdac, SOUND_MIXER_PCM }, { AudioNaux, SOUND_MIXER_LINE1 }, { AudioNrecord, SOUND_MIXER_IMIX }, { AudioNmaster, SOUND_MIXER_VOLUME }, { AudioNtreble, SOUND_MIXER_TREBLE }, { AudioNbass, SOUND_MIXER_BASS }, { AudioNspeaker, SOUND_MIXER_SPEAKER }, { AudioNoutput, SOUND_MIXER_OGAIN }, { AudioNinput, SOUND_MIXER_IGAIN }, { AudioNfmsynth, SOUND_MIXER_SYNTH }, { AudioNmidi, SOUND_MIXER_SYNTH }, { 0, -1 } }; static struct audiodevinfo devcache = { 0 }; struct audiodevinfo *di = &devcache; struct stat sb; /* Figure out what device it is so we can check if the * cached data is valid. */ if (fstat(fd, &sb) < 0) return 0; if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino)) return di; di->done = 1; di->dev = sb.st_dev; di->ino = sb.st_ino; di->devmask = 0; di->recmask = 0; di->stereomask = 0; di->recsource = ~0; di->caps = 0; for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) di->devmap[i] = -1; for(i = 0; i < MAX_MIXER_DEVS; i++) { di->rdevmap[i] = -1; di->names[i][0] = '\0'; di->enum2opaque[i] = -1; } for(i = 0; i < MAX_MIXER_DEVS; i++) { mi.index = i; if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) break; switch(mi.type) { case AUDIO_MIXER_VALUE: for(dp = devs; dp->name; dp++) if (strcmp(dp->name, mi.label.name) == 0) break; if (dp->code >= 0) { di->devmap[dp->code] = i; di->rdevmap[i] = dp->code; di->devmask |= 1 << dp->code; if (mi.un.v.num_channels == 2) di->stereomask |= 1 << dp->code; strncpy(di->names[i], mi.label.name, sizeof di->names[i]); } break; } } for(i = 0; i < MAX_MIXER_DEVS; i++) { mi.index = i; if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) break; if (strcmp(mi.label.name, AudioNsource) != 0) continue; cl.index = mi.mixer_class; if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) < 0) break; if ((cl.type != AUDIO_MIXER_CLASS) || (strcmp(cl.label.name, AudioCrecord) != 0)) continue; di->recsource = i; switch(mi.type) { case AUDIO_MIXER_ENUM: for(j = 0; j < mi.un.e.num_mem; j++) { e = opaque_to_enum(di, &mi.un.e.member[j].label, mi.un.e.member[j].ord); if (e >= 0) di->recmask |= 1 << di->rdevmap[e]; } di->caps = SOUND_CAP_EXCL_INPUT; break; case AUDIO_MIXER_SET: for(j = 0; j < mi.un.s.num_mem; j++) { e = opaque_to_enum(di, &mi.un.s.member[j].label, mi.un.s.member[j].mask); if (e >= 0) di->recmask |= 1 << di->rdevmap[e]; } break; } } return di; }