cxgb{3,4}*: improve Kconfig dependencies
[linux-2.6.git] / drivers / scsi / scsi_transport_spi.c
index a1e521b..8a172d4 100644 (file)
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/config.h>
 #include <linux/ctype.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/workqueue.h>
 #include <linux/blkdev.h>
 #include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
 #include <scsi/scsi.h>
 #include "scsi_priv.h"
 #include <scsi/scsi_device.h>
 #define DV_RETRIES     3       /* should only need at most 
                                 * two cc/ua clears */
 
+/* Our blacklist flags */
+enum {
+       SPI_BLIST_NOIUS = 0x1,
+};
+
+/* blacklist table, modelled on scsi_devinfo.c */
+static struct {
+       char *vendor;
+       char *model;
+       unsigned flags;
+} spi_static_device_list[] __initdata = {
+       {"HP", "Ultrium 3-SCSI", SPI_BLIST_NOIUS },
+       {"IBM", "ULTRIUM-TD3", SPI_BLIST_NOIUS },
+       {NULL, NULL, 0}
+};
+
 /* Private data accessors (keep these out of the header file) */
-#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending)
+#define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress)
 #define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex)
 
 struct spi_internal {
        struct scsi_transport_template t;
        struct spi_function_template *f;
-       /* The actual attributes */
-       struct class_device_attribute private_attrs[SPI_NUM_ATTRS];
-       /* The array of null terminated pointers to attributes 
-        * needed by scsi_sysfs.c */
-       struct class_device_attribute *attrs[SPI_NUM_ATTRS + SPI_OTHER_ATTRS + 1];
-       struct class_device_attribute private_host_attrs[SPI_HOST_ATTRS];
-       struct class_device_attribute *host_attrs[SPI_HOST_ATTRS + 1];
 };
 
 #define to_spi_internal(tmpl)  container_of(tmpl, struct spi_internal, t)
