+/* only need 8 bytes of data plus header (4 or 8) */
+#define BUF_SIZE 64
+
+int sas_read_port_mode_page(struct scsi_device *sdev)
+{
+ char *buffer = kzalloc(BUF_SIZE, GFP_KERNEL), *msdata;
+ struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target);
+ struct sas_end_device *rdev;
+ struct scsi_mode_data mode_data;
+ int res, error;
+
+ BUG_ON(rphy->identify.device_type != SAS_END_DEVICE);
+
+ rdev = rphy_to_end_device(rphy);
+
+ if (!buffer)
+ return -ENOMEM;
+
+ res = scsi_mode_sense(sdev, 1, 0x19, buffer, BUF_SIZE, 30*HZ, 3,
+ &mode_data, NULL);
+
+ error = -EINVAL;
+ if (!scsi_status_is_good(res))
+ goto out;
+
+ msdata = buffer + mode_data.header_length +
+ mode_data.block_descriptor_length;
+
+ if (msdata - buffer > BUF_SIZE - 8)
+ goto out;
+
+ error = 0;
+
+ rdev->ready_led_meaning = msdata[2] & 0x10 ? 1 : 0;
+ rdev->I_T_nexus_loss_timeout = (msdata[4] << 8) + msdata[5];
+ rdev->initiator_response_timeout = (msdata[6] << 8) + msdata[7];
+
+ out:
+ kfree(buffer);
+ return error;
+}
+EXPORT_SYMBOL(sas_read_port_mode_page);
+
+static DECLARE_TRANSPORT_CLASS(sas_end_dev_class,
+ "sas_end_device", NULL, NULL, NULL);
+
+#define sas_end_dev_show_simple(field, name, format_string, cast) \
+static ssize_t \
+show_sas_end_dev_##name(struct class_device *cdev, char *buf) \
+{ \
+ struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
+ struct sas_end_device *rdev = rphy_to_end_device(rphy); \
+ \
+ return snprintf(buf, 20, format_string, cast rdev->field); \
+}
+
+#define sas_end_dev_simple_attr(field, name, format_string, type) \
+ sas_end_dev_show_simple(field, name, format_string, (type)) \
+static SAS_CLASS_DEVICE_ATTR(end_dev, name, S_IRUGO, \
+ show_sas_end_dev_##name, NULL)
+
+sas_end_dev_simple_attr(ready_led_meaning, ready_led_meaning, "%d\n", int);
+sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout,
+ "%d\n", int);
+sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout,
+ "%d\n", int);
+
+static DECLARE_TRANSPORT_CLASS(sas_expander_class,
+ "sas_expander", NULL, NULL, NULL);
+
+#define sas_expander_show_simple(field, name, format_string, cast) \
+static ssize_t \
+show_sas_expander_##name(struct class_device *cdev, char *buf) \
+{ \
+ struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
+ struct sas_expander_device *edev = rphy_to_expander_device(rphy); \
+ \
+ return snprintf(buf, 20, format_string, cast edev->field); \
+}
+
+#define sas_expander_simple_attr(field, name, format_string, type) \
+ sas_expander_show_simple(field, name, format_string, (type)) \
+static SAS_CLASS_DEVICE_ATTR(expander, name, S_IRUGO, \
+ show_sas_expander_##name, NULL)
+
+sas_expander_simple_attr(vendor_id, vendor_id, "%s\n", char *);
+sas_expander_simple_attr(product_id, product_id, "%s\n", char *);
+sas_expander_simple_attr(product_rev, product_rev, "%s\n", char *);
+sas_expander_simple_attr(component_vendor_id, component_vendor_id,
+ "%s\n", char *);
+sas_expander_simple_attr(component_id, component_id, "%u\n", unsigned int);
+sas_expander_simple_attr(component_revision_id, component_revision_id, "%u\n",
+ unsigned int);
+sas_expander_simple_attr(level, level, "%d\n", int);
+