/* * Most ioctl command are just converted to their NetBSD values, * and passed on. The ones that take structure pointers and (flag) * values need some massaging. */ int linux_sys_ioctl(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(u_long) com; syscallarg(void *) data; } */ int error; switch (LINUX_IOCGROUP(SCARG(uap, com))) { case 'M': switch(SCARG(uap, com)) { case LINUX_MEGARAID_CMD: case LINUX_MEGARAID_GET_AEN: { struct sys_ioctl_args ua; u_long com = 0; if (SCARG(uap, com) & IOC_IN) com |= IOC_OUT; if (SCARG(uap, com) & IOC_OUT) com |= IOC_IN; SCARG(&ua, fd) = SCARG(uap, fd); SCARG(&ua, com) = SCARG(uap, com); SCARG(&ua, com) &= ~IOC_DIRMASK; SCARG(&ua, com) |= com; SCARG(&ua, data) = SCARG(uap, data); error = sys_ioctl(l, (const void *)&ua, retval); break; } default: error = oss_ioctl_mixer(l, LINUX_TO_OSS(uap), retval); break; } break; case 'Q': error = oss_ioctl_sequencer(l, LINUX_TO_OSS(uap), retval); break; case 'P': error = oss_ioctl_audio(l, LINUX_TO_OSS(uap), retval); break; case 'V': /* video4linux2 */ case 'd': /* drm */ { struct sys_ioctl_args ua; u_long com = 0; if (SCARG(uap, com) & IOC_IN) com |= IOC_OUT; if (SCARG(uap, com) & IOC_OUT) com |= IOC_IN; SCARG(&ua, fd) = SCARG(uap, fd); SCARG(&ua, com) = SCARG(uap, com); SCARG(&ua, com) &= ~IOC_DIRMASK; SCARG(&ua, com) |= com; SCARG(&ua, data) = SCARG(uap, data); error = sys_ioctl(l, (const void *)&ua, retval); break; } case 'r': /* VFAT ioctls; not yet supported */ error = ENOSYS; break; case 'S': error = linux_ioctl_cdrom(l, uap, retval); break; case 't': case 'f': error = linux_ioctl_termios(l, uap, retval); break; case 'm': error = linux_ioctl_mtio(l, uap, retval); break; case 'T': { #if NSEQUENCER > 0 /* XXX XAX 2x check this. */ /* * Both termios and the MIDI sequencer use 'T' to identify * the ioctl, so we have to differentiate them in another * way. We do it by indexing in the cdevsw with the major * device number and check if that is the sequencer entry. */ bool is_sequencer = false; struct file *fp; struct vnode *vp; struct vattr va; extern const struct cdevsw sequencer_cdevsw; if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) return EBADF; if (fp->f_type == DTYPE_VNODE && (vp = (struct vnode *)fp->f_data) != NULL && vp->v_type == VCHR) { vn_lock(vp, LK_SHARED | LK_RETRY); error = VOP_GETATTR(vp, &va, l->l_cred); VOP_UNLOCK(vp); if (error == 0 && cdevsw_lookup(va.va_rdev) == &sequencer_cdevsw) is_sequencer = true; } if (is_sequencer) { error = oss_ioctl_sequencer(l, (const void *)LINUX_TO_OSS(uap), retval); } else { error = linux_ioctl_termios(l, uap, retval); } fd_putfile(SCARG(uap, fd)); #else error = linux_ioctl_termios(l, uap, retval); #endif } break; case '"': error = linux_ioctl_sg(l, uap, retval); break; case 0x89: error = linux_ioctl_socket(l, uap, retval); break; case 0x03: error = linux_ioctl_hdio(l, uap, retval); break; case 0x02: error = linux_ioctl_fdio(l, uap, retval); break; case 0x12: error = linux_ioctl_blkio(l, uap, retval); break; default: error = linux_machdepioctl(l, uap, retval); break; } if (error == EPASSTHROUGH) { /* * linux returns EINVAL or ENOTTY for not supported ioctls. */ error = EINVAL; } return error; }
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; }