+/*
+ * This interface is deprecated - users should use the scsi generic (sg)
+ * interface instead, as this is a more flexible approach to performing
+ * generic SCSI commands on a device.
+ *
+ * The structure that we are passed should look like:
+ *
+ * struct sdata {
+ * unsigned int inlen; [i] Length of data to be written to device
+ * unsigned int outlen; [i] Length of data to be read from device
+ * unsigned char cmd[x]; [i] SCSI command (6 <= x <= 12).
+ * [o] Data read from device starts here.
+ * [o] On error, sense buffer starts here.
+ * unsigned char wdata[y]; [i] Data written to device starts here.
+ * };
+ * Notes:
+ * - The SCSI command length is determined by examining the 1st byte
+ * of the given command. There is no way to override this.
+ * - Data transfers are limited to PAGE_SIZE (4K on i386, 8K on alpha).
+ * - The length (x + y) must be at least OMAX_SB_LEN bytes long to
+ * accommodate the sense buffer when an error occurs.
+ * The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that
+ * old code will not be surprised.
+ * - If a Unix error occurs (e.g. ENOMEM) then the user will receive
+ * a negative return and the Unix error code in 'errno'.
+ * If the SCSI command succeeds then 0 is returned.
+ * Positive numbers returned are the compacted SCSI error codes (4
+ * bytes in one int) where the lowest byte is the SCSI status.
+ * See the drivers/scsi/scsi.h file for more information on this.
+ *
+ */
+#define OMAX_SB_LEN 16 /* Old sense buffer length */
+
+int scsi_ioctl_send_command(struct scsi_device *sdev,
+ struct scsi_ioctl_command __user *sic)
+{
+ char *buf;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+ char __user *cmd_in;
+ unsigned char opcode;
+ unsigned int inlen, outlen, cmdlen;
+ unsigned int needed, buf_needed;
+ int timeout, retries, result;
+ int data_direction;
+ gfp_t gfp_mask = GFP_KERNEL;
+
+ if (!sic)
+ return -EINVAL;
+
+ if (sdev->host->unchecked_isa_dma)
+ gfp_mask |= GFP_DMA;
+
+ /*
+ * Verify that we can read at least this much.
+ */
+ if (!access_ok(VERIFY_READ, sic, sizeof(Scsi_Ioctl_Command)))
+ return -EFAULT;
+
+ if(__get_user(inlen, &sic->inlen))
+ return -EFAULT;
+
+ if(__get_user(outlen, &sic->outlen))
+ return -EFAULT;
+
+ /*
+ * We do not transfer more than MAX_BUF with this interface.
+ * If the user needs to transfer more data than this, they
+ * should use scsi_generics (sg) instead.
+ */
+ if (inlen > MAX_BUF)
+ return -EINVAL;
+ if (outlen > MAX_BUF)
+ return -EINVAL;
+
+ cmd_in = sic->data;
+ if(get_user(opcode, cmd_in))
+ return -EFAULT;
+
+ needed = buf_needed = (inlen > outlen ? inlen : outlen);
+ if (buf_needed) {
+ buf_needed = (buf_needed + 511) & ~511;
+ if (buf_needed > MAX_BUF)
+ buf_needed = MAX_BUF;
+ buf = kmalloc(buf_needed, gfp_mask);
+ if (!buf)
+ return -ENOMEM;
+ memset(buf, 0, buf_needed);
+ if (inlen == 0) {
+ data_direction = DMA_FROM_DEVICE;
+ } else if (outlen == 0 ) {
+ data_direction = DMA_TO_DEVICE;
+ } else {
+ /*
+ * Can this ever happen?
+ */
+ data_direction = DMA_BIDIRECTIONAL;
+ }
+
+ } else {
+ buf = NULL;
+ data_direction = DMA_NONE;
+ }
+
+ /*
+ * Obtain the command from the user's address space.
+ */
+ cmdlen = COMMAND_SIZE(opcode);
+
+ result = -EFAULT;
+
+ if (!access_ok(VERIFY_READ, cmd_in, cmdlen + inlen))
+ goto error;
+
+ if(__copy_from_user(cmd, cmd_in, cmdlen))
+ goto error;
+
+ /*
+ * Obtain the data to be sent to the device (if any).
+ */
+
+ if(inlen && copy_from_user(buf, cmd_in + cmdlen, inlen))
+ goto error;
+
+ switch (opcode) {
+ case SEND_DIAGNOSTIC:
+ case FORMAT_UNIT:
+ timeout = FORMAT_UNIT_TIMEOUT;
+ retries = 1;
+ break;
+ case START_STOP:
+ timeout = START_STOP_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case MOVE_MEDIUM:
+ timeout = MOVE_MEDIUM_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case READ_ELEMENT_STATUS:
+ timeout = READ_ELEMENT_STATUS_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case READ_DEFECT_DATA:
+ timeout = READ_DEFECT_DATA_TIMEOUT;
+ retries = 1;
+ break;
+ default:
+ timeout = IOCTL_NORMAL_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ }
+
+ result = scsi_execute(sdev, cmd, data_direction, buf, needed,
+ sense, timeout, retries, 0);
+
+ /*
+ * If there was an error condition, pass the info back to the user.
+ */
+ if (result) {
+ int sb_len = sizeof(*sense);
+
+ sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
+ if (copy_to_user(cmd_in, sense, sb_len))
+ result = -EFAULT;
+ } else {
+ if (outlen && copy_to_user(cmd_in, buf, outlen))
+ result = -EFAULT;
+ }
+
+error:
+ kfree(buf);
+ return result;
+}
+EXPORT_SYMBOL(scsi_ioctl_send_command);
+