/*
 * 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;
}
示例#2
0
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;
}