int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mode,
		   unsigned int cmd, void __user *arg)
{
	int err;

	if (!q || blk_get_queue(q))
		return -ENXIO;

	switch (cmd) {
		/*
		 * new sgv3 interface
		 */
		case SG_GET_VERSION_NUM:
			err = sg_get_version(arg);
			break;
		case SCSI_IOCTL_GET_IDLUN:
			err = scsi_get_idlun(q, arg);
			break;
		case SCSI_IOCTL_GET_BUS_NUMBER:
			err = scsi_get_bus(q, arg);
			break;
		case SG_SET_TIMEOUT:
			err = sg_set_timeout(q, arg);
			break;
		case SG_GET_TIMEOUT:
			err = sg_get_timeout(q);
			break;
		case SG_GET_RESERVED_SIZE:
			err = sg_get_reserved_size(q, arg);
			break;
		case SG_SET_RESERVED_SIZE:
			err = sg_set_reserved_size(q, arg);
			break;
		case SG_EMULATED_HOST:
			err = sg_emulated_host(q, arg);
			break;
		case SG_IO: {
			struct sg_io_hdr hdr;

			err = -EFAULT;
			if (copy_from_user(&hdr, arg, sizeof(hdr)))
				break;
			err = sg_io(q, bd_disk, &hdr, mode);
			if (err == -EFAULT)
				break;

			if (copy_to_user(arg, &hdr, sizeof(hdr)))
				err = -EFAULT;
			break;
		}
		case CDROM_SEND_PACKET: {
			struct cdrom_generic_command cgc;
			struct sg_io_hdr hdr;

			err = -EFAULT;
			if (copy_from_user(&cgc, arg, sizeof(cgc)))
				break;
			cgc.timeout = clock_t_to_jiffies(cgc.timeout);
			memset(&hdr, 0, sizeof(hdr));
			hdr.interface_id = 'S';
			hdr.cmd_len = sizeof(cgc.cmd);
			hdr.dxfer_len = cgc.buflen;
			err = 0;
			switch (cgc.data_direction) {
				case CGC_DATA_UNKNOWN:
					hdr.dxfer_direction = SG_DXFER_UNKNOWN;
					break;
				case CGC_DATA_WRITE:
					hdr.dxfer_direction = SG_DXFER_TO_DEV;
					break;
				case CGC_DATA_READ:
					hdr.dxfer_direction = SG_DXFER_FROM_DEV;
					break;
				case CGC_DATA_NONE:
					hdr.dxfer_direction = SG_DXFER_NONE;
					break;
				default:
					err = -EINVAL;
			}
			if (err)
				break;

			hdr.dxferp = cgc.buffer;
			hdr.sbp = cgc.sense;
			if (hdr.sbp)
				hdr.mx_sb_len = sizeof(struct request_sense);
			hdr.timeout = jiffies_to_msecs(cgc.timeout);
			hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd;
			hdr.cmd_len = sizeof(cgc.cmd);

			err = sg_io(q, bd_disk, &hdr, mode);
			if (err == -EFAULT)
				break;

			if (hdr.status)
				err = -EIO;

			cgc.stat = err;
			cgc.buflen = hdr.resid;
			if (copy_to_user(arg, &cgc, sizeof(cgc)))
				err = -EFAULT;

			break;
		}

		/*
		 * old junk scsi send command ioctl
		 */
		case SCSI_IOCTL_SEND_COMMAND:
			printk(KERN_WARNING "program %s is using a deprecated SCSI ioctl, please convert it to SG_IO\n", current->comm);
			err = -EINVAL;
			if (!arg)
				break;

			err = sg_scsi_ioctl(q, bd_disk, mode, arg);
			break;
		case CDROMCLOSETRAY:
			err = blk_send_start_stop(q, bd_disk, 0x03);
			break;
		case CDROMEJECT:
			err = blk_send_start_stop(q, bd_disk, 0x02);
			break;
		default:
			err = -ENOTTY;
	}

	blk_put_queue(q);
	return err;
}
Ejemplo n.º 2
0
int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd, void __user *arg)
{
    request_queue_t *q;
    struct request *rq;
    int close = 0, err;

    q = bd_disk->queue;
    if (!q)
        return -ENXIO;

    if (blk_get_queue(q))
        return -ENXIO;

    switch (cmd) {
    /*
     * new sgv3 interface
     */
    case SG_GET_VERSION_NUM:
        err = sg_get_version(arg);
        break;
    case SCSI_IOCTL_GET_IDLUN:
        err = scsi_get_idlun(q, arg);
        break;
    case SCSI_IOCTL_GET_BUS_NUMBER:
        err = scsi_get_bus(q, arg);
        break;
    case SG_SET_TIMEOUT:
        err = sg_set_timeout(q, arg);
        break;
    case SG_GET_TIMEOUT:
        err = sg_get_timeout(q);
        break;
    case SG_GET_RESERVED_SIZE:
        err = sg_get_reserved_size(q, arg);
        break;
    case SG_SET_RESERVED_SIZE:
        err = sg_set_reserved_size(q, arg);
        break;
    case SG_EMULATED_HOST:
        err = sg_emulated_host(q, arg);
        break;
    case SG_IO: {
        struct sg_io_hdr hdr;

        err = -EFAULT;
        if (copy_from_user(&hdr, arg, sizeof(hdr)))
            break;
        err = sg_io(file, q, bd_disk, &hdr);
        if (err == -EFAULT)
            break;

        if (copy_to_user(arg, &hdr, sizeof(hdr)))
            err = -EFAULT;
        break;
    }
    case CDROM_SEND_PACKET: {
        struct cdrom_generic_command cgc;
        struct sg_io_hdr hdr;

        err = -EFAULT;
        if (copy_from_user(&cgc, arg, sizeof(cgc)))
            break;
        cgc.timeout = clock_t_to_jiffies(cgc.timeout);
        memset(&hdr, 0, sizeof(hdr));
        hdr.interface_id = 'S';
        hdr.cmd_len = sizeof(cgc.cmd);
        hdr.dxfer_len = cgc.buflen;
        err = 0;
        switch (cgc.data_direction) {
        case CGC_DATA_UNKNOWN:
            hdr.dxfer_direction = SG_DXFER_UNKNOWN;
            break;
        case CGC_DATA_WRITE:
            hdr.dxfer_direction = SG_DXFER_TO_DEV;
            break;
        case CGC_DATA_READ:
            hdr.dxfer_direction = SG_DXFER_FROM_DEV;
            break;
        case CGC_DATA_NONE:
            hdr.dxfer_direction = SG_DXFER_NONE;
            break;
        default:
            err = -EINVAL;
        }
        if (err)
            break;

        hdr.dxferp = cgc.buffer;
        hdr.sbp = cgc.sense;
        if (hdr.sbp)
            hdr.mx_sb_len = sizeof(struct request_sense);
        hdr.timeout = cgc.timeout;
        hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd;
        hdr.cmd_len = sizeof(cgc.cmd);

        err = sg_io(file, q, bd_disk, &hdr);
        if (err == -EFAULT)
            break;

        if (hdr.status)
            err = -EIO;

        cgc.stat = err;
        cgc.buflen = hdr.resid;
        if (copy_to_user(arg, &cgc, sizeof(cgc)))
            err = -EFAULT;

        break;
    }

    /*
     * old junk scsi send command ioctl
     */
    case SCSI_IOCTL_SEND_COMMAND:
        err = -EINVAL;
        if (!arg)
            break;

        err = sg_scsi_ioctl(file, q, bd_disk, arg);
        break;
    case CDROMCLOSETRAY:
        close = 1;
    case CDROMEJECT:
        rq = blk_get_request(q, WRITE, __GFP_WAIT);
        rq->flags |= REQ_BLOCK_PC;
        rq->data = NULL;
        rq->data_len = 0;
        rq->timeout = BLK_DEFAULT_TIMEOUT;
        memset(rq->cmd, 0, sizeof(rq->cmd));
        rq->cmd[0] = GPCMD_START_STOP_UNIT;
        rq->cmd[4] = 0x02 + (close != 0);
        rq->cmd_len = 6;
        err = blk_execute_rq(q, bd_disk, rq);
        blk_put_request(rq);
        break;
    default:
        err = -ENOTTY;
    }

    blk_put_queue(q);
    return err;
}