@@ -116,13 +126,16 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
        for(i = 0; i < DV_RETRIES; i++) {
                result = scsi_execute(sdev, cmd, dir, buffer, bufflen,
                                      sense, DV_TIMEOUT, /* retries */ 1,
-                                     REQ_FAILFAST);
-               if (result & DRIVER_SENSE) {
+                                     REQ_FAILFAST_DEV |
+                                     REQ_FAILFAST_TRANSPORT |
+                                     REQ_FAILFAST_DRIVER,
+                                     NULL);
+               if (driver_byte(result) & DRIVER_SENSE) {
                        struct scsi_sense_hdr sshdr_tmp;
                        if (!sshdr)
                                sshdr = &sshdr_tmp;
 
-                       if (scsi_normalize_sense(sense, sizeof(*sense),
+                       if (scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE,
                                                 sshdr)
                            && sshdr->sense_key == UNIT_ATTENTION)
                                continue;
@@ -146,7 +159,7 @@ static inline const char *spi_signal_to_string(enum spi_signal_type type)
 {
        int i;
 
-       for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(signal_types); i++) {
                if (type == signal_types[i].value)
                        return signal_types[i].name;
        }
@@ -156,7 +169,7 @@ static inline enum spi_signal_type spi_signal_to_value(const char *name)
 {
        int i, len;
 
-       for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(signal_types); i++) {
                len =  strlen(signal_types[i].name);
                if (strncmp(name, signal_types[i].name, len) == 0 &&
                    (name[len] == '\n' || name[len] == '\0'))
@@ -166,7 +179,7 @@ static inline enum spi_signal_type spi_signal_to_value(const char *name)
 }
 
 static int spi_host_setup(struct transport_container *tc, struct device *dev,
-                         struct class_device *cdev)
+                         struct device *cdev)
 {
        struct Scsi_Host *shost = dev_to_shost(dev);
 
@@ -175,17 +188,20 @@ static int spi_host_setup(struct transport_container *tc, struct device *dev,
        return 0;
 }
 
+static int spi_host_configure(struct transport_container *tc,
+                             struct device *dev,
+                             struct device *cdev);
+
 static DECLARE_TRANSPORT_CLASS(spi_host_class,
                               "spi_host",
                               spi_host_setup,
                               NULL,
-                              NULL);
+                              spi_host_configure);
 
 static int spi_host_match(struct attribute_container *cont,
                          struct device *dev)
 {
        struct Scsi_Host *shost;
-       struct spi_internal *i;
 
        if (!scsi_is_host_device(dev))
                return 0;
@@ -195,17 +211,22 @@ static int spi_host_match(struct attribute_container *cont,
            != &spi_host_class.class)
                return 0;
 
-       i = to_spi_internal(shost->transportt);
-       
-       return &i->t.host_attrs.ac == cont;
+       return &shost->transportt->host_attrs.ac == cont;
 }
 
+static int spi_target_configure(struct transport_container *tc,
+                               struct device *dev,
+                               struct device *cdev);
+
 static int spi_device_configure(struct transport_container *tc,
                                struct device *dev,
-                               struct class_device *cdev)
+                               struct device *cdev)
 {
        struct scsi_device *sdev = to_scsi_device(dev);
        struct scsi_target *starget = sdev->sdev_target;
+       unsigned bflags = scsi_get_device_flags_keyed(sdev, &sdev->inquiry[8],
+                                                     &sdev->inquiry[16],
+                                                     SCSI_DEVINFO_SPI);
 
        /* Populate the target capability fields with the values
         * gleaned from the device inquiry */
@@ -215,6 +236,10 @@ static int spi_device_configure(struct transport_container *tc,
        spi_support_dt(starget) = scsi_device_dt(sdev);
        spi_support_dt_only(starget) = scsi_device_dt_only(sdev);
        spi_support_ius(starget) = scsi_device_ius(sdev);
+       if (bflags & SPI_BLIST_NOIUS) {
+               dev_info(dev, "Information Units disabled by blacklist\n");
+               spi_support_ius(starget) = 0;
+       }
        spi_support_qas(starget) = scsi_device_qas(sdev);
 
        return 0;
@@ -222,7 +247,7 @@ static int spi_device_configure(struct transport_container *tc,
 
 static int spi_setup_transport_attrs(struct transport_container *tc,
                                     struct device *dev,
-                                    struct class_device *cdev)
+                                    struct device *cdev)
 {
        struct scsi_target *starget = to_scsi_target(dev);
 
@@ -233,14 +258,17 @@ static int spi_setup_transport_attrs(struct transport_container *tc,
        spi_width(starget) = 0; /* narrow */
        spi_max_width(starget) = 1;
        spi_iu(starget) = 0;    /* no IU */
+       spi_max_iu(starget) = 1;
        spi_dt(starget) = 0;    /* ST */
        spi_qas(starget) = 0;
+       spi_max_qas(starget) = 1;
        spi_wr_flow(starget) = 0;
        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_dv_in_progress(starget) = 0;
        spi_initial_dv(starget) = 0;
        mutex_init(&spi_dv_mutex(starget));
 
@@ -250,9 +278,10 @@ static int spi_setup_transport_attrs(struct transport_container *tc,
 #define spi_transport_show_simple(field, format_string)                        \
                                                                        \
 static ssize_t                                                         \
-show_spi_transport_##field(struct class_device *cdev, char *buf)       \
+show_spi_transport_##field(struct device *dev,                         \
+                          struct device_attribute *attr, char *buf)    \
 {                                                                      \
-       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct scsi_target *starget = transport_class_to_starget(dev);  \
        struct spi_transport_attrs *tp;                                 \
                                                                        \
        tp = (struct spi_transport_attrs *)&starget->starget_data;      \
@@ -262,11 +291,12 @@ show_spi_transport_##field(struct class_device *cdev, char *buf)  \
 #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)                               \
+store_spi_transport_##field(struct device *dev,                        \
+                           struct device_attribute *attr,              \
+                           const char *buf, size_t count)              \
 {                                                                      \
        int val;                                                        \
-       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct scsi_target *starget = transport_class_to_starget(dev);  \
        struct spi_transport_attrs *tp;                                 \
                                                                        \
        tp = (struct spi_transport_attrs *)&starget->starget_data;      \
@@ -278,9 +308,10 @@ store_spi_transport_##field(struct class_device *cdev, const char *buf, \
 #define spi_transport_show_function(field, format_string)              \
                                                                        \
 static ssize_t                                                         \
-show_spi_transport_##field(struct class_device *cdev, char *buf)       \
+show_spi_transport_##field(struct device *dev,                         \
+                          struct device_attribute *attr, char *buf)    \
 {                                                                      \
-       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct scsi_target *starget = transport_class_to_starget(dev);  \
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
        struct spi_transport_attrs *tp;                                 \
        struct spi_internal *i = to_spi_internal(shost->transportt);    \
@@ -292,31 +323,37 @@ show_spi_transport_##field(struct class_device *cdev, char *buf)  \
 
 #define spi_transport_store_function(field, format_string)             \
 static ssize_t                                                         \
-store_spi_transport_##field(struct class_device *cdev, const char *buf, \
-                           size_t count)                               \
+store_spi_transport_##field(struct device *dev,                        \
+                           struct device_attribute *attr,              \
+                           const char *buf, size_t count)              \
 {                                                                      \
        int val;                                                        \
-       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct scsi_target *starget = transport_class_to_starget(dev);  \
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
        struct spi_internal *i = to_spi_internal(shost->transportt);    \
                                                                        \
+       if (!i->f->set_##field)                                         \
+               return -EINVAL;                                         \
        val = simple_strtoul(buf, NULL, 0);                             \
-       i->f->set_##field(starget, val);                        \
+       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)                               \
+store_spi_transport_##field(struct device *dev,                        \
+                           struct device_attribute *attr,              \
+                           const char *buf, size_t count)              \
 {                                                                      \
        int val;                                                        \
-       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct scsi_target *starget = transport_class_to_starget(dev);  \
        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->set_##field)                                          \
+               return -EINVAL;                                         \
        val = simple_strtoul(buf, NULL, 0);                             \
        if (val > tp->max_##field)                                      \
                val = tp->max_##field;                                  \
@@ -327,55 +364,58 @@ store_spi_transport_##field(struct class_device *cdev, const char *buf, \
 #define spi_transport_rd_attr(field, format_string)                    \
        spi_transport_show_function(field, format_string)               \
        spi_transport_store_function(field, format_string)              \
-static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,                     \
-                        show_spi_transport_##field,                    \
-                        store_spi_transport_##field);
+static DEVICE_ATTR(field, S_IRUGO,                             \
+                  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);
+static DEVICE_ATTR(field, S_IRUGO,                             \
+                  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);
+static DEVICE_ATTR(field, S_IRUGO,                             \
+                  show_spi_transport_##field,                  \
+                  store_spi_transport_##field);
 
 /* The Parallel SCSI Tranport Attributes: */
 spi_transport_max_attr(offset, "%d\n");
 spi_transport_max_attr(width, "%d\n");
-spi_transport_rd_attr(iu, "%d\n");
+spi_transport_max_attr(iu, "%d\n");
 spi_transport_rd_attr(dt, "%d\n");
-spi_transport_rd_attr(qas, "%d\n");
+spi_transport_max_attr(qas, "%d\n");
 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 */
+/* we only care about the first child device that's a real SCSI device
+ * so we return 1 to terminate the iteration when we find it */
 static int child_iter(struct device *dev, void *data)
 {
-       struct scsi_device *sdev = to_scsi_device(dev);
+       if (!scsi_is_sdev_device(dev))
+               return 0;
 
-       spi_dv_device(sdev);
+       spi_dv_device(to_scsi_device(dev));
        return 1;
 }
 
 static ssize_t
-store_spi_revalidate(struct class_device *cdev, const char *buf, size_t count)
+store_spi_revalidate(struct device *dev, struct device_attribute *attr,
+                    const char *buf, size_t count)
 {
-       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct scsi_target *starget = transport_class_to_starget(dev);
 
        device_for_each_child(&starget->dev, NULL, child_iter);
        return count;
 }
-static CLASS_DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate);
+static DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate);
 
 /* Translate the period into ns according to the current spec
  * for SDTR/PPR messages */
@@ -401,8 +441,7 @@ static int period_to_str(char *buf, int period)
 }
 
 static ssize_t
-show_spi_transport_period_helper(struct class_device *cdev, char *buf,
-                                int period)
+show_spi_transport_period_helper(char *buf, int period)
 {
        int len = period_to_str(buf, period);
        buf[len++] = '\n';
@@ -411,7 +450,7 @@ show_spi_transport_period_helper(struct class_device *cdev, char *buf,
 }
 
 static ssize_t
-store_spi_transport_period_helper(struct class_device *cdev, const char *buf,
+store_spi_transport_period_helper(struct device *dev, const char *buf,
                                  size_t count, int *periodp)
 {
        int j, picosec, period = -1;
@@ -448,9 +487,10 @@ store_spi_transport_period_helper(struct class_device *cdev, const char *buf,
 }
 
 static ssize_t
-show_spi_transport_period(struct class_device *cdev, char *buf)
+show_spi_transport_period(struct device *dev,
+                         struct device_attribute *attr, char *buf)
 {
-       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct scsi_target *starget = transport_class_to_starget(dev);
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        struct spi_internal *i = to_spi_internal(shost->transportt);
        struct spi_transport_attrs *tp =
@@ -459,12 +499,12 @@ show_spi_transport_period(struct class_device *cdev, char *buf)
        if (i->f->get_period)
                i->f->get_period(starget);
 
-       return show_spi_transport_period_helper(cdev, buf, tp->period);
+       return show_spi_transport_period_helper(buf, tp->period);
 }
 
 static ssize_t
-store_spi_transport_period(struct class_device *cdev, const char *buf,
-                           size_t count)
+store_spi_transport_period(struct device *cdev, struct device_attribute *attr,
+                          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);
@@ -473,6 +513,9 @@ store_spi_transport_period(struct class_device *cdev, const char *buf,
                (struct spi_transport_attrs *)&starget->starget_data;
        int period, retval;
 
+       if (!i->f->set_period)
+               return -EINVAL;
+
        retval = store_spi_transport_period_helper(cdev, buf, count, &period);
 
        if (period < tp->min_period)
@@ -483,23 +526,30 @@ store_spi_transport_period(struct class_device *cdev, const char *buf,
        return retval;
 }
 
-static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR, 
-                        show_spi_transport_period,
-                        store_spi_transport_period);
+static DEVICE_ATTR(period, S_IRUGO,
+                  show_spi_transport_period,
+                  store_spi_transport_period);
 
 static ssize_t
-show_spi_transport_min_period(struct class_device *cdev, char *buf)
+show_spi_transport_min_period(struct device *cdev,
+                             struct device_attribute *attr, 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;
 
-       return show_spi_transport_period_helper(cdev, buf, tp->min_period);
+       if (!i->f->set_period)
+               return -EINVAL;
+
+       return show_spi_transport_period_helper(buf, tp->min_period);
 }
 
 static ssize_t
-store_spi_transport_min_period(struct class_device *cdev, const char *buf,
-                           size_t count)
+store_spi_transport_min_period(struct device *cdev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
 {
        struct scsi_target *starget = transport_class_to_starget(cdev);
        struct spi_transport_attrs *tp =
@@ -510,12 +560,14 @@ store_spi_transport_min_period(struct class_device *cdev, const char *buf,
 }
 
 
-static CLASS_DEVICE_ATTR(min_period, S_IRUGO | S_IWUSR, 
-                        show_spi_transport_min_period,
-                        store_spi_transport_min_period);
+static DEVICE_ATTR(min_period, S_IRUGO,
+                  show_spi_transport_min_period,
+                  store_spi_transport_min_period);
 
 
-static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf)
+static ssize_t show_spi_host_signalling(struct device *cdev,
+                                       struct device_attribute *attr,
+                                       char *buf)
 {
        struct Scsi_Host *shost = transport_class_to_shost(cdev);
        struct spi_internal *i = to_spi_internal(shost->transportt);
@@ -525,21 +577,25 @@ static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf)
 
        return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost)));
 }
-static ssize_t store_spi_host_signalling(struct class_device *cdev,
+static ssize_t store_spi_host_signalling(struct device *dev,
+                                        struct device_attribute *attr,
                                         const char *buf, size_t count)
 {
-       struct Scsi_Host *shost = transport_class_to_shost(cdev);
+       struct Scsi_Host *shost = transport_class_to_shost(dev);
        struct spi_internal *i = to_spi_internal(shost->transportt);
        enum spi_signal_type type = spi_signal_to_value(buf);
 
+       if (!i->f->set_signalling)
+               return -EINVAL;
+
        if (type != SPI_SIGNAL_UNKNOWN)
                i->f->set_signalling(shost, type);
 
        return count;
 }
-static CLASS_DEVICE_ATTR(signalling, S_IRUGO | S_IWUSR,
-                        show_spi_host_signalling,
-                        store_spi_host_signalling);
+static DEVICE_ATTR(signalling, S_IRUGO,
+                  show_spi_host_signalling,
+                  store_spi_host_signalling);
 
 #define DV_SET(x, y)                   \
        if(i->f->set_##x)               \
@@ -786,11 +842,14 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
 {
        struct spi_internal *i = to_spi_internal(sdev->host->transportt);
        struct scsi_target *starget = sdev->sdev_target;
+       struct Scsi_Host *shost = sdev->host;
        int len = sdev->inquiry_len;
+       int min_period = spi_min_period(starget);
+       int max_width = spi_max_width(starget);
        /* first set us up for narrow async */
        DV_SET(offset, 0);
        DV_SET(width, 0);
-       
+
        if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS)
            != SPI_COMPARE_SUCCESS) {
                starget_printk(KERN_ERR, starget, "Domain Validation Initial Inquiry Failed\n");
@@ -798,9 +857,13 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
                return;
        }
 
+       if (!spi_support_wide(starget)) {
+               spi_max_width(starget) = 0;
+               max_width = 0;
+       }
+
        /* test width */
-       if (i->f->set_width && spi_max_width(starget) &&
-           scsi_device_wide(sdev)) {
+       if (i->f->set_width && max_width) {
                i->f->set_width(starget, 1);
 
                if (spi_dv_device_compare_inquiry(sdev, buffer,
@@ -809,6 +872,11 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
                    != SPI_COMPARE_SUCCESS) {
                        starget_printk(KERN_ERR, starget, "Wide Transfers Fail\n");
                        i->f->set_width(starget, 0);
+                       /* Make sure we don't force wide back on by asking
+                        * for a transfer period that requires it */
+                       max_width = 0;
+                       if (min_period < 10)
+                               min_period = 10;
                }
        }
 
@@ -816,7 +884,7 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
                return;
 
        /* device can't handle synchronous */
-       if (!scsi_device_sync(sdev) && !scsi_device_dt(sdev))
+       if (!spi_support_sync(starget) && !spi_support_dt(starget))
                return;
 
        /* len == -1 is the signal that we need to ascertain the
@@ -828,23 +896,46 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
 
        /* now set up to the maximum */
        DV_SET(offset, spi_max_offset(starget));
-       DV_SET(period, spi_min_period(starget));
+       DV_SET(period, min_period);
+
        /* try QAS requests; this should be harmless to set if the
         * target supports it */
-       if (scsi_device_qas(sdev))
+       if (spi_support_qas(starget) && spi_max_qas(starget)) {
                DV_SET(qas, 1);
-       /* Also try IU transfers */
-       if (scsi_device_ius(sdev))
+       } else {
+               DV_SET(qas, 0);
+       }
+
+       if (spi_support_ius(starget) && spi_max_iu(starget) &&
+           min_period < 9) {
+               /* This u320 (or u640). Set IU transfers */
                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 */
+               /* Then set the optional parameters */
                DV_SET(rd_strm, 1);
                DV_SET(wr_flow, 1);
                DV_SET(rti, 1);
-               if (spi_min_period(starget) == 8)
+               if (min_period == 8)
                        DV_SET(pcomp_en, 1);
+       } else {
+               DV_SET(iu, 0);
+       }
+
+       /* now that we've done all this, actually check the bus
+        * signal type (if known).  Some devices are stupid on
+        * a SE bus and still claim they can try LVD only settings */
+       if (i->f->get_signalling)
+               i->f->get_signalling(shost);
+       if (spi_signalling(shost) == SPI_SIGNAL_SE ||
+           spi_signalling(shost) == SPI_SIGNAL_HVD ||
+           !spi_support_dt(starget)) {
+               DV_SET(dt, 0);
+       } else {
+               DV_SET(dt, 1);
        }
+       /* set width last because it will pull all the other
+        * parameters down to required values */
+       DV_SET(width, max_width);
+
        /* Do the read only INQUIRY tests */
        spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len,
                       spi_dv_device_compare_inquiry);
@@ -900,6 +991,10 @@ spi_dv_device(struct scsi_device *sdev)
        if (unlikely(scsi_device_get(sdev)))
                return;
 
+       if (unlikely(spi_dv_in_progress(starget)))
+               return;
+       spi_dv_in_progress(starget) = 1;
+
        buffer = kzalloc(len, GFP_KERNEL);
 
        if (unlikely(!buffer))
@@ -931,6 +1026,7 @@ spi_dv_device(struct scsi_device *sdev)
  out_free:
        kfree(buffer);
  out_put:
+       spi_dv_in_progress(starget) = 0;
        scsi_device_put(sdev);
 }
 EXPORT_SYMBOL(spi_dv_device);
@@ -941,9 +1037,10 @@ struct work_queue_wrapper {
 };
 
 static void
-spi_dv_device_work_wrapper(void *data)
+spi_dv_device_work_wrapper(struct work_struct *work)
 {
-       struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data;
+       struct work_queue_wrapper *wqw =
+               container_of(work, struct work_queue_wrapper, work);
        struct scsi_device *sdev = wqw->sdev;
 
        kfree(wqw);
@@ -983,7 +1080,7 @@ spi_schedule_dv_device(struct scsi_device *sdev)
                return;
        }
 
-       INIT_WORK(&wqw->work, spi_dv_device_work_wrapper, wqw);
+       INIT_WORK(&wqw->work, spi_dv_device_work_wrapper);
        wqw->sdev = sdev;
 
        schedule_work(&wqw->work);
@@ -1052,25 +1149,63 @@ void spi_display_xfer_agreement(struct scsi_target *starget)
 }
 EXPORT_SYMBOL(spi_display_xfer_agreement);
 
+int spi_populate_width_msg(unsigned char *msg, int width)
+{
+       msg[0] = EXTENDED_MESSAGE;
+       msg[1] = 2;
+       msg[2] = EXTENDED_WDTR;
+       msg[3] = width;
+       return 4;
+}
+EXPORT_SYMBOL_GPL(spi_populate_width_msg);
+
+int spi_populate_sync_msg(unsigned char *msg, int period, int offset)
+{
+       msg[0] = EXTENDED_MESSAGE;
+       msg[1] = 3;
+       msg[2] = EXTENDED_SDTR;
+       msg[3] = period;
+       msg[4] = offset;
+       return 5;
+}
+EXPORT_SYMBOL_GPL(spi_populate_sync_msg);
+
+int spi_populate_ppr_msg(unsigned char *msg, int period, int offset,
+               int width, int options)
+{
+       msg[0] = EXTENDED_MESSAGE;
+       msg[1] = 6;
+       msg[2] = EXTENDED_PPR;
+       msg[3] = period;
+       msg[4] = 0;
+       msg[5] = offset;
+       msg[6] = width;
+       msg[7] = options;
+       return 8;
+}
+EXPORT_SYMBOL_GPL(spi_populate_ppr_msg);
+
 #ifdef CONFIG_SCSI_CONSTANTS
 static const char * const one_byte_msgs[] = {
-/* 0x00 */ "Command Complete", NULL, "Save Pointers",
+/* 0x00 */ "Task Complete", NULL /* Extended Message */, "Save Pointers",
 /* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error", 
-/* 0x06 */ "Abort", "Message Reject", "Nop", "Message Parity Error",
+/* 0x06 */ "Abort Task Set", "Message Reject", "Nop", "Message Parity Error",
 /* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
-/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue", 
-/* 0x0f */ "Initiate Recovery", "Release Recovery"
+/* 0x0c */ "Target Reset", "Abort Task", "Clear Task Set", 
+/* 0x0f */ "Initiate Recovery", "Release Recovery",
+/* 0x11 */ "Terminate Process", "Continue Task", "Target Transfer Disable",
+/* 0x14 */ NULL, NULL, "Clear ACA", "LUN Reset"
 };
 
 static const char * const two_byte_msgs[] = {
 /* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag",
-/* 0x23 */ "Ignore Wide Residue"
+/* 0x23 */ "Ignore Wide Residue", "ACA"
 };
 
 static const char * const extended_msgs[] = {
 /* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
 /* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request",
-/* 0x04 */ "Parallel Protocol Request"
+/* 0x04 */ "Parallel Protocol Request", "Modify Bidirectional Data Pointer"
 };
 
 static void print_nego(const unsigned char *msg, int per, int off, int width)
@@ -1087,11 +1222,20 @@ static void print_nego(const unsigned char *msg, int per, int off, int width)
                printk("width = %d ", 8 << msg[width]);
 }
 
+static void print_ptr(const unsigned char *msg, int msb, const char *desc)
+{
+       int ptr = (msg[msb] << 24) | (msg[msb+1] << 16) | (msg[msb+2] << 8) |
+                       msg[msb+3];
+       printk("%s = %d ", desc, ptr);
+}
+
 int spi_print_msg(const unsigned char *msg)
 {
-       int len = 0, i;
+       int len = 1, i;
        if (msg[0] == EXTENDED_MESSAGE) {
-               len = 3 + msg[1];
+               len = 2 + msg[1];
+               if (len == 2)
+                       len += 256;
                if (msg[2] < ARRAY_SIZE(extended_msgs))
                        printk ("%s ", extended_msgs[msg[2]]); 
                else 
@@ -1099,8 +1243,7 @@ int spi_print_msg(const unsigned char *msg)
                                (int) msg[2]);
                switch (msg[2]) {
                case EXTENDED_MODIFY_DATA_POINTER:
-                       printk("pointer = %d", (int) (msg[3] << 24) |
-                               (msg[4] << 16) | (msg[5] << 8) | msg[6]);
+                       print_ptr(msg, 3, "pointer");
                        break;
                case EXTENDED_SDTR:
                        print_nego(msg, 3, 4, 0);
@@ -1111,6 +1254,10 @@ int spi_print_msg(const unsigned char *msg)
                case EXTENDED_PPR:
                        print_nego(msg, 3, 5, 6);
                        break;
+               case EXTENDED_MODIFY_BIDI_DATA_PTR:
+                       print_ptr(msg, 3, "out");
+                       print_ptr(msg, 7, "in");
+                       break;
                default:
                for (i = 2; i < len; ++i) 
                        printk("%02x ", msg[i]);
@@ -1121,14 +1268,14 @@ int spi_print_msg(const unsigned char *msg)
                        (msg[0] & 0x40) ? "" : "not ",
                        (msg[0] & 0x20) ? "target routine" : "lun",
                        msg[0] & 0x7);
-               len = 1;
        /* Normal One byte */
        } else if (msg[0] < 0x1f) {
-               if (msg[0] < ARRAY_SIZE(one_byte_msgs))
-                       printk(one_byte_msgs[msg[0]]);
+               if (msg[0] < ARRAY_SIZE(one_byte_msgs) && one_byte_msgs[msg[0]])
+                       printk("%s ", one_byte_msgs[msg[0]]);
                else
                        printk("reserved (%02x) ", msg[0]);
-               len = 1;
+       } else if (msg[0] == 0x55) {
+               printk("QAS Request ");
        /* Two byte */
        } else if (msg[0] <= 0x2f) {
                if ((msg[0] - 0x20) < ARRAY_SIZE(two_byte_msgs))
@@ -1139,7 +1286,7 @@ int spi_print_msg(const unsigned char *msg)
                                msg[0], msg[1]);
                len = 2;
        } else 
-               printk("reserved");
+               printk("reserved ");
        return len;
 }
 EXPORT_SYMBOL(spi_print_msg);
@@ -1148,20 +1295,20 @@ EXPORT_SYMBOL(spi_print_msg);
 
 int spi_print_msg(const unsigned char *msg)
 {
-       int len = 0, i;
+       int len = 1, i;
 
        if (msg[0] == EXTENDED_MESSAGE) {
-               len = 3 + msg[1];
+               len = 2 + msg[1];
+               if (len == 2)
+                       len += 256;
                for (i = 0; i < len; ++i)
                        printk("%02x ", msg[i]);
        /* Identify */
        } else if (msg[0] & 0x80) {
                printk("%02x ", msg[0]);
-               len = 1;
        /* Normal One byte */
-       } else if (msg[0] < 0x1f) {
+       } else if ((msg[0] < 0x1f) || (msg[0] == 0x55)) {
                printk("%02x ", msg[0]);
-               len = 1;
        /* Two byte */
        } else if (msg[0] <= 0x2f) {
                printk("%02x %02x", msg[0], msg[1]);
@@ -1173,35 +1320,6 @@ int spi_print_msg(const unsigned char *msg)
 EXPORT_SYMBOL(spi_print_msg);
 #endif /* ! CONFIG_SCSI_CONSTANTS */
 
-#define SETUP_ATTRIBUTE(field)                                         \
-       i->private_attrs[count] = class_device_attr_##field;            \
-       if (!i->f->set_##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_##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) {                                       \
-               i->private_host_attrs[count].attr.mode = S_IRUGO;       \
-               i->private_host_attrs[count].store = NULL;              \
-       }                                                               \
-       i->host_attrs[count] = &i->private_host_attrs[count];           \
-       count++
-
 static int spi_device_match(struct attribute_container *cont,
                            struct device *dev)
 {
@@ -1254,16 +1372,147 @@ static DECLARE_TRANSPORT_CLASS(spi_transport_class,
                               "spi_transport",
                               spi_setup_transport_attrs,
                               NULL,
-                              NULL);
+                              spi_target_configure);
 
 static DECLARE_ANON_TRANSPORT_CLASS(spi_device_class,
                                    spi_device_match,
                                    spi_device_configure);
 
+static struct attribute *host_attributes[] = {
+       &dev_attr_signalling.attr,
+       NULL
+};
+
+static struct attribute_group host_attribute_group = {
+       .attrs = host_attributes,
+};
+
+static int spi_host_configure(struct transport_container *tc,
+                             struct device *dev,
+                             struct device *cdev)
+{
+       struct kobject *kobj = &cdev->kobj;
+       struct Scsi_Host *shost = transport_class_to_shost(cdev);
+       struct spi_internal *si = to_spi_internal(shost->transportt);
+       struct attribute *attr = &dev_attr_signalling.attr;
+       int rc = 0;
+
+       if (si->f->set_signalling)
+               rc = sysfs_chmod_file(kobj, attr, attr->mode | S_IWUSR);
+
+       return rc;
+}
+
+/* returns true if we should be showing the variable.  Also
+ * overloads the return by setting 1<<1 if the attribute should
+ * be writeable */
+#define TARGET_ATTRIBUTE_HELPER(name) \
+       (si->f->show_##name ? S_IRUGO : 0) | \
+       (si->f->set_##name ? S_IWUSR : 0)
+
+static mode_t target_attribute_is_visible(struct kobject *kobj,
+                                         struct attribute *attr, int i)
+{
+       struct device *cdev = container_of(kobj, struct device, kobj);
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = transport_class_to_shost(cdev);
+       struct spi_internal *si = to_spi_internal(shost->transportt);
+
+       if (attr == &dev_attr_period.attr &&
+           spi_support_sync(starget))
+               return TARGET_ATTRIBUTE_HELPER(period);
+       else if (attr == &dev_attr_min_period.attr &&
+                spi_support_sync(starget))
+               return TARGET_ATTRIBUTE_HELPER(period);
+       else if (attr == &dev_attr_offset.attr &&
+                spi_support_sync(starget))
+               return TARGET_ATTRIBUTE_HELPER(offset);
+       else if (attr == &dev_attr_max_offset.attr &&
+                spi_support_sync(starget))
+               return TARGET_ATTRIBUTE_HELPER(offset);
+       else if (attr == &dev_attr_width.attr &&
+                spi_support_wide(starget))
+               return TARGET_ATTRIBUTE_HELPER(width);
+       else if (attr == &dev_attr_max_width.attr &&
+                spi_support_wide(starget))
+               return TARGET_ATTRIBUTE_HELPER(width);
+       else if (attr == &dev_attr_iu.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(iu);
+       else if (attr == &dev_attr_max_iu.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(iu);
+       else if (attr == &dev_attr_dt.attr &&
+                spi_support_dt(starget))
+               return TARGET_ATTRIBUTE_HELPER(dt);
+       else if (attr == &dev_attr_qas.attr &&
+                spi_support_qas(starget))
+               return TARGET_ATTRIBUTE_HELPER(qas);
+       else if (attr == &dev_attr_max_qas.attr &&
+                spi_support_qas(starget))
+               return TARGET_ATTRIBUTE_HELPER(qas);
+       else if (attr == &dev_attr_wr_flow.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(wr_flow);
+       else if (attr == &dev_attr_rd_strm.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(rd_strm);
+       else if (attr == &dev_attr_rti.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(rti);
+       else if (attr == &dev_attr_pcomp_en.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(pcomp_en);
+       else if (attr == &dev_attr_hold_mcs.attr &&
+                spi_support_ius(starget))
+               return TARGET_ATTRIBUTE_HELPER(hold_mcs);
+       else if (attr == &dev_attr_revalidate.attr)
+               return S_IWUSR;
+
+       return 0;
+}
+
+static struct attribute *target_attributes[] = {
+       &dev_attr_period.attr,
+       &dev_attr_min_period.attr,
+       &dev_attr_offset.attr,
+       &dev_attr_max_offset.attr,
+       &dev_attr_width.attr,
+       &dev_attr_max_width.attr,
+       &dev_attr_iu.attr,
+       &dev_attr_max_iu.attr,
+       &dev_attr_dt.attr,
+       &dev_attr_qas.attr,
+       &dev_attr_max_qas.attr,
+       &dev_attr_wr_flow.attr,
+       &dev_attr_rd_strm.attr,
+       &dev_attr_rti.attr,
+       &dev_attr_pcomp_en.attr,
+       &dev_attr_hold_mcs.attr,
+       &dev_attr_revalidate.attr,
+       NULL
+};
+
+static struct attribute_group target_attribute_group = {
+       .attrs = target_attributes,
+       .is_visible = target_attribute_is_visible,
+};
+
+static int spi_target_configure(struct transport_container *tc,
+                               struct device *dev,
+                               struct device *cdev)
+{
+       struct kobject *kobj = &cdev->kobj;
+
+       /* force an update based on parameters read from the device */
+       sysfs_update_group(kobj, &target_attribute_group);
+
+       return 0;
+}
+
 struct scsi_transport_template *
 spi_attach_transport(struct spi_function_template *ft)
 {
-       int count = 0;
        struct spi_internal *i = kzalloc(sizeof(struct spi_internal),
                                         GFP_KERNEL);
 
@@ -1271,47 +1520,17 @@ spi_attach_transport(struct spi_function_template *ft)
                return NULL;
 
        i->t.target_attrs.ac.class = &spi_transport_class.class;
-       i->t.target_attrs.ac.attrs = &i->attrs[0];
+       i->t.target_attrs.ac.grp = &target_attribute_group;
        i->t.target_attrs.ac.match = spi_target_match;
        transport_container_register(&i->t.target_attrs);
        i->t.target_size = sizeof(struct spi_transport_attrs);
        i->t.host_attrs.ac.class = &spi_host_class.class;
-       i->t.host_attrs.ac.attrs = &i->host_attrs[0];
+       i->t.host_attrs.ac.grp = &host_attribute_group;
        i->t.host_attrs.ac.match = spi_host_match;
        transport_container_register(&i->t.host_attrs);
        i->t.host_size = sizeof(struct spi_host_attrs);
        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);
-       SETUP_ATTRIBUTE(wr_flow);
-       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 */
-       BUG_ON(count > SPI_NUM_ATTRS);
-
-       i->attrs[count++] = &class_device_attr_revalidate;
-
-       i->attrs[count] = NULL;
-
-       count = 0;
-       SETUP_HOST_ATTRIBUTE(signalling);
-
-       BUG_ON(count > SPI_HOST_ATTRS);
-
-       i->host_attrs[count] = NULL;
-
        return &i->t;
 }
 EXPORT_SYMBOL(spi_attach_transport);
@@ -1329,7 +1548,21 @@ EXPORT_SYMBOL(spi_release_transport);
 
 static __init int spi_transport_init(void)
 {
-       int error = transport_class_register(&spi_transport_class);
+       int error = scsi_dev_info_add_list(SCSI_DEVINFO_SPI,
+                                          "SCSI Parallel Transport Class");
+       if (!error) {
+               int i;
+
+               for (i = 0; spi_static_device_list[i].vendor; i++)
+                       scsi_dev_info_list_add_keyed(1, /* compatible */
+                                                    spi_static_device_list[i].vendor,
+                                                    spi_static_device_list[i].model,
+                                                    NULL,
+                                                    spi_static_device_list[i].flags,
+                                                    SCSI_DEVINFO_SPI);
+       }
+
+       error = transport_class_register(&spi_transport_class);
        if (error)
                return error;
        error = anon_transport_class_register(&spi_device_class);
@@ -1341,6 +1574,7 @@ static void __exit spi_transport_exit(void)
        transport_class_unregister(&spi_transport_class);
        anon_transport_class_unregister(&spi_device_class);
        transport_class_unregister(&spi_host_class);
+       scsi_dev_info_remove_list(SCSI_DEVINFO_SPI);
 }
 
 MODULE_AUTHOR("Martin Hicks");