]> err.no Git - linux-2.6/blobdiff - drivers/scsi/scsi_lib.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[linux-2.6] / drivers / scsi / scsi_lib.c
index 4a602853a98e72f89b94757c4b9e56b87587ad53..ede158d08d9df06830b92b6047b0a9453a71611c 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/hardirq.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_dbg.h>
@@ -285,13 +286,12 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
        int result;
        
        if (sshdr) {
-               sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
+               sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO);
                if (!sense)
                        return DRIVER_ERROR << 24;
-               memset(sense, 0, SCSI_SENSE_BUFFERSIZE);
        }
        result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
-                                 sense, timeout, retries, 0);
+                             sense, timeout, retries, 0);
        if (sshdr)
                scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
 
@@ -1497,7 +1497,7 @@ static void scsi_kill_request(struct request *req, request_queue_t *q)
 static void scsi_softirq_done(struct request *rq)
 {
        struct scsi_cmnd *cmd = rq->completion_data;
-       unsigned long wait_for = cmd->allowed * cmd->timeout_per_command;
+       unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command;
        int disposition;
 
        INIT_LIST_HEAD(&cmd->eh_entry);
@@ -1811,6 +1811,84 @@ void scsi_exit_queue(void)
                kmem_cache_destroy(sgp->slab);
        }
 }
+
+/**
+ *     scsi_mode_select - issue a mode select
+ *     @sdev:  SCSI device to be queried
+ *     @pf:    Page format bit (1 == standard, 0 == vendor specific)
+ *     @sp:    Save page bit (0 == don't save, 1 == save)
+ *     @modepage: mode page being requested
+ *     @buffer: request buffer (may not be smaller than eight bytes)
+ *     @len:   length of request buffer.
+ *     @timeout: command timeout
+ *     @retries: number of retries before failing
+ *     @data: returns a structure abstracting the mode header data
+ *     @sense: place to put sense data (or NULL if no sense to be collected).
+ *             must be SCSI_SENSE_BUFFERSIZE big.
+ *
+ *     Returns zero if successful; negative error number or scsi
+ *     status on error
+ *
+ */
+int
+scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
+                unsigned char *buffer, int len, int timeout, int retries,
+                struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
+{
+       unsigned char cmd[10];
+       unsigned char *real_buffer;
+       int ret;
+
+       memset(cmd, 0, sizeof(cmd));
+       cmd[1] = (pf ? 0x10 : 0) | (sp ? 0x01 : 0);
+
+       if (sdev->use_10_for_ms) {
+               if (len > 65535)
+                       return -EINVAL;
+               real_buffer = kmalloc(8 + len, GFP_KERNEL);
+               if (!real_buffer)
+                       return -ENOMEM;
+               memcpy(real_buffer + 8, buffer, len);
+               len += 8;
+               real_buffer[0] = 0;
+               real_buffer[1] = 0;
+               real_buffer[2] = data->medium_type;
+               real_buffer[3] = data->device_specific;
+               real_buffer[4] = data->longlba ? 0x01 : 0;
+               real_buffer[5] = 0;
+               real_buffer[6] = data->block_descriptor_length >> 8;
+               real_buffer[7] = data->block_descriptor_length;
+
+               cmd[0] = MODE_SELECT_10;
+               cmd[7] = len >> 8;
+               cmd[8] = len;
+       } else {
+               if (len > 255 || data->block_descriptor_length > 255 ||
+                   data->longlba)
+                       return -EINVAL;
+
+               real_buffer = kmalloc(4 + len, GFP_KERNEL);
+               if (!real_buffer)
+                       return -ENOMEM;
+               memcpy(real_buffer + 4, buffer, len);
+               len += 4;
+               real_buffer[0] = 0;
+               real_buffer[1] = data->medium_type;
+               real_buffer[2] = data->device_specific;
+               real_buffer[3] = data->block_descriptor_length;
+               
+
+               cmd[0] = MODE_SELECT;
+               cmd[4] = len;
+       }
+
+       ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len,
+                              sshdr, timeout, retries);
+       kfree(real_buffer);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_mode_select);
+
 /**
  *     scsi_mode_sense - issue a mode sense, falling back from 10 to 
  *             six bytes if necessary.
@@ -1832,7 +1910,8 @@ void scsi_exit_queue(void)
 int
 scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                  unsigned char *buffer, int len, int timeout, int retries,
-                 struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr) {
+                 struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
+{
        unsigned char cmd[12];
        int use_10_for_ms;
        int header_length;
@@ -1892,8 +1971,16 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
        }
 
        if(scsi_status_is_good(result)) {
-               data->header_length = header_length;
-               if(use_10_for_ms) {
+               if (unlikely(buffer[0] == 0x86 && buffer[1] == 0x0b &&
+                            (modepage == 6 || modepage == 8))) {
+                       /* Initio breakage? */
+                       header_length = 0;
+                       data->length = 13;
+                       data->medium_type = 0;
+                       data->device_specific = 0;
+                       data->longlba = 0;
+                       data->block_descriptor_length = 0;
+               } else if(use_10_for_ms) {
                        data->length = buffer[0]*256 + buffer[1] + 2;
                        data->medium_type = buffer[2];
                        data->device_specific = buffer[3];
@@ -1906,6 +1993,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                        data->device_specific = buffer[2];
                        data->block_descriptor_length = buffer[3];
                }
+               data->header_length = header_length;
        }
 
        return result;