Merge branch 'iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[cascardo/linux.git] / drivers / scsi / ses.c
index 7dd9cf5..dcb0d76 100644 (file)
@@ -47,7 +47,6 @@ struct ses_device {
 
 struct ses_component {
        u64 addr;
-       unsigned char *desc;
 };
 
 static int ses_probe(struct device *dev)
@@ -68,6 +67,20 @@ static int ses_probe(struct device *dev)
 #define SES_TIMEOUT (30 * HZ)
 #define SES_RETRIES 3
 
+static void init_device_slot_control(unsigned char *dest_desc,
+                                    struct enclosure_component *ecomp,
+                                    unsigned char *status)
+{
+       memcpy(dest_desc, status, 4);
+       dest_desc[0] = 0;
+       /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */
+       if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE)
+               dest_desc[1] = 0;
+       dest_desc[2] &= 0xde;
+       dest_desc[3] &= 0x3c;
+}
+
+
 static int ses_recv_diag(struct scsi_device *sdev, int page_code,
                         void *buf, int bufflen)
 {
@@ -179,14 +192,22 @@ static int ses_set_fault(struct enclosure_device *edev,
                          struct enclosure_component *ecomp,
                         enum enclosure_component_setting val)
 {
-       unsigned char desc[4] = {0 };
+       unsigned char desc[4];
+       unsigned char *desc_ptr;
+
+       desc_ptr = ses_get_page2_descriptor(edev, ecomp);
+
+       if (!desc_ptr)
+               return -EIO;
+
+       init_device_slot_control(desc, ecomp, desc_ptr);
 
        switch (val) {
        case ENCLOSURE_SETTING_DISABLED:
-               /* zero is disabled */
+               desc[3] &= 0xdf;
                break;
        case ENCLOSURE_SETTING_ENABLED:
-               desc[3] = 0x20;
+               desc[3] |= 0x20;
                break;
        default:
                /* SES doesn't do the SGPIO blink settings */
@@ -220,14 +241,22 @@ static int ses_set_locate(struct enclosure_device *edev,
                          struct enclosure_component *ecomp,
                          enum enclosure_component_setting val)
 {
-       unsigned char desc[4] = {0 };
+       unsigned char desc[4];
+       unsigned char *desc_ptr;
+
+       desc_ptr = ses_get_page2_descriptor(edev, ecomp);
+
+       if (!desc_ptr)
+               return -EIO;
+
+       init_device_slot_control(desc, ecomp, desc_ptr);
 
        switch (val) {
        case ENCLOSURE_SETTING_DISABLED:
-               /* zero is disabled */
+               desc[2] &= 0xfd;
                break;
        case ENCLOSURE_SETTING_ENABLED:
-               desc[2] = 0x02;
+               desc[2] |= 0x02;
                break;
        default:
                /* SES doesn't do the SGPIO blink settings */
@@ -240,15 +269,23 @@ static int ses_set_active(struct enclosure_device *edev,
                          struct enclosure_component *ecomp,
                          enum enclosure_component_setting val)
 {
-       unsigned char desc[4] = {0 };
+       unsigned char desc[4];
+       unsigned char *desc_ptr;
+
+       desc_ptr = ses_get_page2_descriptor(edev, ecomp);
+
+       if (!desc_ptr)
+               return -EIO;
+
+       init_device_slot_control(desc, ecomp, desc_ptr);
 
        switch (val) {
        case ENCLOSURE_SETTING_DISABLED:
-               /* zero is disabled */
+               desc[2] &= 0x7f;
                ecomp->active = 0;
                break;
        case ENCLOSURE_SETTING_ENABLED:
-               desc[2] = 0x80;
+               desc[2] |= 0x80;
                ecomp->active = 1;
                break;
        default:
@@ -258,13 +295,63 @@ static int ses_set_active(struct enclosure_device *edev,
        return ses_set_page2_descriptor(edev, ecomp, desc);
 }
 
+static int ses_show_id(struct enclosure_device *edev, char *buf)
+{
+       struct ses_device *ses_dev = edev->scratch;
+       unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4);
+
+       return sprintf(buf, "%#llx\n", id);
+}
+
+static void ses_get_power_status(struct enclosure_device *edev,
+                                struct enclosure_component *ecomp)
+{
+       unsigned char *desc;
+
+       desc = ses_get_page2_descriptor(edev, ecomp);
+       if (desc)
+               ecomp->power_status = (desc[3] & 0x10) ? 0 : 1;
+}
+
+static int ses_set_power_status(struct enclosure_device *edev,
+                               struct enclosure_component *ecomp,
+                               int val)
+{
+       unsigned char desc[4];
+       unsigned char *desc_ptr;
+
+       desc_ptr = ses_get_page2_descriptor(edev, ecomp);
+
+       if (!desc_ptr)
+               return -EIO;
+
+       init_device_slot_control(desc, ecomp, desc_ptr);
+
+       switch (val) {
+       /* power = 1 is device_off = 0 and vice versa */
+       case 0:
+               desc[3] |= 0x10;
+               break;
+       case 1:
+               desc[3] &= 0xef;
+               break;
+       default:
+               return -EINVAL;
+       }
+       ecomp->power_status = val;
+       return ses_set_page2_descriptor(edev, ecomp, desc);
+}
+
 static struct enclosure_component_callbacks ses_enclosure_callbacks = {
        .get_fault              = ses_get_fault,
        .set_fault              = ses_set_fault,
        .get_status             = ses_get_status,
        .get_locate             = ses_get_locate,
        .set_locate             = ses_set_locate,
+       .get_power_status       = ses_get_power_status,
+       .set_power_status       = ses_set_power_status,
        .set_active             = ses_set_active,
+       .show_id                = ses_show_id,
 };
 
 struct ses_host_edev {
@@ -298,19 +385,26 @@ static void ses_process_descriptor(struct enclosure_component *ecomp,
        int invalid = desc[0] & 0x80;
        enum scsi_protocol proto = desc[0] & 0x0f;
        u64 addr = 0;
+       int slot = -1;
        struct ses_component *scomp = ecomp->scratch;
        unsigned char *d;
 
-       scomp->desc = desc;
-
        if (invalid)
                return;
 
        switch (proto) {
+       case SCSI_PROTOCOL_FCP:
+               if (eip) {
+                       d = desc + 4;
+                       slot = d[3];
+               }
+               break;
        case SCSI_PROTOCOL_SAS:
-               if (eip)
+               if (eip) {
+                       d = desc + 4;
+                       slot = d[3];
                        d = desc + 8;
-               else
+               else
                        d = desc + 4;
                /* only take the phy0 addr */
                addr = (u64)d[12] << 56 |
@@ -326,6 +420,7 @@ static void ses_process_descriptor(struct enclosure_component *ecomp,
                /* FIXME: Need to add more protocols than just SAS */
                break;
        }
+       ecomp->slot = slot;
        scomp->addr = addr;
 }
 
@@ -349,7 +444,8 @@ static int ses_enclosure_find_by_addr(struct enclosure_device *edev,
                if (scomp->addr != efd->addr)
                        continue;
 
-               enclosure_add_device(edev, i, efd->dev);
+               if (enclosure_add_device(edev, i, efd->dev) == 0)
+                       kobject_uevent(&efd->dev->kobj, KOBJ_CHANGE);
                return 1;
        }
        return 0;
@@ -432,6 +528,7 @@ static void ses_enclosure_data_process(struct enclosure_device *edev,
                                        ecomp = &edev->component[components++];
 
                                if (!IS_ERR(ecomp)) {
+                                       ses_get_power_status(edev, ecomp);
                                        if (addl_desc_ptr)
                                                ses_process_descriptor(
                                                        ecomp,