linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / scsi / scsi_ioctl.c
index 32293f4..0bba7d8 100644 (file)
 #include <asm/uaccess.h>
 
 #include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
 #include <scsi/sg.h>
 #include <scsi/scsi_dbg.h>
 
@@ -110,8 +110,11 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
                                       sshdr.asc, sshdr.ascq);
                        break;
                case NOT_READY: /* This happens if there is no disc in drive */
-                       if (sdev->removable)
+                       if (sdev->removable && (cmd[0] != TEST_UNIT_READY)) {
+                               printk(KERN_INFO "Device not ready. Make sure"
+                                      " there is a disc in the drive.\n");
                                break;
+                       }
                case UNIT_ATTENTION:
                        if (sdev->removable) {
                                sdev->changed = 1;
@@ -154,6 +157,181 @@ int scsi_set_medium_removal(struct scsi_device *sdev, char state)
 }
 EXPORT_SYMBOL(scsi_set_medium_removal);
 
+/*
+ * 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);
+
 /*
  * The scsi_ioctl_get_pci() function places into arg the value
  * pci_dev::slot_name (8 characters) for the PCI device (if any).
@@ -232,7 +410,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
        case SCSI_IOCTL_SEND_COMMAND:
                if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
                        return -EACCES;
-               return sg_scsi_ioctl(NULL, sdev->request_queue, NULL, arg);
+               return scsi_ioctl_send_command(sdev, arg);
        case SCSI_IOCTL_DOORLOCK:
                return scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
        case SCSI_IOCTL_DOORUNLOCK: