[SCSI] iscsi: data digest calculation fix
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_spi.c
index 28966d0..4002a98 100644 (file)
 #include "scsi_priv.h"
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
-#include <scsi/scsi_request.h>
+#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_spi.h>
 
-#define SPI_PRINTK(x, l, f, a...)      dev_printk(l, &(x)->dev, f , ##a)
-
-#define SPI_NUM_ATTRS 10       /* increase this if you add attributes */
+#define SPI_NUM_ATTRS 14       /* increase this if you add attributes */
 #define SPI_OTHER_ATTRS 1      /* Increase this if you add "always
                                 * on" attributes */
 #define SPI_HOST_ATTRS 1
@@ -106,27 +104,31 @@ static int sprint_frac(char *dest, int value, int denom)
        return result;
 }
 
-/* Modification of scsi_wait_req that will clear UNIT ATTENTION conditions
- * resulting from (likely) bus and device resets */
-static void spi_wait_req(struct scsi_request *sreq, const void *cmd,
-                        void *buffer, unsigned bufflen)
+static int spi_execute(struct scsi_device *sdev, const void *cmd,
+                      enum dma_data_direction dir,
+                      void *buffer, unsigned bufflen,
+                      struct scsi_sense_hdr *sshdr)
 {
-       int i;
+       int i, result;
+       unsigned char sense[SCSI_SENSE_BUFFERSIZE];
 
        for(i = 0; i < DV_RETRIES; i++) {
-               sreq->sr_request->flags |= REQ_FAILFAST;
-
-               scsi_wait_req(sreq, cmd, buffer, bufflen,
-                             DV_TIMEOUT, /* retries */ 1);
-               if (sreq->sr_result & DRIVER_SENSE) {
-                       struct scsi_sense_hdr sshdr;
-
-                       if (scsi_request_normalize_sense(sreq, &sshdr)
-                           && sshdr.sense_key == UNIT_ATTENTION)
+               result = scsi_execute(sdev, cmd, dir, buffer, bufflen,
+                                     sense, DV_TIMEOUT, /* retries */ 1,
+                                     REQ_FAILFAST);
+               if (result & DRIVER_SENSE) {
+                       struct scsi_sense_hdr sshdr_tmp;
+                       if (!sshdr)
+                               sshdr = &sshdr_tmp;
+
+                       if (scsi_normalize_sense(sense, sizeof(*sense),
+                                                sshdr)
+                           && sshdr->sense_key == UNIT_ATTENTION)
                                continue;
                }
                break;
        }
+       return result;
 }
 
 static struct {
@@ -162,7 +164,8 @@ static inline enum spi_signal_type spi_signal_to_value(const char *name)
        return SPI_SIGNAL_UNKNOWN;
 }
 
-static int spi_host_setup(struct device *dev)
+static int spi_host_setup(struct transport_container *tc, struct device *dev,
+                         struct class_device *cdev)
 {
        struct Scsi_Host *shost = dev_to_shost(dev);
 
@@ -196,7 +199,9 @@ static int spi_host_match(struct attribute_container *cont,
        return &i->t.host_attrs.ac == cont;
 }
 
-static int spi_device_configure(struct device *dev)
+static int spi_device_configure(struct transport_container *tc,
+                               struct device *dev,
+                               struct class_device *cdev)
 {
        struct scsi_device *sdev = to_scsi_device(dev);
        struct scsi_target *starget = sdev->sdev_target;
@@ -214,13 +219,18 @@ static int spi_device_configure(struct device *dev)
        return 0;
 }
 
-static int spi_setup_transport_attrs(struct device *dev)
+static int spi_setup_transport_attrs(struct transport_container *tc,
+                                    struct device *dev,
+                                    struct class_device *cdev)
 {
        struct scsi_target *starget = to_scsi_target(dev);
 
        spi_period(starget) = -1;       /* illegal value */
+       spi_min_period(starget) = 0;
        spi_offset(starget) = 0;        /* async */
+       spi_max_offset(starget) = 255;
        spi_width(starget) = 0; /* narrow */
+       spi_max_width(starget) = 1;
        spi_iu(starget) = 0;    /* no IU */
        spi_dt(starget) = 0;    /* ST */
        spi_qas(starget) = 0;
@@ -228,6 +238,7 @@ static int spi_setup_transport_attrs(struct device *dev)
        spi_rd_strm(starget) = 0;
        spi_rti(starget) = 0;
        spi_pcomp_en(starget) = 0;
+       spi_hold_mcs(starget) = 0;
        spi_dv_pending(starget) = 0;
        spi_initial_dv(starget) = 0;
        init_MUTEX(&spi_dv_sem(starget));
@@ -235,6 +246,34 @@ static int spi_setup_transport_attrs(struct device *dev)
        return 0;
 }
 
+#define spi_transport_show_simple(field, format_string)                        \
+                                                                       \
+static ssize_t                                                         \
+show_spi_transport_##field(struct class_device *cdev, char *buf)       \
+{                                                                      \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct spi_transport_attrs *tp;                                 \
+                                                                       \
+       tp = (struct spi_transport_attrs *)&starget->starget_data;      \
+       return snprintf(buf, 20, format_string, tp->field);             \
+}
+
+#define spi_transport_store_simple(field, format_string)               \
+                                                                       \
+static ssize_t                                                         \
+store_spi_transport_##field(struct class_device *cdev, const char *buf, \
+                           size_t count)                               \
+{                                                                      \
+       int val;                                                        \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct spi_transport_attrs *tp;                                 \
+                                                                       \
+       tp = (struct spi_transport_attrs *)&starget->starget_data;      \
+       val = simple_strtoul(buf, NULL, 0);                             \
+       tp->field = val;                                                \
+       return count;                                                   \
+}
+
 #define spi_transport_show_function(field, format_string)              \
                                                                        \
 static ssize_t                                                         \
@@ -261,6 +300,25 @@ store_spi_transport_##field(struct class_device *cdev, const char *buf, \
        struct spi_internal *i = to_spi_internal(shost->transportt);    \
                                                                        \
        val = simple_strtoul(buf, NULL, 0);                             \
+       i->f->set_##field(starget, val);                        \
+       return count;                                                   \
+}
+
+#define spi_transport_store_max(field, format_string)                  \
+static ssize_t                                                         \
+store_spi_transport_##field(struct class_device *cdev, const char *buf, \
+                           size_t count)                               \
+{                                                                      \
+       int val;                                                        \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
+       struct spi_internal *i = to_spi_internal(shost->transportt);    \
+       struct spi_transport_attrs *tp                                  \
+               = (struct spi_transport_attrs *)&starget->starget_data; \
+                                                                       \
+       val = simple_strtoul(buf, NULL, 0);                             \
+       if (val > tp->max_##field)                                      \
+               val = tp->max_##field;                                  \
        i->f->set_##field(starget, val);                                \
        return count;                                                   \
 }
@@ -272,9 +330,24 @@ static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,                 \
                         show_spi_transport_##field,                    \
                         store_spi_transport_##field);
 
+#define spi_transport_simple_attr(field, format_string)                        \
+       spi_transport_show_simple(field, format_string)                 \
+       spi_transport_store_simple(field, format_string)                \
+static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,                     \
+                        show_spi_transport_##field,                    \
+                        store_spi_transport_##field);
+
+#define spi_transport_max_attr(field, format_string)                   \
+       spi_transport_show_function(field, format_string)               \
+       spi_transport_store_max(field, format_string)                   \
+       spi_transport_simple_attr(max_##field, format_string)           \
+static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,                     \
+                        show_spi_transport_##field,                    \
+                        store_spi_transport_##field);
+
 /* The Parallel SCSI Tranport Attributes: */
-spi_transport_rd_attr(offset, "%d\n");
-spi_transport_rd_attr(width, "%d\n");
+spi_transport_max_attr(offset, "%d\n");
+spi_transport_max_attr(width, "%d\n");
 spi_transport_rd_attr(iu, "%d\n");
 spi_transport_rd_attr(dt, "%d\n");
 spi_transport_rd_attr(qas, "%d\n");
@@ -282,44 +355,41 @@ spi_transport_rd_attr(wr_flow, "%d\n");
 spi_transport_rd_attr(rd_strm, "%d\n");
 spi_transport_rd_attr(rti, "%d\n");
 spi_transport_rd_attr(pcomp_en, "%d\n");
+spi_transport_rd_attr(hold_mcs, "%d\n");
+
+/* we only care about the first child device so we return 1 */
+static int child_iter(struct device *dev, void *data)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+
+       spi_dv_device(sdev);
+       return 1;
+}
 
 static ssize_t
 store_spi_revalidate(struct class_device *cdev, const char *buf, size_t count)
 {
        struct scsi_target *starget = transport_class_to_starget(cdev);
 
-       /* FIXME: we're relying on an awful lot of device internals
-        * here.  We really need a function to get the first available
-        * child */
-       struct device *dev = container_of(starget->dev.children.next, struct device, node);
-       struct scsi_device *sdev = to_scsi_device(dev);
-       spi_dv_device(sdev);
+       device_for_each_child(&starget->dev, NULL, child_iter);
        return count;
 }
 static CLASS_DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate);
 
 /* Translate the period into ns according to the current spec
  * for SDTR/PPR messages */
-static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
-
+static ssize_t
+show_spi_transport_period_helper(struct class_device *cdev, char *buf,
+                                int period)
 {
-       struct scsi_target *starget = transport_class_to_starget(cdev);
-       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
-       struct spi_transport_attrs *tp;
        int len, picosec;
-       struct spi_internal *i = to_spi_internal(shost->transportt);
 
-       tp = (struct spi_transport_attrs *)&starget->starget_data;
-
-       if (i->f->get_period)
-               i->f->get_period(starget);
-
-       if (tp->period < 0 || tp->period > 0xff) {
+       if (period < 0 || period > 0xff) {
                picosec = -1;
-       } else if (tp->period <= SPI_STATIC_PPR) {
-               picosec = ppr_to_ps[tp->period];
+       } else if (period <= SPI_STATIC_PPR) {
+               picosec = ppr_to_ps[period];
        } else {
-               picosec = tp->period * 4000;
+               picosec = period * 4000;
        }
 
        if (picosec == -1) {
@@ -334,12 +404,9 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
 }
 
 static ssize_t
-store_spi_transport_period(struct class_device *cdev, const char *buf,
-                           size_t count)
+store_spi_transport_period_helper(struct class_device *cdev, const char *buf,
+                                 size_t count, int *periodp)
 {
-       struct scsi_target *starget = transport_class_to_starget(cdev);
-       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
-       struct spi_internal *i = to_spi_internal(shost->transportt);
        int j, picosec, period = -1;
        char *endp;
 
@@ -368,15 +435,79 @@ store_spi_transport_period(struct class_device *cdev, const char *buf,
        if (period > 0xff)
                period = 0xff;
 
-       i->f->set_period(starget, period);
+       *periodp = period;
 
        return count;
 }
 
+static ssize_t
+show_spi_transport_period(struct class_device *cdev, char *buf)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct spi_internal *i = to_spi_internal(shost->transportt);
+       struct spi_transport_attrs *tp =
+               (struct spi_transport_attrs *)&starget->starget_data;
+
+       if (i->f->get_period)
+               i->f->get_period(starget);
+
+       return show_spi_transport_period_helper(cdev, buf, tp->period);
+}
+
+static ssize_t
+store_spi_transport_period(struct class_device *cdev, const char *buf,
+                           size_t count)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct spi_internal *i = to_spi_internal(shost->transportt);
+       struct spi_transport_attrs *tp =
+               (struct spi_transport_attrs *)&starget->starget_data;
+       int period, retval;
+
+       retval = store_spi_transport_period_helper(cdev, buf, count, &period);
+
+       if (period < tp->min_period)
+               period = tp->min_period;
+
+       i->f->set_period(starget, period);
+
+       return retval;
+}
+
 static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR, 
                         show_spi_transport_period,
                         store_spi_transport_period);
 
+static ssize_t
+show_spi_transport_min_period(struct class_device *cdev, char *buf)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct spi_transport_attrs *tp =
+               (struct spi_transport_attrs *)&starget->starget_data;
+
+       return show_spi_transport_period_helper(cdev, buf, tp->min_period);
+}
+
+static ssize_t
+store_spi_transport_min_period(struct class_device *cdev, const char *buf,
+                           size_t count)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct spi_transport_attrs *tp =
+               (struct spi_transport_attrs *)&starget->starget_data;
+
+       return store_spi_transport_period_helper(cdev, buf, count,
+                                                &tp->min_period);
+}
+
+
+static CLASS_DEVICE_ATTR(min_period, S_IRUGO | S_IWUSR, 
+                        show_spi_transport_min_period,
+                        store_spi_transport_min_period);
+
+
 static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf)
 {
        struct Scsi_Host *shost = transport_class_to_shost(cdev);
@@ -417,13 +548,13 @@ enum spi_compare_returns {
 /* This is for read/write Domain Validation:  If the device supports
  * an echo buffer, we do read/write tests to it */
 static enum spi_compare_returns
-spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
+spi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer,
                          u8 *ptr, const int retries)
 {
-       struct scsi_device *sdev = sreq->sr_device;
        int len = ptr - buffer;
-       int j, k, r;
+       int j, k, r, result;
        unsigned int pattern = 0x0000ffff;
+       struct scsi_sense_hdr sshdr;
 
        const char spi_write_buffer[] = {
                WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0
@@ -468,14 +599,12 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
        }
 
        for (r = 0; r < retries; r++) {
-               sreq->sr_cmd_len = 0;   /* wait_req to fill in */
-               sreq->sr_data_direction = DMA_TO_DEVICE;
-               spi_wait_req(sreq, spi_write_buffer, buffer, len);
-               if(sreq->sr_result || !scsi_device_online(sdev)) {
-                       struct scsi_sense_hdr sshdr;
+               result = spi_execute(sdev, spi_write_buffer, DMA_TO_DEVICE,
+                                    buffer, len, &sshdr);
+               if(result || !scsi_device_online(sdev)) {
 
                        scsi_device_set_state(sdev, SDEV_QUIESCE);
-                       if (scsi_request_normalize_sense(sreq, &sshdr)
+                       if (scsi_sense_valid(&sshdr)
                            && sshdr.sense_key == ILLEGAL_REQUEST
                            /* INVALID FIELD IN CDB */
                            && sshdr.asc == 0x24 && sshdr.ascq == 0x00)
@@ -487,14 +616,13 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
                                return SPI_COMPARE_SKIP_TEST;
 
 
-                       SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result);
+                       sdev_printk(KERN_ERR, sdev, "Write Buffer failure %x\n", result);
                        return SPI_COMPARE_FAILURE;
                }
 
                memset(ptr, 0, len);
-               sreq->sr_cmd_len = 0;   /* wait_req to fill in */
-               sreq->sr_data_direction = DMA_FROM_DEVICE;
-               spi_wait_req(sreq, spi_read_buffer, ptr, len);
+               spi_execute(sdev, spi_read_buffer, DMA_FROM_DEVICE,
+                           ptr, len, NULL);
                scsi_device_set_state(sdev, SDEV_QUIESCE);
 
                if (memcmp(buffer, ptr, len) != 0)
@@ -506,25 +634,22 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,
 /* This is for the simplest form of Domain Validation: a read test
  * on the inquiry data from the device */
 static enum spi_compare_returns
-spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,
+spi_dv_device_compare_inquiry(struct scsi_device *sdev, u8 *buffer,
                              u8 *ptr, const int retries)
 {
-       int r;
-       const int len = sreq->sr_device->inquiry_len;
-       struct scsi_device *sdev = sreq->sr_device;
+       int r, result;
+       const int len = sdev->inquiry_len;
        const char spi_inquiry[] = {
                INQUIRY, 0, 0, 0, len, 0
        };
 
        for (r = 0; r < retries; r++) {
-               sreq->sr_cmd_len = 0;   /* wait_req to fill in */
-               sreq->sr_data_direction = DMA_FROM_DEVICE;
-
                memset(ptr, 0, len);
 
-               spi_wait_req(sreq, spi_inquiry, ptr, len);
+               result = spi_execute(sdev, spi_inquiry, DMA_FROM_DEVICE,
+                                    ptr, len, NULL);
                
-               if(sreq->sr_result || !scsi_device_online(sdev)) {
+               if(result || !scsi_device_online(sdev)) {
                        scsi_device_set_state(sdev, SDEV_QUIESCE);
                        return SPI_COMPARE_FAILURE;
                }
@@ -545,51 +670,67 @@ spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,
 }
 
 static enum spi_compare_returns
-spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr,
+spi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr,
               enum spi_compare_returns 
-              (*compare_fn)(struct scsi_request *, u8 *, u8 *, int))
+              (*compare_fn)(struct scsi_device *, u8 *, u8 *, int))
 {
-       struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
-       struct scsi_device *sdev = sreq->sr_device;
+       struct spi_internal *i = to_spi_internal(sdev->host->transportt);
+       struct scsi_target *starget = sdev->sdev_target;
        int period = 0, prevperiod = 0; 
        enum spi_compare_returns retval;
 
 
        for (;;) {
                int newperiod;
-               retval = compare_fn(sreq, buffer, ptr, DV_LOOPS);
+               retval = compare_fn(sdev, buffer, ptr, DV_LOOPS);
 
                if (retval == SPI_COMPARE_SUCCESS
                    || retval == SPI_COMPARE_SKIP_TEST)
                        break;
 
                /* OK, retrain, fallback */
+               if (i->f->get_iu)
+                       i->f->get_iu(starget);
+               if (i->f->get_qas)
+                       i->f->get_qas(starget);
                if (i->f->get_period)
                        i->f->get_period(sdev->sdev_target);
-               newperiod = spi_period(sdev->sdev_target);
-               period = newperiod > period ? newperiod : period;
-               if (period < 0x0d)
-                       period++;
-               else
-                       period += period >> 1;
-
-               if (unlikely(period > 0xff || period == prevperiod)) {
-                       /* Total failure; set to async and return */
-                       SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Failure, dropping back to Asynchronous\n");
-                       DV_SET(offset, 0);
-                       return SPI_COMPARE_FAILURE;
+
+               /* Here's the fallback sequence; first try turning off
+                * IU, then QAS (if we can control them), then finally
+                * fall down the periods */
+               if (i->f->set_iu && spi_iu(starget)) {
+                       starget_printk(KERN_ERR, starget, "Domain Validation Disabing Information Units\n");
+                       DV_SET(iu, 0);
+               } else if (i->f->set_qas && spi_qas(starget)) {
+                       starget_printk(KERN_ERR, starget, "Domain Validation Disabing Quick Arbitration and Selection\n");
+                       DV_SET(qas, 0);
+               } else {
+                       newperiod = spi_period(starget);
+                       period = newperiod > period ? newperiod : period;
+                       if (period < 0x0d)
+                               period++;
+                       else
+                               period += period >> 1;
+
+                       if (unlikely(period > 0xff || period == prevperiod)) {
+                               /* Total failure; set to async and return */
+                               starget_printk(KERN_ERR, starget, "Domain Validation Failure, dropping back to Asynchronous\n");
+                               DV_SET(offset, 0);
+                               return SPI_COMPARE_FAILURE;
+                       }
+                       starget_printk(KERN_ERR, starget, "Domain Validation detected failure, dropping back\n");
+                       DV_SET(period, period);
+                       prevperiod = period;
                }
-               SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation detected failure, dropping back\n");
-               DV_SET(period, period);
-               prevperiod = period;
        }
        return retval;
 }
 
 static int
-spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
+spi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer)
 {
-       int l;
+       int l, result;
 
        /* first off do a test unit ready.  This can error out 
         * because of reservations or some other reason.  If it
@@ -605,18 +746,16 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
        };
 
        
-       sreq->sr_cmd_len = 0;
-       sreq->sr_data_direction = DMA_NONE;
-
        /* We send a set of three TURs to clear any outstanding 
         * unit attention conditions if they exist (Otherwise the
         * buffer tests won't be happy).  If the TUR still fails
         * (reservation conflict, device not ready, etc) just
         * skip the write tests */
        for (l = 0; ; l++) {
-               spi_wait_req(sreq, spi_test_unit_ready, NULL, 0);
+               result = spi_execute(sdev, spi_test_unit_ready, DMA_NONE, 
+                                    NULL, 0, NULL);
 
-               if(sreq->sr_result) {
+               if(result) {
                        if(l >= 3)
                                return 0;
                } else {
@@ -625,12 +764,10 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
                }
        }
 
-       sreq->sr_cmd_len = 0;
-       sreq->sr_data_direction = DMA_FROM_DEVICE;
+       result = spi_execute(sdev, spi_read_buffer_descriptor, 
+                            DMA_FROM_DEVICE, buffer, 4, NULL);
 
-       spi_wait_req(sreq, spi_read_buffer_descriptor, buffer, 4);
-
-       if (sreq->sr_result)
+       if (result)
                /* Device has no echo buffer */
                return 0;
 
@@ -638,32 +775,33 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer)
 }
 
 static void
-spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
+spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
 {
-       struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt);
-       struct scsi_device *sdev = sreq->sr_device;
+       struct spi_internal *i = to_spi_internal(sdev->host->transportt);
+       struct scsi_target *starget = sdev->sdev_target;
        int len = sdev->inquiry_len;
        /* first set us up for narrow async */
        DV_SET(offset, 0);
        DV_SET(width, 0);
        
-       if (spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS)
+       if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS)
            != SPI_COMPARE_SUCCESS) {
-               SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Initial Inquiry Failed\n");
+               starget_printk(KERN_ERR, starget, "Domain Validation Initial Inquiry Failed\n");
                /* FIXME: should probably offline the device here? */
                return;
        }
 
        /* test width */
-       if (i->f->set_width && sdev->wdtr) {
-               i->f->set_width(sdev->sdev_target, 1);
+       if (i->f->set_width && spi_max_width(starget) &&
+           scsi_device_wide(sdev)) {
+               i->f->set_width(starget, 1);
 
-               if (spi_dv_device_compare_inquiry(sreq, buffer,
+               if (spi_dv_device_compare_inquiry(sdev, buffer,
                                                   buffer + len,
                                                   DV_LOOPS)
                    != SPI_COMPARE_SUCCESS) {
-                       SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Wide Transfers Fail\n");
-                       i->f->set_width(sdev->sdev_target, 0);
+                       starget_printk(KERN_ERR, starget, "Wide Transfers Fail\n");
+                       i->f->set_width(starget, 0);
                }
        }
 
@@ -671,35 +809,61 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
                return;
 
        /* device can't handle synchronous */
-       if(!sdev->ppr && !sdev->sdtr)
+       if (!scsi_device_sync(sdev) && !scsi_device_dt(sdev))
                return;
 
-       /* see if the device has an echo buffer.  If it does we can
-        * do the SPI pattern write tests */
-
-       len = 0;
-       if (sdev->ppr)
-               len = spi_dv_device_get_echo_buffer(sreq, buffer);
+       /* len == -1 is the signal that we need to ascertain the
+        * presence of an echo buffer before trying to use it.  len ==
+        * 0 means we don't have an echo buffer */
+       len = -1;
 
  retry:
 
        /* now set up to the maximum */
-       DV_SET(offset, 255);
-       DV_SET(period, 1);
-
-       if (len == 0) {
-               SPI_PRINTK(sdev->sdev_target, KERN_INFO, "Domain Validation skipping write tests\n");
-               spi_dv_retrain(sreq, buffer, buffer + len,
-                              spi_dv_device_compare_inquiry);
+       DV_SET(offset, spi_max_offset(starget));
+       DV_SET(period, spi_min_period(starget));
+       /* try QAS requests; this should be harmless to set if the
+        * target supports it */
+       if (scsi_device_qas(sdev))
+               DV_SET(qas, 1);
+       /* Also try IU transfers */
+       if (scsi_device_ius(sdev))
+               DV_SET(iu, 1);
+       if (spi_min_period(starget) < 9) {
+               /* This u320 (or u640). Ignore the coupled parameters
+                * like DT and IU, but set the optional ones */
+               DV_SET(rd_strm, 1);
+               DV_SET(wr_flow, 1);
+               DV_SET(rti, 1);
+               if (spi_min_period(starget) == 8)
+                       DV_SET(pcomp_en, 1);
+       }
+       /* Do the read only INQUIRY tests */
+       spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len,
+                      spi_dv_device_compare_inquiry);
+       /* See if we actually managed to negotiate and sustain DT */
+       if (i->f->get_dt)
+               i->f->get_dt(starget);
+
+       /* see if the device has an echo buffer.  If it does we can do
+        * the SPI pattern write tests.  Because of some broken
+        * devices, we *only* try this on a device that has actually
+        * negotiated DT */
+
+       if (len == -1 && spi_dt(starget))
+               len = spi_dv_device_get_echo_buffer(sdev, buffer);
+
+       if (len <= 0) {
+               starget_printk(KERN_INFO, starget, "Domain Validation skipping write tests\n");
                return;
        }
 
        if (len > SPI_MAX_ECHO_BUFFER_SIZE) {
-               SPI_PRINTK(sdev->sdev_target, KERN_WARNING, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE);
+               starget_printk(KERN_WARNING, starget, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE);
                len = SPI_MAX_ECHO_BUFFER_SIZE;
        }
 
-       if (spi_dv_retrain(sreq, buffer, buffer + len,
+       if (spi_dv_retrain(sdev, buffer, buffer + len,
                           spi_dv_device_echo_buffer)
            == SPI_COMPARE_SKIP_TEST) {
                /* OK, the stupid drive can't do a write echo buffer
@@ -722,16 +886,12 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer)
 void
 spi_dv_device(struct scsi_device *sdev)
 {
-       struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL);
        struct scsi_target *starget = sdev->sdev_target;
        u8 *buffer;
        const int len = SPI_MAX_ECHO_BUFFER_SIZE*2;
 
-       if (unlikely(!sreq))
-               return;
-
        if (unlikely(scsi_device_get(sdev)))
-               goto out_free_req;
+               return;
 
        buffer = kmalloc(len, GFP_KERNEL);
 
@@ -750,11 +910,11 @@ spi_dv_device(struct scsi_device *sdev)
        spi_dv_pending(starget) = 1;
        down(&spi_dv_sem(starget));
 
-       SPI_PRINTK(starget, KERN_INFO, "Beginning Domain Validation\n");
+       starget_printk(KERN_INFO, starget, "Beginning Domain Validation\n");
 
-       spi_dv_device_internal(sreq, buffer);
+       spi_dv_device_internal(sdev, buffer);
 
-       SPI_PRINTK(starget, KERN_INFO, "Ending Domain Validation\n");
+       starget_printk(KERN_INFO, starget, "Ending Domain Validation\n");
 
        up(&spi_dv_sem(starget));
        spi_dv_pending(starget) = 0;
@@ -767,8 +927,6 @@ spi_dv_device(struct scsi_device *sdev)
        kfree(buffer);
  out_put:
        scsi_device_put(sdev);
- out_free_req:
-       scsi_release_request(sreq);
 }
 EXPORT_SYMBOL(spi_dv_device);
 
@@ -871,12 +1029,19 @@ void spi_display_xfer_agreement(struct scsi_target *starget)
                sprint_frac(tmp, picosec, 1000);
 
                dev_info(&starget->dev,
-                       "%s %sSCSI %d.%d MB/s %s%s%s (%s ns, offset %d)\n",
-                       scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
-                       tp->dt ? "DT" : "ST", tp->iu ? " IU" : "",
-                       tp->qas  ? " QAS" : "", tmp, tp->offset);
+                        "%s %sSCSI %d.%d MB/s %s%s%s%s%s%s%s%s (%s ns, offset %d)\n",
+                        scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
+                        tp->dt ? "DT" : "ST",
+                        tp->iu ? " IU" : "",
+                        tp->qas  ? " QAS" : "",
+                        tp->rd_strm ? " RDSTRM" : "",
+                        tp->rti ? " RTI" : "",
+                        tp->wr_flow ? " WRFLOW" : "",
+                        tp->pcomp_en ? " PCOMP" : "",
+                        tp->hold_mcs ? " HMCS" : "",
+                        tmp, tp->offset);
        } else {
-               dev_info(&starget->dev, "%sasynchronous.\n",
+               dev_info(&starget->dev, "%sasynchronous\n",
                                tp->width ? "wide " : "");
        }
 }
@@ -892,6 +1057,16 @@ EXPORT_SYMBOL(spi_display_xfer_agreement);
        if (i->f->show_##field)                                         \
                count++
 
+#define SETUP_RELATED_ATTRIBUTE(field, rel_field)                      \
+       i->private_attrs[count] = class_device_attr_##field;            \
+       if (!i->f->set_##rel_field) {                                   \
+               i->private_attrs[count].attr.mode = S_IRUGO;            \
+               i->private_attrs[count].store = NULL;                   \
+       }                                                               \
+       i->attrs[count] = &i->private_attrs[count];                     \
+       if (i->f->show_##rel_field)                                     \
+               count++
+
 #define SETUP_HOST_ATTRIBUTE(field)                                    \
        i->private_host_attrs[count] = class_device_attr_##field;       \
        if (!i->f->set_##field) {                                       \
@@ -906,6 +1081,7 @@ static int spi_device_match(struct attribute_container *cont,
 {
        struct scsi_device *sdev;
        struct Scsi_Host *shost;
+       struct spi_internal *i;
 
        if (!scsi_is_sdev_device(dev))
                return 0;
@@ -918,6 +1094,9 @@ static int spi_device_match(struct attribute_container *cont,
        /* Note: this class has no device attributes, so it has
         * no per-HBA allocation and thus we don't need to distinguish
         * the attribute containers for the device */
+       i = to_spi_internal(shost->transportt);
+       if (i->f->deny_binding && i->f->deny_binding(sdev->sdev_target))
+               return 0;
        return 1;
 }
 
@@ -925,6 +1104,7 @@ static int spi_target_match(struct attribute_container *cont,
                            struct device *dev)
 {
        struct Scsi_Host *shost;
+       struct scsi_target *starget;
        struct spi_internal *i;
 
        if (!scsi_is_target_device(dev))
@@ -936,7 +1116,11 @@ static int spi_target_match(struct attribute_container *cont,
                return 0;
 
        i = to_spi_internal(shost->transportt);
-       
+       starget = to_scsi_target(dev);
+
+       if (i->f->deny_binding && i->f->deny_binding(starget))
+               return 0;
+
        return &i->t.target_attrs.ac == cont;
 }
 
@@ -975,8 +1159,11 @@ spi_attach_transport(struct spi_function_template *ft)
        i->f = ft;
 
        SETUP_ATTRIBUTE(period);
+       SETUP_RELATED_ATTRIBUTE(min_period, period);
        SETUP_ATTRIBUTE(offset);
+       SETUP_RELATED_ATTRIBUTE(max_offset, offset);
        SETUP_ATTRIBUTE(width);
+       SETUP_RELATED_ATTRIBUTE(max_width, width);
        SETUP_ATTRIBUTE(iu);
        SETUP_ATTRIBUTE(dt);
        SETUP_ATTRIBUTE(qas);
@@ -984,6 +1171,7 @@ spi_attach_transport(struct spi_function_template *ft)
        SETUP_ATTRIBUTE(rd_strm);
        SETUP_ATTRIBUTE(rti);
        SETUP_ATTRIBUTE(pcomp_en);
+       SETUP_ATTRIBUTE(hold_mcs);
 
        /* if you add an attribute but forget to increase SPI_NUM_ATTRS
         * this bug will trigger */