cxgb{3,4}*: improve Kconfig dependencies
[linux-2.6.git] / drivers / scsi / scsi_transport_spi.c
index 6f56f87..8a172d4 100644 (file)
@@ -24,6 +24,8 @@
 #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_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)
@@ -115,8 +126,11 @@ 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;
@@ -165,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);
 
@@ -174,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;
@@ -194,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 */
@@ -214,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;
@@ -221,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);
 
@@ -232,8 +258,10 @@ 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;
@@ -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 */
@@ -410,7 +450,7 @@ show_spi_transport_period_helper(char *buf, int period)
 }
 
 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;
@@ -447,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 =
@@ -462,8 +503,8 @@ 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(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);
@@ -472,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)
@@ -482,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;
 
+       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 =
@@ -509,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);
@@ -524,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)               \
@@ -787,10 +844,12 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
        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,25 @@ 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);
        } else {
                DV_SET(qas, 0);
        }
 
-       if (scsi_device_ius(sdev) && spi_min_period(starget) < 9) {
+       if (spi_support_ius(starget) && spi_max_iu(starget) &&
+           min_period < 9) {
                /* This u320 (or u640). Set IU transfers */
                DV_SET(iu, 1);
                /* 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);
@@ -857,11 +927,15 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer)
                i->f->get_signalling(shost);
        if (spi_signalling(shost) == SPI_SIGNAL_SE ||
            spi_signalling(shost) == SPI_SIGNAL_HVD ||
-           !scsi_device_dt(sdev)) {
+           !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);
@@ -1246,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)
 {
@@ -1327,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);
 
@@ -1344,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);
@@ -1402,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);
@@ -1414,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");