int linux_ioctl_fdio(struct proc *p, struct linux_sys_ioctl_args *uap, register_t *retval) { struct filedesc *fdp; struct file *fp; int error; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); u_long com; struct fd_type fparams; struct linux_floppy_struct lflop; struct linux_floppy_drive_struct ldrive; com = (u_long)SCARG(uap, data); fdp = p->p_fd; if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); FREF(fp); com = SCARG(uap, com); ioctlf = fp->f_ops->fo_ioctl; retval[0] = error = 0; switch (com) { case LINUX_FDMSGON: case LINUX_FDMSGOFF: case LINUX_FDTWADDLE: case LINUX_FDCLRPRM: /* whatever you say */ break; case LINUX_FDPOLLDRVSTAT: /* * Just fill in some innocent defaults. */ memset(&ldrive, 0, sizeof ldrive); ldrive.fd_ref = 1; ldrive.maxblock = 2; ldrive.maxtrack = ldrive.track = 1; ldrive.flags = LINUX_FD_DISK_WRITABLE; error = copyout(&ldrive, SCARG(uap, data), sizeof ldrive); break; case LINUX_FDGETPRM: error = ioctlf(fp, FD_GTYPE, (caddr_t)&fparams, p); if (error != 0) break; lflop.size = fparams.heads * fparams.sectrac * fparams.tracks; lflop.sect = fparams.sectrac; lflop.head = fparams.heads; lflop.track = fparams.tracks; lflop.stretch = fparams.step == 2 ? 1 : 0; lflop.spec1 = fparams.steprate; lflop.gap = fparams.gap1; lflop.fmt_gap = fparams.gap2; lflop.rate = fparams.rate; error = copyout(&lflop, SCARG(uap, data), sizeof lflop); break; case LINUX_FDSETPRM: /* * Should use FDIOCSETFORMAT here, iff its interface * is extended. */ case LINUX_FDDEFPRM: case LINUX_FDFMTBEG: case LINUX_FDFMTTRK: case LINUX_FDFMTEND: case LINUX_FDSETEMSGTRESH: case LINUX_FDFLUSH: case LINUX_FDSETMAXERRS: case LINUX_FDGETMAXERRS: case LINUX_FDGETDRVTYP: case LINUX_FDSETDRVPRM: case LINUX_FDGETDRVPRM: case LINUX_FDGETDRVSTAT: case LINUX_FDRESET: case LINUX_FDGETFDCSTAT: case LINUX_FDWERRORCLR: case LINUX_FDWERRORGET: case LINUX_FDRAWCMD: case LINUX_FDEJECT: default: error = EINVAL; } FRELE(fp, p); return 0; }
/* * We come here in a last attempt to satisfy a Linux ioctl() call */ int linux_machdepioctl(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(u_long) com; syscallarg(void *) data; } */ struct sys_ioctl_args bia; u_long com; int error, error1; #if (NWSDISPLAY > 0) struct vt_mode lvt; struct kbentry kbe; #endif struct linux_hd_geometry hdg; struct linux_hd_big_geometry hdg_big; struct biosdisk_info *bip; file_t *fp; int fd; struct disklabel label, *labp; struct partinfo partp; int (*ioctlf)(struct file *, u_long, void *); u_long start, biostotal, realtotal; u_char heads, sectors; u_int cylinders; struct ioctl_pt pt; fd = SCARG(uap, fd); SCARG(&bia, fd) = fd; SCARG(&bia, data) = SCARG(uap, data); com = SCARG(uap, com); if ((fp = fd_getfile(fd)) == NULL) return (EBADF); switch (com) { #if (NWSDISPLAY > 0) case LINUX_KDGKBMODE: com = KDGKBMODE; break; case LINUX_KDSKBMODE: com = KDSKBMODE; if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW) SCARG(&bia, data) = (void *)K_RAW; break; case LINUX_KIOCSOUND: SCARG(&bia, data) = (void *)(((unsigned long)SCARG(&bia, data)) & 0xffff); /* fall through */ case LINUX_KDMKTONE: com = KDMKTONE; break; case LINUX_KDSETMODE: com = KDSETMODE; break; case LINUX_KDGETMODE: /* KD_* values are equal to the wscons numbers */ com = WSDISPLAYIO_GMODE; break; case LINUX_KDENABIO: com = KDENABIO; break; case LINUX_KDDISABIO: com = KDDISABIO; break; case LINUX_KDGETLED: com = KDGETLED; break; case LINUX_KDSETLED: com = KDSETLED; break; case LINUX_VT_OPENQRY: com = VT_OPENQRY; break; case LINUX_VT_GETMODE: error = fp->f_ops->fo_ioctl(fp, VT_GETMODE, &lvt); if (error != 0) goto out; lvt.relsig = native_to_linux_signo[lvt.relsig]; lvt.acqsig = native_to_linux_signo[lvt.acqsig]; lvt.frsig = native_to_linux_signo[lvt.frsig]; error = copyout(&lvt, SCARG(uap, data), sizeof (lvt)); goto out; case LINUX_VT_SETMODE: error = copyin(SCARG(uap, data), &lvt, sizeof (lvt)); if (error != 0) goto out; lvt.relsig = linux_to_native_signo[lvt.relsig]; lvt.acqsig = linux_to_native_signo[lvt.acqsig]; lvt.frsig = linux_to_native_signo[lvt.frsig]; error = fp->f_ops->fo_ioctl(fp, VT_SETMODE, &lvt); goto out; case LINUX_VT_DISALLOCATE: /* XXX should use WSDISPLAYIO_DELSCREEN */ error = 0; goto out; case LINUX_VT_RELDISP: com = VT_RELDISP; break; case LINUX_VT_ACTIVATE: com = VT_ACTIVATE; break; case LINUX_VT_WAITACTIVE: com = VT_WAITACTIVE; break; case LINUX_VT_GETSTATE: com = VT_GETSTATE; break; case LINUX_KDGKBTYPE: { static const u_int8_t kb101 = KB_101; /* This is what Linux does. */ error = copyout(&kb101, SCARG(uap, data), 1); goto out; } case LINUX_KDGKBENT: /* * The Linux KDGKBENT ioctl is different from the * SYSV original. So we handle it in machdep code. * XXX We should use keyboard mapping information * from wsdisplay, but this would be expensive. */ if ((error = copyin(SCARG(uap, data), &kbe, sizeof(struct kbentry)))) goto out; if (kbe.kb_table >= sizeof(linux_keytabs) / sizeof(u_short *) || kbe.kb_index >= NR_KEYS) { error = EINVAL; goto out; } kbe.kb_value = linux_keytabs[kbe.kb_table][kbe.kb_index]; error = copyout(&kbe, SCARG(uap, data), sizeof(struct kbentry)); goto out; #endif case LINUX_HDIO_GETGEO: case LINUX_HDIO_GETGEO_BIG: /* * Try to mimic Linux behaviour: return the BIOS geometry * if possible (extending its # of cylinders if it's beyond * the 1023 limit), fall back to the MI geometry (i.e. * the real geometry) if not found, by returning an * error. See common/linux_hdio.c */ bip = fd2biosinfo(curproc, fp); ioctlf = fp->f_ops->fo_ioctl; error = ioctlf(fp, DIOCGDEFLABEL, (void *)&label); error1 = ioctlf(fp, DIOCGPART, (void *)&partp); if (error != 0 && error1 != 0) { error = error1; goto out; } labp = error != 0 ? &label : partp.disklab; start = error1 != 0 ? partp.part->p_offset : 0; if (bip != NULL && bip->bi_head != 0 && bip->bi_sec != 0 && bip->bi_cyl != 0) { heads = bip->bi_head; sectors = bip->bi_sec; cylinders = bip->bi_cyl; biostotal = heads * sectors * cylinders; realtotal = labp->d_ntracks * labp->d_nsectors * labp->d_ncylinders; if (realtotal > biostotal) cylinders = realtotal / (heads * sectors); } else { heads = labp->d_ntracks; cylinders = labp->d_ncylinders; sectors = labp->d_nsectors; } if (com == LINUX_HDIO_GETGEO) { hdg.start = start; hdg.heads = heads; hdg.cylinders = cylinders; hdg.sectors = sectors; error = copyout(&hdg, SCARG(uap, data), sizeof hdg); goto out; } else { hdg_big.start = start; hdg_big.heads = heads; hdg_big.cylinders = cylinders; hdg_big.sectors = sectors; error = copyout(&hdg_big, SCARG(uap, data), sizeof hdg_big); goto out; } default: /* * Unknown to us. If it's on a device, just pass it through * using PTIOCLINUX, the device itself might be able to * make some sense of it. * XXX hack: if the function returns EJUSTRETURN, * it has stuffed a sysctl return value in pt.data. */ ioctlf = fp->f_ops->fo_ioctl; pt.com = SCARG(uap, com); pt.data = SCARG(uap, data); error = ioctlf(fp, PTIOCLINUX, &pt); if (error == EJUSTRETURN) { retval[0] = (register_t)pt.data; error = 0; } if (error == ENOTTY) { DPRINTF(("linux_machdepioctl: invalid ioctl %08lx\n", com)); } goto out; } SCARG(&bia, com) = com; error = sys_ioctl(curlwp, &bia, retval); out: fd_putfile(fd); return error; }
/* * We come here in a last attempt to satisfy a Linux ioctl() call */ int linux_machdepioctl(struct proc *p, void *v, register_t *retval) { struct linux_sys_ioctl_args /* { syscallarg(int) fd; syscallarg(u_long) com; syscallarg(caddr_t) data; } */ *uap = v; struct sys_ioctl_args bia; u_long com; int error; #if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL)) struct vt_mode lvt; caddr_t bvtp, sg; #endif struct filedesc *fdp; struct file *fp; int fd; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); struct ioctl_pt pt; fd = SCARG(uap, fd); SCARG(&bia, fd) = SCARG(uap, fd); SCARG(&bia, data) = SCARG(uap, data); com = SCARG(uap, com); fdp = p->p_fd; if ((fp = fd_getfile(fdp, fd)) == NULL) return (EBADF); switch (com) { #if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL)) case LINUX_KDGKBMODE: com = KDGKBMODE; break; case LINUX_KDSKBMODE: com = KDSKBMODE; if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW) SCARG(&bia, data) = (caddr_t)K_RAW; break; case LINUX_KIOCSOUND: SCARG(&bia, data) = (caddr_t)(((unsigned long)SCARG(&bia, data)) & 0xffff); /* FALLTHROUGH */ case LINUX_KDMKTONE: com = KDMKTONE; break; case LINUX_KDSETMODE: com = KDSETMODE; break; case LINUX_KDGETMODE: #if NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL) com = WSDISPLAYIO_GMODE; #else com = KDGETMODE; #endif break; case LINUX_KDENABIO: com = KDENABIO; break; case LINUX_KDDISABIO: com = KDDISABIO; break; case LINUX_KDGETLED: com = KDGETLED; break; case LINUX_KDSETLED: com = KDSETLED; break; case LINUX_VT_OPENQRY: com = VT_OPENQRY; break; case LINUX_VT_GETMODE: { int sig; SCARG(&bia, com) = VT_GETMODE; if ((error = sys_ioctl(p, &bia, retval))) return error; if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt, sizeof (struct vt_mode)))) return error; /* We need to bounds check here in case there is a race with another thread */ if ((error = bsd_to_linux_signal(lvt.relsig, &sig))) return error; lvt.relsig = sig; if ((error = bsd_to_linux_signal(lvt.acqsig, &sig))) return error; lvt.acqsig = sig; if ((error = bsd_to_linux_signal(lvt.frsig, &sig))) return error; lvt.frsig = sig; return copyout((caddr_t)&lvt, SCARG(uap, data), sizeof (struct vt_mode)); } case LINUX_VT_SETMODE: { int sig; com = VT_SETMODE; if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt, sizeof (struct vt_mode)))) return error; if ((error = linux_to_bsd_signal(lvt.relsig, &sig))) return error; lvt.relsig = sig; if ((error = linux_to_bsd_signal(lvt.acqsig, &sig))) return error; lvt.acqsig = sig; if ((error = linux_to_bsd_signal(lvt.frsig, &sig))) return error; lvt.frsig = sig; sg = stackgap_init(p->p_emul); bvtp = stackgap_alloc(&sg, sizeof (struct vt_mode)); if ((error = copyout(&lvt, bvtp, sizeof (struct vt_mode)))) return error; SCARG(&bia, data) = bvtp; break; } case LINUX_VT_DISALLOCATE: /* XXX should use WSDISPLAYIO_DELSCREEN */ return 0; case LINUX_VT_RELDISP: com = VT_RELDISP; break; case LINUX_VT_ACTIVATE: com = VT_ACTIVATE; break; case LINUX_VT_WAITACTIVE: com = VT_WAITACTIVE; break; case LINUX_VT_GETSTATE: com = VT_GETSTATE; break; case LINUX_KDGKBTYPE: { char tmp = KB_101; /* This is what Linux does */ return copyout(&tmp, SCARG(uap, data), sizeof(char)); } #endif default: /* * Unknown to us. If it's on a device, just pass it through * using PTIOCLINUX, the device itself might be able to * make some sense of it. * XXX hack: if the function returns EJUSTRETURN, * it has stuffed a sysctl return value in pt.data. */ FREF(fp); ioctlf = fp->f_ops->fo_ioctl; pt.com = SCARG(uap, com); pt.data = SCARG(uap, data); error = ioctlf(fp, PTIOCLINUX, (caddr_t)&pt, p); FRELE(fp); if (error == EJUSTRETURN) { retval[0] = (register_t)pt.data; error = 0; } if (error == ENOTTY) printf("linux_machdepioctl: invalid ioctl %08lx\n", com); return (error); } SCARG(&bia, com) = com; return sys_ioctl(p, &bia, retval); }
int linux_ioctl_hdio(struct proc *p, struct linux_sys_ioctl_args *uap, register_t *retval) { u_long com; int error, error1; caddr_t sg; struct filedesc *fdp; struct file *fp; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); struct ataparams *atap, ata; struct atareq req; struct disklabel label, *labp; struct partinfo partp; struct linux_hd_geometry hdg; struct linux_hd_big_geometry hdg_big; fdp = p->p_fd; if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); FREF(fp); com = SCARG(uap, com); ioctlf = fp->f_ops->fo_ioctl; retval[0] = error = 0; com = SCARG(uap, com); switch (com) { case LINUX_HDIO_OBSOLETE_IDENTITY: case LINUX_HDIO_GET_IDENTITY: sg = stackgap_init(p->p_emul); atap = stackgap_alloc(&sg, DEV_BSIZE); if (atap == NULL) { error = ENOMEM; break; } req.flags = ATACMD_READ; req.command = WDCC_IDENTIFY; req.databuf = (caddr_t)atap; req.datalen = DEV_BSIZE; req.timeout = 1000; error = ioctlf(fp, ATAIOCCOMMAND, (caddr_t)&req, p); if (error != 0) break; if (req.retsts != ATACMD_OK) { error = EIO; break; } error = copyin(atap, &ata, sizeof ata); if (error != 0) break; /* * 142 is the size of the old structure used by Linux, * which doesn't seem to be defined anywhere anymore. */ error = copyout(&ata, SCARG(uap, data), com == LINUX_HDIO_GET_IDENTITY ? sizeof ata : 142); break; case LINUX_HDIO_GETGEO: error = linux_machdepioctl(p, uap, retval); if (error == 0) break; error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p); error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p); if (error != 0 && error1 != 0) { error = error1; break; } labp = error != 0 ? &label : partp.disklab; hdg.start = error1 != 0 ? partp.part->p_offset : 0; hdg.heads = labp->d_ntracks; hdg.cylinders = labp->d_ncylinders; hdg.sectors = labp->d_nsectors; error = copyout(&hdg, SCARG(uap, data), sizeof hdg); break; case LINUX_HDIO_GETGEO_BIG: error = linux_machdepioctl(p, uap, retval); if (error == 0) break; case LINUX_HDIO_GETGEO_BIG_RAW: error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p); error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p); if (error != 0 && error1 != 0) { error = error1; break; } labp = error != 0 ? &label : partp.disklab; hdg_big.start = error1 != 0 ? partp.part->p_offset : 0; hdg_big.heads = labp->d_ntracks; hdg_big.cylinders = labp->d_ncylinders; hdg_big.sectors = labp->d_nsectors; error = copyout(&hdg_big, SCARG(uap, data), sizeof hdg_big); break; case LINUX_HDIO_GET_UNMASKINTR: case LINUX_HDIO_GET_MULTCOUNT: case LINUX_HDIO_GET_KEEPSETTINGS: case LINUX_HDIO_GET_32BIT: case LINUX_HDIO_GET_NOWERR: case LINUX_HDIO_GET_DMA: case LINUX_HDIO_GET_NICE: case LINUX_HDIO_DRIVE_RESET: case LINUX_HDIO_TRISTATE_HWIF: case LINUX_HDIO_DRIVE_TASK: case LINUX_HDIO_DRIVE_CMD: case LINUX_HDIO_SET_MULTCOUNT: case LINUX_HDIO_SET_UNMASKINTR: case LINUX_HDIO_SET_KEEPSETTINGS: case LINUX_HDIO_SET_32BIT: case LINUX_HDIO_SET_NOWERR: case LINUX_HDIO_SET_DMA: case LINUX_HDIO_SET_PIO_MODE: case LINUX_HDIO_SCAN_HWIF: case LINUX_HDIO_SET_NICE: case LINUX_HDIO_UNREGISTER_HWIF: error = EINVAL; } FRELE(fp); return error; }
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 oss_ioctl_audio(struct lwp *l, 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 audio_info tmpinfo; struct audio_offset tmpoffs; struct oss_audio_buf_info bufinfo; struct oss_count_info cntinfo; struct audio_encoding tmpenc; u_int u; int idat, idata; int error = 0; 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_audio: com=%08lx\n", com)); retval[0] = 0; ioctlf = fp->f_ops->fo_ioctl; switch (com) { case OSS_SNDCTL_DSP_RESET: error = ioctlf(fp, AUDIO_FLUSH, NULL); if (error) goto out; break; case OSS_SNDCTL_DSP_SYNC: error = ioctlf(fp, AUDIO_DRAIN, NULL); if (error) goto out; break; case OSS_SNDCTL_DSP_POST: /* This call is merely advisory, and may be a nop. */ break; case OSS_SNDCTL_DSP_SPEED: AUDIO_INITINFO(&tmpinfo); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; tmpinfo.play.sample_rate = tmpinfo.record.sample_rate = idat; error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n", idat, error)); if (error) goto out; /* fall into ... */ case OSS_SOUND_PCM_READ_RATE: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; idat = tmpinfo.play.sample_rate; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_STEREO: AUDIO_INITINFO(&tmpinfo); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; tmpinfo.play.channels = tmpinfo.record.channels = idat ? 2 : 1; (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; idat = tmpinfo.play.channels - 1; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_GETBLKSIZE: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; setblocksize(fp, &tmpinfo); idat = tmpinfo.blocksize; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_SETFMT: AUDIO_INITINFO(&tmpinfo); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; switch (idat) { case OSS_AFMT_MU_LAW: tmpinfo.play.precision = tmpinfo.record.precision = 8; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; break; case OSS_AFMT_A_LAW: tmpinfo.play.precision = tmpinfo.record.precision = 8; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; break; case OSS_AFMT_U8: tmpinfo.play.precision = tmpinfo.record.precision = 8; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; break; case OSS_AFMT_S8: tmpinfo.play.precision = tmpinfo.record.precision = 8; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; break; case OSS_AFMT_S16_LE: tmpinfo.play.precision = tmpinfo.record.precision = 16; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; break; case OSS_AFMT_S16_BE: tmpinfo.play.precision = tmpinfo.record.precision = 16; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; break; case OSS_AFMT_U16_LE: tmpinfo.play.precision = tmpinfo.record.precision = 16; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; break; case OSS_AFMT_U16_BE: tmpinfo.play.precision = tmpinfo.record.precision = 16; tmpinfo.play.encoding = tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; break; default: error = EINVAL; goto out; } (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); /* fall into ... */ case OSS_SOUND_PCM_READ_BITS: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; switch (tmpinfo.play.encoding) { case AUDIO_ENCODING_ULAW: idat = OSS_AFMT_MU_LAW; break; case AUDIO_ENCODING_ALAW: idat = OSS_AFMT_A_LAW; break; case AUDIO_ENCODING_SLINEAR_LE: if (tmpinfo.play.precision == 16) idat = OSS_AFMT_S16_LE; else idat = OSS_AFMT_S8; break; case AUDIO_ENCODING_SLINEAR_BE: if (tmpinfo.play.precision == 16) idat = OSS_AFMT_S16_BE; else idat = OSS_AFMT_S8; break; case AUDIO_ENCODING_ULINEAR_LE: if (tmpinfo.play.precision == 16) idat = OSS_AFMT_U16_LE; else idat = OSS_AFMT_U8; break; case AUDIO_ENCODING_ULINEAR_BE: if (tmpinfo.play.precision == 16) idat = OSS_AFMT_U16_BE; else idat = OSS_AFMT_U8; break; case AUDIO_ENCODING_ADPCM: idat = OSS_AFMT_IMA_ADPCM; break; } error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_CHANNELS: AUDIO_INITINFO(&tmpinfo); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; tmpinfo.play.channels = tmpinfo.record.channels = idat; (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); /* fall into ... */ case OSS_SOUND_PCM_READ_CHANNELS: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; idat = tmpinfo.play.channels; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SOUND_PCM_WRITE_FILTER: case OSS_SOUND_PCM_READ_FILTER: error = EINVAL; /* XXX unimplemented */ goto out; case OSS_SNDCTL_DSP_SUBDIVIDE: error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); setblocksize(fp, &tmpinfo); if (error) goto out; if (idat == 0) idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; idat = (tmpinfo.play.buffer_size / idat) & -4; AUDIO_INITINFO(&tmpinfo); tmpinfo.blocksize = idat; error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); if (error) goto out; idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_SETFRAGMENT: AUDIO_INITINFO(&tmpinfo); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) { error = EINVAL; goto out; } tmpinfo.blocksize = 1 << (idat & 0xffff); tmpinfo.hiwat = (idat >> 16) & 0x7fff; DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n", tmpinfo.blocksize, tmpinfo.hiwat)); if (tmpinfo.hiwat == 0) /* 0 means set to max */ tmpinfo.hiwat = 65536; (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; u = tmpinfo.blocksize; for(idat = 0; u > 1; idat++, u >>= 1) ; idat |= (tmpinfo.hiwat & 0x7fff) << 16; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_GETFMTS: for(idat = 0, tmpenc.index = 0; ioctlf(fp, AUDIO_GETENC, &tmpenc) == 0; tmpenc.index++) { switch(tmpenc.encoding) { case AUDIO_ENCODING_ULAW: idat |= OSS_AFMT_MU_LAW; break; case AUDIO_ENCODING_ALAW: idat |= OSS_AFMT_A_LAW; break; case AUDIO_ENCODING_SLINEAR: idat |= OSS_AFMT_S8; break; case AUDIO_ENCODING_SLINEAR_LE: if (tmpenc.precision == 16) idat |= OSS_AFMT_S16_LE; else idat |= OSS_AFMT_S8; break; case AUDIO_ENCODING_SLINEAR_BE: if (tmpenc.precision == 16) idat |= OSS_AFMT_S16_BE; else idat |= OSS_AFMT_S8; break; case AUDIO_ENCODING_ULINEAR: idat |= OSS_AFMT_U8; break; case AUDIO_ENCODING_ULINEAR_LE: if (tmpenc.precision == 16) idat |= OSS_AFMT_U16_LE; else idat |= OSS_AFMT_U8; break; case AUDIO_ENCODING_ULINEAR_BE: if (tmpenc.precision == 16) idat |= OSS_AFMT_U16_BE; else idat |= OSS_AFMT_U8; break; case AUDIO_ENCODING_ADPCM: idat |= OSS_AFMT_IMA_ADPCM; break; default: break; } } DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat)); error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_GETOSPACE: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; setblocksize(fp, &tmpinfo); bufinfo.fragsize = tmpinfo.blocksize; bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek + tmpinfo.blocksize - 1) / tmpinfo.blocksize; bufinfo.fragstotal = tmpinfo.hiwat; bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek; error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); if (error) goto out; break; case OSS_SNDCTL_DSP_GETISPACE: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; setblocksize(fp, &tmpinfo); bufinfo.fragsize = tmpinfo.blocksize; bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.record.seek + tmpinfo.blocksize - 1) / tmpinfo.blocksize; bufinfo.fragstotal = tmpinfo.hiwat; bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek; DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n", bufinfo.fragsize, bufinfo.fragments, bufinfo.fragstotal, bufinfo.bytes)); error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); if (error) goto out; break; case OSS_SNDCTL_DSP_NONBLOCK: idat = 1; error = ioctlf(fp, FIONBIO, &idat); if (error) goto out; break; case OSS_SNDCTL_DSP_GETCAPS: error = ioctlf(fp, AUDIO_GETPROPS, &idata); if (error) goto out; idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */ if (idata & AUDIO_PROP_FULLDUPLEX) idat |= OSS_DSP_CAP_DUPLEX; if (idata & AUDIO_PROP_MMAP) idat |= OSS_DSP_CAP_MMAP; DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat)); error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; #if 0 case OSS_SNDCTL_DSP_GETTRIGGER: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) | (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT); error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_SETTRIGGER: (void) ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo, p); error = copyin(SCARG(uap, data), &idat, sizeof idat); if (error) goto out; tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0; tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0; (void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo); error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; #else case OSS_SNDCTL_DSP_GETTRIGGER: case OSS_SNDCTL_DSP_SETTRIGGER: /* XXX Do nothing for now. */ idat = OSS_PCM_ENABLE_OUTPUT; error = copyout(&idat, SCARG(uap, data), sizeof idat); goto out; #endif case OSS_SNDCTL_DSP_GETIPTR: error = ioctlf(fp, AUDIO_GETIOFFS, &tmpoffs); if (error) goto out; cntinfo.bytes = tmpoffs.samples; cntinfo.blocks = tmpoffs.deltablks; cntinfo.ptr = tmpoffs.offset; error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); if (error) goto out; break; case OSS_SNDCTL_DSP_GETOPTR: error = ioctlf(fp, AUDIO_GETOOFFS, &tmpoffs); if (error) goto out; cntinfo.bytes = tmpoffs.samples; cntinfo.blocks = tmpoffs.deltablks; cntinfo.ptr = tmpoffs.offset; error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); if (error) goto out; break; case OSS_SNDCTL_DSP_SETDUPLEX: idat = 1; error = ioctlf(fp, AUDIO_SETFD, &idat); goto out; case OSS_SNDCTL_DSP_MAPINBUF: case OSS_SNDCTL_DSP_MAPOUTBUF: case OSS_SNDCTL_DSP_SETSYNCRO: error = EINVAL; goto out; case OSS_SNDCTL_DSP_GETODELAY: error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); if (error) goto out; idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; error = copyout(&idat, SCARG(uap, data), sizeof idat); if (error) goto out; break; case OSS_SNDCTL_DSP_PROFILE: /* This gives just a hint to the driver, * implementing it as a NOP is ok */ break; default: error = EINVAL; goto out; } out: fd_putfile(SCARG(uap, fd)); return error; }
/* * 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; }