forcedeth: statistics supported
[safe/jmp/linux-2.6] / drivers / ata / libata-scsi.c
index 307910b..73902d3 100644 (file)
@@ -273,8 +273,8 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
 {
        int rc = 0;
        u8 scsi_cmd[MAX_COMMAND_SIZE];
-       u8 args[7];
-       struct scsi_sense_hdr sshdr;
+       u8 args[7], *sensebuf = NULL;
+       int cmd_result;
 
        if (arg == NULL)
                return -EINVAL;
@@ -282,10 +282,14 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
        if (copy_from_user(args, arg, sizeof(args)))
                return -EFAULT;
 
+       sensebuf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
+       if (!sensebuf)
+               return -ENOMEM;
+
        memset(scsi_cmd, 0, sizeof(scsi_cmd));
        scsi_cmd[0]  = ATA_16;
        scsi_cmd[1]  = (3 << 1); /* Non-data */
-       /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */
+       scsi_cmd[2]  = 0x20;     /* cc but no off.line or data xfer */
        scsi_cmd[4]  = args[1];
        scsi_cmd[6]  = args[2];
        scsi_cmd[8]  = args[3];
@@ -295,11 +299,46 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
 
        /* Good values for timeout and retries?  Values below
           from scsi_ioctl_send_command() for default case... */
-       if (scsi_execute_req(scsidev, scsi_cmd, DMA_NONE, NULL, 0, &sshdr,
-                            (10*HZ), 5))
+       cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0,
+                               sensebuf, (10*HZ), 5, 0);
+
+       if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
+               u8 *desc = sensebuf + 8;
+               cmd_result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */
+
+               /* If we set cc then ATA pass-through will cause a
+                * check condition even if no error. Filter that. */
+               if (cmd_result & SAM_STAT_CHECK_CONDITION) {
+                       struct scsi_sense_hdr sshdr;
+                       scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE,
+                                               &sshdr);
+                       if (sshdr.sense_key==0 &&
+                               sshdr.asc==0 && sshdr.ascq==0)
+                               cmd_result &= ~SAM_STAT_CHECK_CONDITION;
+               }
+
+               /* Send userspace ATA registers */
+               if (sensebuf[0] == 0x72 &&      /* format is "descriptor" */
+                               desc[0] == 0x09) {/* code is "ATA Descriptor" */
+                       args[0] = desc[13];     /* status */
+                       args[1] = desc[3];      /* error */
+                       args[2] = desc[5];      /* sector count (0:7) */
+                       args[3] = desc[7];      /* lbal */
+                       args[4] = desc[9];      /* lbam */
+                       args[5] = desc[11];     /* lbah */
+                       args[6] = desc[12];     /* select */
+                       if (copy_to_user(arg, args, sizeof(args)))
+                               rc = -EFAULT;
+               }
+       }
+
+       if (cmd_result) {
                rc = -EIO;
+               goto error;
+       }
 
-       /* Need code to retrieve data from check condition? */
+ error:
+       kfree(sensebuf);
        return rc;
 }
 
@@ -372,7 +411,7 @@ struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
                if (cmd->use_sg) {
                        qc->__sg = (struct scatterlist *) cmd->request_buffer;
                        qc->n_elem = cmd->use_sg;
-               } else {
+               } else if (cmd->request_bufflen) {
                        qc->__sg = &qc->sgent;
                        qc->n_elem = 1;
                }
@@ -953,6 +992,9 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
        struct ata_taskfile *tf = &qc->tf;
        const u8 *cdb = scmd->cmnd;
 
+       if (scmd->cmd_len < 5)
+               goto invalid_fld;
+
        tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
        tf->protocol = ATA_PROT_NODATA;
        if (cdb[1] & 0x1) {
@@ -980,11 +1022,10 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
                }
 
                tf->command = ATA_CMD_VERIFY;   /* READ VERIFY */
-       } else {
-               tf->nsect = 0;  /* time period value (0 implies now) */
-               tf->command = ATA_CMD_STANDBY;
-               /* Consider: ATA STANDBY IMMEDIATE command */
-       }
+       } else
+               /* Issue ATA STANDBY IMMEDIATE command */
+               tf->command = ATA_CMD_STANDBYNOW1;
+
        /*
         * Standby and Idle condition timers could be implemented but that
         * would require libata to implement the Power condition mode page
@@ -1144,11 +1185,15 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc)
        tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
        tf->protocol = ATA_PROT_NODATA;
 
-       if (cdb[0] == VERIFY)
+       if (cdb[0] == VERIFY) {
+               if (scmd->cmd_len < 10)
+                       goto invalid_fld;
                scsi_10_lba_len(cdb, &block, &n_block);
-       else if (cdb[0] == VERIFY_16)
+       } else if (cdb[0] == VERIFY_16) {
+               if (scmd->cmd_len < 16)
+                       goto invalid_fld;
                scsi_16_lba_len(cdb, &block, &n_block);
-       else
+       else
                goto invalid_fld;
 
        if (!n_block)
@@ -1271,12 +1316,16 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
        switch (cdb[0]) {
        case READ_10:
        case WRITE_10:
+               if (unlikely(scmd->cmd_len < 10))
+                       goto invalid_fld;
                scsi_10_lba_len(cdb, &block, &n_block);
                if (unlikely(cdb[1] & (1 << 3)))
                        tf_flags |= ATA_TFLAG_FUA;
                break;
        case READ_6:
        case WRITE_6:
+               if (unlikely(scmd->cmd_len < 6))
+                       goto invalid_fld;
                scsi_6_lba_len(cdb, &block, &n_block);
 
                /* for 6-byte r/w commands, transfer length 0
@@ -1287,6 +1336,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
                break;
        case READ_16:
        case WRITE_16:
+               if (unlikely(scmd->cmd_len < 16))
+                       goto invalid_fld;
                scsi_16_lba_len(cdb, &block, &n_block);
                if (unlikely(cdb[1] & (1 << 3)))
                        tf_flags |= ATA_TFLAG_FUA;
@@ -2355,7 +2406,8 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
                if (ata_check_atapi_dma(qc))
                        using_pio = 1;
 
-       memcpy(&qc->cdb, scmd->cmnd, dev->cdb_len);
+       memset(qc->cdb, 0, dev->cdb_len);
+       memcpy(qc->cdb, scmd->cmnd, scmd->cmd_len);
 
        qc->complete_fn = atapi_qc_complete;
 
@@ -2696,6 +2748,13 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
 {
        int rc = 0;
 
+       if (unlikely(!scmd->cmd_len)) {
+               ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len CDB\n");
+               scmd->result = DID_ERROR << 16;
+               done(scmd);
+               return 0;
+       }
+
        if (dev->class == ATA_DEV_ATA) {
                ata_xlat_func_t xlat_func = ata_get_xlat_func(dev,
                                                              scmd->cmnd[0]);