162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCSI Enclosure Services 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/enclosure.h> 1262306a36Sopenharmony_ci#include <asm/unaligned.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <scsi/scsi.h> 1562306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 1662306a36Sopenharmony_ci#include <scsi/scsi_dbg.h> 1762306a36Sopenharmony_ci#include <scsi/scsi_device.h> 1862306a36Sopenharmony_ci#include <scsi/scsi_driver.h> 1962306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <scsi/scsi_transport_sas.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct ses_device { 2462306a36Sopenharmony_ci unsigned char *page1; 2562306a36Sopenharmony_ci unsigned char *page1_types; 2662306a36Sopenharmony_ci unsigned char *page2; 2762306a36Sopenharmony_ci unsigned char *page10; 2862306a36Sopenharmony_ci short page1_len; 2962306a36Sopenharmony_ci short page1_num_types; 3062306a36Sopenharmony_ci short page2_len; 3162306a36Sopenharmony_ci short page10_len; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct ses_component { 3562306a36Sopenharmony_ci u64 addr; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic bool ses_page2_supported(struct enclosure_device *edev) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct ses_device *ses_dev = edev->scratch; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return (ses_dev->page2 != NULL); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int ses_probe(struct device *dev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev); 4862306a36Sopenharmony_ci int err = -ENODEV; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (sdev->type != TYPE_ENCLOSURE) 5162306a36Sopenharmony_ci goto out; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci err = 0; 5462306a36Sopenharmony_ci sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci out: 5762306a36Sopenharmony_ci return err; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define SES_TIMEOUT (30 * HZ) 6162306a36Sopenharmony_ci#define SES_RETRIES 3 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void init_device_slot_control(unsigned char *dest_desc, 6462306a36Sopenharmony_ci struct enclosure_component *ecomp, 6562306a36Sopenharmony_ci unsigned char *status) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci memcpy(dest_desc, status, 4); 6862306a36Sopenharmony_ci dest_desc[0] = 0; 6962306a36Sopenharmony_ci /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */ 7062306a36Sopenharmony_ci if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE) 7162306a36Sopenharmony_ci dest_desc[1] = 0; 7262306a36Sopenharmony_ci dest_desc[2] &= 0xde; 7362306a36Sopenharmony_ci dest_desc[3] &= 0x3c; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int ses_recv_diag(struct scsi_device *sdev, int page_code, 7862306a36Sopenharmony_ci void *buf, int bufflen) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci unsigned char cmd[] = { 8262306a36Sopenharmony_ci RECEIVE_DIAGNOSTIC, 8362306a36Sopenharmony_ci 1, /* Set PCV bit */ 8462306a36Sopenharmony_ci page_code, 8562306a36Sopenharmony_ci bufflen >> 8, 8662306a36Sopenharmony_ci bufflen & 0xff, 8762306a36Sopenharmony_ci 0 8862306a36Sopenharmony_ci }; 8962306a36Sopenharmony_ci unsigned char recv_page_code; 9062306a36Sopenharmony_ci unsigned int retries = SES_RETRIES; 9162306a36Sopenharmony_ci struct scsi_sense_hdr sshdr; 9262306a36Sopenharmony_ci const struct scsi_exec_args exec_args = { 9362306a36Sopenharmony_ci .sshdr = &sshdr, 9462306a36Sopenharmony_ci }; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci do { 9762306a36Sopenharmony_ci ret = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buf, bufflen, 9862306a36Sopenharmony_ci SES_TIMEOUT, 1, &exec_args); 9962306a36Sopenharmony_ci } while (ret > 0 && --retries && scsi_sense_valid(&sshdr) && 10062306a36Sopenharmony_ci (sshdr.sense_key == NOT_READY || 10162306a36Sopenharmony_ci (sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29))); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (unlikely(ret)) 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci recv_page_code = ((unsigned char *)buf)[0]; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (likely(recv_page_code == page_code)) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* successful diagnostic but wrong page code. This happens to some 11262306a36Sopenharmony_ci * USB devices, just print a message and pretend there was an error */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, 11562306a36Sopenharmony_ci "Wrong diagnostic page; asked for %d got %u\n", 11662306a36Sopenharmony_ci page_code, recv_page_code); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int ses_send_diag(struct scsi_device *sdev, int page_code, 12262306a36Sopenharmony_ci void *buf, int bufflen) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int result; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci unsigned char cmd[] = { 12762306a36Sopenharmony_ci SEND_DIAGNOSTIC, 12862306a36Sopenharmony_ci 0x10, /* Set PF bit */ 12962306a36Sopenharmony_ci 0, 13062306a36Sopenharmony_ci bufflen >> 8, 13162306a36Sopenharmony_ci bufflen & 0xff, 13262306a36Sopenharmony_ci 0 13362306a36Sopenharmony_ci }; 13462306a36Sopenharmony_ci struct scsi_sense_hdr sshdr; 13562306a36Sopenharmony_ci unsigned int retries = SES_RETRIES; 13662306a36Sopenharmony_ci const struct scsi_exec_args exec_args = { 13762306a36Sopenharmony_ci .sshdr = &sshdr, 13862306a36Sopenharmony_ci }; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci do { 14162306a36Sopenharmony_ci result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_OUT, buf, 14262306a36Sopenharmony_ci bufflen, SES_TIMEOUT, 1, &exec_args); 14362306a36Sopenharmony_ci } while (result > 0 && --retries && scsi_sense_valid(&sshdr) && 14462306a36Sopenharmony_ci (sshdr.sense_key == NOT_READY || 14562306a36Sopenharmony_ci (sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29))); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (result) 14862306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n", 14962306a36Sopenharmony_ci result); 15062306a36Sopenharmony_ci return result; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int ses_set_page2_descriptor(struct enclosure_device *edev, 15462306a36Sopenharmony_ci struct enclosure_component *ecomp, 15562306a36Sopenharmony_ci unsigned char *desc) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int i, j, count = 0, descriptor = ecomp->number; 15862306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(edev->edev.parent); 15962306a36Sopenharmony_ci struct ses_device *ses_dev = edev->scratch; 16062306a36Sopenharmony_ci unsigned char *type_ptr = ses_dev->page1_types; 16162306a36Sopenharmony_ci unsigned char *desc_ptr = ses_dev->page2 + 8; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Clear everything */ 16462306a36Sopenharmony_ci memset(desc_ptr, 0, ses_dev->page2_len - 8); 16562306a36Sopenharmony_ci for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { 16662306a36Sopenharmony_ci for (j = 0; j < type_ptr[1]; j++) { 16762306a36Sopenharmony_ci desc_ptr += 4; 16862306a36Sopenharmony_ci if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && 16962306a36Sopenharmony_ci type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) 17062306a36Sopenharmony_ci continue; 17162306a36Sopenharmony_ci if (count++ == descriptor) { 17262306a36Sopenharmony_ci memcpy(desc_ptr, desc, 4); 17362306a36Sopenharmony_ci /* set select */ 17462306a36Sopenharmony_ci desc_ptr[0] |= 0x80; 17562306a36Sopenharmony_ci /* clear reserved, just in case */ 17662306a36Sopenharmony_ci desc_ptr[0] &= 0xf0; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, 18562306a36Sopenharmony_ci struct enclosure_component *ecomp) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int i, j, count = 0, descriptor = ecomp->number; 18862306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(edev->edev.parent); 18962306a36Sopenharmony_ci struct ses_device *ses_dev = edev->scratch; 19062306a36Sopenharmony_ci unsigned char *type_ptr = ses_dev->page1_types; 19162306a36Sopenharmony_ci unsigned char *desc_ptr = ses_dev->page2 + 8; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len) < 0) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { 19762306a36Sopenharmony_ci for (j = 0; j < type_ptr[1]; j++) { 19862306a36Sopenharmony_ci desc_ptr += 4; 19962306a36Sopenharmony_ci if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && 20062306a36Sopenharmony_ci type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) 20162306a36Sopenharmony_ci continue; 20262306a36Sopenharmony_ci if (count++ == descriptor) 20362306a36Sopenharmony_ci return desc_ptr; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci return NULL; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* For device slot and array device slot elements, byte 3 bit 6 21062306a36Sopenharmony_ci * is "fault sensed" while byte 3 bit 5 is "fault reqstd". As this 21162306a36Sopenharmony_ci * code stands these bits are shifted 4 positions right so in 21262306a36Sopenharmony_ci * sysfs they will appear as bits 2 and 1 respectively. Strange. */ 21362306a36Sopenharmony_cistatic void ses_get_fault(struct enclosure_device *edev, 21462306a36Sopenharmony_ci struct enclosure_component *ecomp) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci unsigned char *desc; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!ses_page2_supported(edev)) { 21962306a36Sopenharmony_ci ecomp->fault = 0; 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci desc = ses_get_page2_descriptor(edev, ecomp); 22362306a36Sopenharmony_ci if (desc) 22462306a36Sopenharmony_ci ecomp->fault = (desc[3] & 0x60) >> 4; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int ses_set_fault(struct enclosure_device *edev, 22862306a36Sopenharmony_ci struct enclosure_component *ecomp, 22962306a36Sopenharmony_ci enum enclosure_component_setting val) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci unsigned char desc[4]; 23262306a36Sopenharmony_ci unsigned char *desc_ptr; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!ses_page2_supported(edev)) 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci desc_ptr = ses_get_page2_descriptor(edev, ecomp); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!desc_ptr) 24062306a36Sopenharmony_ci return -EIO; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci init_device_slot_control(desc, ecomp, desc_ptr); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci switch (val) { 24562306a36Sopenharmony_ci case ENCLOSURE_SETTING_DISABLED: 24662306a36Sopenharmony_ci desc[3] &= 0xdf; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case ENCLOSURE_SETTING_ENABLED: 24962306a36Sopenharmony_ci desc[3] |= 0x20; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci default: 25262306a36Sopenharmony_ci /* SES doesn't do the SGPIO blink settings */ 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return ses_set_page2_descriptor(edev, ecomp, desc); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void ses_get_status(struct enclosure_device *edev, 26062306a36Sopenharmony_ci struct enclosure_component *ecomp) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci unsigned char *desc; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!ses_page2_supported(edev)) { 26562306a36Sopenharmony_ci ecomp->status = 0; 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci desc = ses_get_page2_descriptor(edev, ecomp); 26962306a36Sopenharmony_ci if (desc) 27062306a36Sopenharmony_ci ecomp->status = (desc[0] & 0x0f); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void ses_get_locate(struct enclosure_device *edev, 27462306a36Sopenharmony_ci struct enclosure_component *ecomp) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci unsigned char *desc; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!ses_page2_supported(edev)) { 27962306a36Sopenharmony_ci ecomp->locate = 0; 28062306a36Sopenharmony_ci return; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci desc = ses_get_page2_descriptor(edev, ecomp); 28362306a36Sopenharmony_ci if (desc) 28462306a36Sopenharmony_ci ecomp->locate = (desc[2] & 0x02) ? 1 : 0; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int ses_set_locate(struct enclosure_device *edev, 28862306a36Sopenharmony_ci struct enclosure_component *ecomp, 28962306a36Sopenharmony_ci enum enclosure_component_setting val) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci unsigned char desc[4]; 29262306a36Sopenharmony_ci unsigned char *desc_ptr; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!ses_page2_supported(edev)) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci desc_ptr = ses_get_page2_descriptor(edev, ecomp); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!desc_ptr) 30062306a36Sopenharmony_ci return -EIO; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci init_device_slot_control(desc, ecomp, desc_ptr); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci switch (val) { 30562306a36Sopenharmony_ci case ENCLOSURE_SETTING_DISABLED: 30662306a36Sopenharmony_ci desc[2] &= 0xfd; 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci case ENCLOSURE_SETTING_ENABLED: 30962306a36Sopenharmony_ci desc[2] |= 0x02; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci default: 31262306a36Sopenharmony_ci /* SES doesn't do the SGPIO blink settings */ 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci return ses_set_page2_descriptor(edev, ecomp, desc); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int ses_set_active(struct enclosure_device *edev, 31962306a36Sopenharmony_ci struct enclosure_component *ecomp, 32062306a36Sopenharmony_ci enum enclosure_component_setting val) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci unsigned char desc[4]; 32362306a36Sopenharmony_ci unsigned char *desc_ptr; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!ses_page2_supported(edev)) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci desc_ptr = ses_get_page2_descriptor(edev, ecomp); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!desc_ptr) 33162306a36Sopenharmony_ci return -EIO; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci init_device_slot_control(desc, ecomp, desc_ptr); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci switch (val) { 33662306a36Sopenharmony_ci case ENCLOSURE_SETTING_DISABLED: 33762306a36Sopenharmony_ci desc[2] &= 0x7f; 33862306a36Sopenharmony_ci ecomp->active = 0; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case ENCLOSURE_SETTING_ENABLED: 34162306a36Sopenharmony_ci desc[2] |= 0x80; 34262306a36Sopenharmony_ci ecomp->active = 1; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci default: 34562306a36Sopenharmony_ci /* SES doesn't do the SGPIO blink settings */ 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return ses_set_page2_descriptor(edev, ecomp, desc); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int ses_show_id(struct enclosure_device *edev, char *buf) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct ses_device *ses_dev = edev->scratch; 35462306a36Sopenharmony_ci unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return sprintf(buf, "%#llx\n", id); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void ses_get_power_status(struct enclosure_device *edev, 36062306a36Sopenharmony_ci struct enclosure_component *ecomp) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci unsigned char *desc; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!ses_page2_supported(edev)) { 36562306a36Sopenharmony_ci ecomp->power_status = 0; 36662306a36Sopenharmony_ci return; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci desc = ses_get_page2_descriptor(edev, ecomp); 37062306a36Sopenharmony_ci if (desc) 37162306a36Sopenharmony_ci ecomp->power_status = (desc[3] & 0x10) ? 0 : 1; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int ses_set_power_status(struct enclosure_device *edev, 37562306a36Sopenharmony_ci struct enclosure_component *ecomp, 37662306a36Sopenharmony_ci int val) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci unsigned char desc[4]; 37962306a36Sopenharmony_ci unsigned char *desc_ptr; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!ses_page2_supported(edev)) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci desc_ptr = ses_get_page2_descriptor(edev, ecomp); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!desc_ptr) 38762306a36Sopenharmony_ci return -EIO; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci init_device_slot_control(desc, ecomp, desc_ptr); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci switch (val) { 39262306a36Sopenharmony_ci /* power = 1 is device_off = 0 and vice versa */ 39362306a36Sopenharmony_ci case 0: 39462306a36Sopenharmony_ci desc[3] |= 0x10; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case 1: 39762306a36Sopenharmony_ci desc[3] &= 0xef; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci default: 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci ecomp->power_status = val; 40362306a36Sopenharmony_ci return ses_set_page2_descriptor(edev, ecomp, desc); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic struct enclosure_component_callbacks ses_enclosure_callbacks = { 40762306a36Sopenharmony_ci .get_fault = ses_get_fault, 40862306a36Sopenharmony_ci .set_fault = ses_set_fault, 40962306a36Sopenharmony_ci .get_status = ses_get_status, 41062306a36Sopenharmony_ci .get_locate = ses_get_locate, 41162306a36Sopenharmony_ci .set_locate = ses_set_locate, 41262306a36Sopenharmony_ci .get_power_status = ses_get_power_status, 41362306a36Sopenharmony_ci .set_power_status = ses_set_power_status, 41462306a36Sopenharmony_ci .set_active = ses_set_active, 41562306a36Sopenharmony_ci .show_id = ses_show_id, 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistruct ses_host_edev { 41962306a36Sopenharmony_ci struct Scsi_Host *shost; 42062306a36Sopenharmony_ci struct enclosure_device *edev; 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci#if 0 42462306a36Sopenharmony_ciint ses_match_host(struct enclosure_device *edev, void *data) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct ses_host_edev *sed = data; 42762306a36Sopenharmony_ci struct scsi_device *sdev; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (!scsi_is_sdev_device(edev->edev.parent)) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci sdev = to_scsi_device(edev->edev.parent); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (sdev->host != sed->shost) 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci sed->edev = edev; 43862306a36Sopenharmony_ci return 1; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci#endif /* 0 */ 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int ses_process_descriptor(struct enclosure_component *ecomp, 44362306a36Sopenharmony_ci unsigned char *desc, int max_desc_len) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int eip = desc[0] & 0x10; 44662306a36Sopenharmony_ci int invalid = desc[0] & 0x80; 44762306a36Sopenharmony_ci enum scsi_protocol proto = desc[0] & 0x0f; 44862306a36Sopenharmony_ci u64 addr = 0; 44962306a36Sopenharmony_ci int slot = -1; 45062306a36Sopenharmony_ci struct ses_component *scomp = ecomp->scratch; 45162306a36Sopenharmony_ci unsigned char *d; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (invalid) 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci switch (proto) { 45762306a36Sopenharmony_ci case SCSI_PROTOCOL_FCP: 45862306a36Sopenharmony_ci if (eip) { 45962306a36Sopenharmony_ci if (max_desc_len <= 7) 46062306a36Sopenharmony_ci return 1; 46162306a36Sopenharmony_ci d = desc + 4; 46262306a36Sopenharmony_ci slot = d[3]; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case SCSI_PROTOCOL_SAS: 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (eip) { 46862306a36Sopenharmony_ci if (max_desc_len <= 27) 46962306a36Sopenharmony_ci return 1; 47062306a36Sopenharmony_ci d = desc + 4; 47162306a36Sopenharmony_ci slot = d[3]; 47262306a36Sopenharmony_ci d = desc + 8; 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci if (max_desc_len <= 23) 47562306a36Sopenharmony_ci return 1; 47662306a36Sopenharmony_ci d = desc + 4; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* only take the phy0 addr */ 48162306a36Sopenharmony_ci addr = (u64)d[12] << 56 | 48262306a36Sopenharmony_ci (u64)d[13] << 48 | 48362306a36Sopenharmony_ci (u64)d[14] << 40 | 48462306a36Sopenharmony_ci (u64)d[15] << 32 | 48562306a36Sopenharmony_ci (u64)d[16] << 24 | 48662306a36Sopenharmony_ci (u64)d[17] << 16 | 48762306a36Sopenharmony_ci (u64)d[18] << 8 | 48862306a36Sopenharmony_ci (u64)d[19]; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci default: 49162306a36Sopenharmony_ci /* FIXME: Need to add more protocols than just SAS */ 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci ecomp->slot = slot; 49562306a36Sopenharmony_ci scomp->addr = addr; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistruct efd { 50162306a36Sopenharmony_ci u64 addr; 50262306a36Sopenharmony_ci struct device *dev; 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int ses_enclosure_find_by_addr(struct enclosure_device *edev, 50662306a36Sopenharmony_ci void *data) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct efd *efd = data; 50962306a36Sopenharmony_ci int i; 51062306a36Sopenharmony_ci struct ses_component *scomp; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (i = 0; i < edev->components; i++) { 51362306a36Sopenharmony_ci scomp = edev->component[i].scratch; 51462306a36Sopenharmony_ci if (scomp->addr != efd->addr) 51562306a36Sopenharmony_ci continue; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (enclosure_add_device(edev, i, efd->dev) == 0) 51862306a36Sopenharmony_ci kobject_uevent(&efd->dev->kobj, KOBJ_CHANGE); 51962306a36Sopenharmony_ci return 1; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci#define INIT_ALLOC_SIZE 32 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void ses_enclosure_data_process(struct enclosure_device *edev, 52762306a36Sopenharmony_ci struct scsi_device *sdev, 52862306a36Sopenharmony_ci int create) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci u32 result; 53162306a36Sopenharmony_ci unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL; 53262306a36Sopenharmony_ci int i, j, page7_len, len, components; 53362306a36Sopenharmony_ci struct ses_device *ses_dev = edev->scratch; 53462306a36Sopenharmony_ci int types = ses_dev->page1_num_types; 53562306a36Sopenharmony_ci unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!hdr_buf) 53862306a36Sopenharmony_ci goto simple_populate; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* re-read page 10 */ 54162306a36Sopenharmony_ci if (ses_dev->page10) 54262306a36Sopenharmony_ci ses_recv_diag(sdev, 10, ses_dev->page10, ses_dev->page10_len); 54362306a36Sopenharmony_ci /* Page 7 for the descriptors is optional */ 54462306a36Sopenharmony_ci result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); 54562306a36Sopenharmony_ci if (result) 54662306a36Sopenharmony_ci goto simple_populate; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 54962306a36Sopenharmony_ci /* add 1 for trailing '\0' we'll use */ 55062306a36Sopenharmony_ci buf = kzalloc(len + 1, GFP_KERNEL); 55162306a36Sopenharmony_ci if (!buf) 55262306a36Sopenharmony_ci goto simple_populate; 55362306a36Sopenharmony_ci result = ses_recv_diag(sdev, 7, buf, len); 55462306a36Sopenharmony_ci if (result) { 55562306a36Sopenharmony_ci simple_populate: 55662306a36Sopenharmony_ci kfree(buf); 55762306a36Sopenharmony_ci buf = NULL; 55862306a36Sopenharmony_ci desc_ptr = NULL; 55962306a36Sopenharmony_ci len = 0; 56062306a36Sopenharmony_ci page7_len = 0; 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci desc_ptr = buf + 8; 56362306a36Sopenharmony_ci len = (desc_ptr[2] << 8) + desc_ptr[3]; 56462306a36Sopenharmony_ci /* skip past overall descriptor */ 56562306a36Sopenharmony_ci desc_ptr += len + 4; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci if (ses_dev->page10 && ses_dev->page10_len > 9) 56862306a36Sopenharmony_ci addl_desc_ptr = ses_dev->page10 + 8; 56962306a36Sopenharmony_ci type_ptr = ses_dev->page1_types; 57062306a36Sopenharmony_ci components = 0; 57162306a36Sopenharmony_ci for (i = 0; i < types; i++, type_ptr += 4) { 57262306a36Sopenharmony_ci for (j = 0; j < type_ptr[1]; j++) { 57362306a36Sopenharmony_ci char *name = NULL; 57462306a36Sopenharmony_ci struct enclosure_component *ecomp; 57562306a36Sopenharmony_ci int max_desc_len; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (desc_ptr) { 57862306a36Sopenharmony_ci if (desc_ptr + 3 >= buf + page7_len) { 57962306a36Sopenharmony_ci desc_ptr = NULL; 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci len = (desc_ptr[2] << 8) + desc_ptr[3]; 58262306a36Sopenharmony_ci desc_ptr += 4; 58362306a36Sopenharmony_ci if (desc_ptr + len > buf + page7_len) 58462306a36Sopenharmony_ci desc_ptr = NULL; 58562306a36Sopenharmony_ci else { 58662306a36Sopenharmony_ci /* Add trailing zero - pushes into 58762306a36Sopenharmony_ci * reserved space */ 58862306a36Sopenharmony_ci desc_ptr[len] = '\0'; 58962306a36Sopenharmony_ci name = desc_ptr; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 59462306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (create) 59762306a36Sopenharmony_ci ecomp = enclosure_component_alloc( 59862306a36Sopenharmony_ci edev, 59962306a36Sopenharmony_ci components++, 60062306a36Sopenharmony_ci type_ptr[0], 60162306a36Sopenharmony_ci name); 60262306a36Sopenharmony_ci else if (components < edev->components) 60362306a36Sopenharmony_ci ecomp = &edev->component[components++]; 60462306a36Sopenharmony_ci else 60562306a36Sopenharmony_ci ecomp = ERR_PTR(-EINVAL); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!IS_ERR(ecomp)) { 60862306a36Sopenharmony_ci if (addl_desc_ptr) { 60962306a36Sopenharmony_ci max_desc_len = ses_dev->page10_len - 61062306a36Sopenharmony_ci (addl_desc_ptr - ses_dev->page10); 61162306a36Sopenharmony_ci if (ses_process_descriptor(ecomp, 61262306a36Sopenharmony_ci addl_desc_ptr, 61362306a36Sopenharmony_ci max_desc_len)) 61462306a36Sopenharmony_ci addl_desc_ptr = NULL; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci if (create) 61762306a36Sopenharmony_ci enclosure_component_register( 61862306a36Sopenharmony_ci ecomp); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci if (desc_ptr) 62262306a36Sopenharmony_ci desc_ptr += len; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (addl_desc_ptr && 62562306a36Sopenharmony_ci /* only find additional descriptions for specific devices */ 62662306a36Sopenharmony_ci (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 62762306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE || 62862306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_SAS_EXPANDER || 62962306a36Sopenharmony_ci /* these elements are optional */ 63062306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_TARGET_PORT || 63162306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT || 63262306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS)) { 63362306a36Sopenharmony_ci addl_desc_ptr += addl_desc_ptr[1] + 2; 63462306a36Sopenharmony_ci if (addl_desc_ptr + 1 >= ses_dev->page10 + ses_dev->page10_len) 63562306a36Sopenharmony_ci addl_desc_ptr = NULL; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci kfree(buf); 64062306a36Sopenharmony_ci kfree(hdr_buf); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void ses_match_to_enclosure(struct enclosure_device *edev, 64462306a36Sopenharmony_ci struct scsi_device *sdev, 64562306a36Sopenharmony_ci int refresh) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct scsi_device *edev_sdev = to_scsi_device(edev->edev.parent); 64862306a36Sopenharmony_ci struct efd efd = { 64962306a36Sopenharmony_ci .addr = 0, 65062306a36Sopenharmony_ci }; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (refresh) 65362306a36Sopenharmony_ci ses_enclosure_data_process(edev, edev_sdev, 0); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent)) 65662306a36Sopenharmony_ci efd.addr = sas_get_address(sdev); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (efd.addr) { 65962306a36Sopenharmony_ci efd.dev = &sdev->sdev_gendev; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci enclosure_for_each_device(ses_enclosure_find_by_addr, &efd); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int ses_intf_add(struct device *cdev) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(cdev->parent); 66862306a36Sopenharmony_ci struct scsi_device *tmp_sdev; 66962306a36Sopenharmony_ci unsigned char *buf = NULL, *hdr_buf, *type_ptr, page; 67062306a36Sopenharmony_ci struct ses_device *ses_dev; 67162306a36Sopenharmony_ci u32 result; 67262306a36Sopenharmony_ci int i, types, len, components = 0; 67362306a36Sopenharmony_ci int err = -ENOMEM; 67462306a36Sopenharmony_ci int num_enclosures; 67562306a36Sopenharmony_ci struct enclosure_device *edev; 67662306a36Sopenharmony_ci struct ses_component *scomp = NULL; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (!scsi_device_enclosure(sdev)) { 67962306a36Sopenharmony_ci /* not an enclosure, but might be in one */ 68062306a36Sopenharmony_ci struct enclosure_device *prev = NULL; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { 68362306a36Sopenharmony_ci ses_match_to_enclosure(edev, sdev, 1); 68462306a36Sopenharmony_ci prev = edev; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci return -ENODEV; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* TYPE_ENCLOSURE prints a message in probe */ 69062306a36Sopenharmony_ci if (sdev->type != TYPE_ENCLOSURE) 69162306a36Sopenharmony_ci sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n"); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL); 69462306a36Sopenharmony_ci hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); 69562306a36Sopenharmony_ci if (!hdr_buf || !ses_dev) 69662306a36Sopenharmony_ci goto err_init_free; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci page = 1; 69962306a36Sopenharmony_ci result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); 70062306a36Sopenharmony_ci if (result) 70162306a36Sopenharmony_ci goto recv_failed; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 70462306a36Sopenharmony_ci buf = kzalloc(len, GFP_KERNEL); 70562306a36Sopenharmony_ci if (!buf) 70662306a36Sopenharmony_ci goto err_free; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci result = ses_recv_diag(sdev, page, buf, len); 70962306a36Sopenharmony_ci if (result) 71062306a36Sopenharmony_ci goto recv_failed; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci types = 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* we always have one main enclosure and the rest are referred 71562306a36Sopenharmony_ci * to as secondary subenclosures */ 71662306a36Sopenharmony_ci num_enclosures = buf[1] + 1; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* begin at the enclosure descriptor */ 71962306a36Sopenharmony_ci type_ptr = buf + 8; 72062306a36Sopenharmony_ci /* skip all the enclosure descriptors */ 72162306a36Sopenharmony_ci for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) { 72262306a36Sopenharmony_ci types += type_ptr[2]; 72362306a36Sopenharmony_ci type_ptr += type_ptr[3] + 4; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ses_dev->page1_types = type_ptr; 72762306a36Sopenharmony_ci ses_dev->page1_num_types = types; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci for (i = 0; i < types && type_ptr < buf + len; i++, type_ptr += 4) { 73062306a36Sopenharmony_ci if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 73162306a36Sopenharmony_ci type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) 73262306a36Sopenharmony_ci components += type_ptr[1]; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ses_dev->page1 = buf; 73662306a36Sopenharmony_ci ses_dev->page1_len = len; 73762306a36Sopenharmony_ci buf = NULL; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci page = 2; 74062306a36Sopenharmony_ci result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); 74162306a36Sopenharmony_ci if (result) 74262306a36Sopenharmony_ci goto page2_not_supported; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 74562306a36Sopenharmony_ci buf = kzalloc(len, GFP_KERNEL); 74662306a36Sopenharmony_ci if (!buf) 74762306a36Sopenharmony_ci goto err_free; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* make sure getting page 2 actually works */ 75062306a36Sopenharmony_ci result = ses_recv_diag(sdev, 2, buf, len); 75162306a36Sopenharmony_ci if (result) 75262306a36Sopenharmony_ci goto recv_failed; 75362306a36Sopenharmony_ci ses_dev->page2 = buf; 75462306a36Sopenharmony_ci ses_dev->page2_len = len; 75562306a36Sopenharmony_ci buf = NULL; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* The additional information page --- allows us 75862306a36Sopenharmony_ci * to match up the devices */ 75962306a36Sopenharmony_ci page = 10; 76062306a36Sopenharmony_ci result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); 76162306a36Sopenharmony_ci if (!result) { 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 76462306a36Sopenharmony_ci buf = kzalloc(len, GFP_KERNEL); 76562306a36Sopenharmony_ci if (!buf) 76662306a36Sopenharmony_ci goto err_free; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci result = ses_recv_diag(sdev, page, buf, len); 76962306a36Sopenharmony_ci if (result) 77062306a36Sopenharmony_ci goto recv_failed; 77162306a36Sopenharmony_ci ses_dev->page10 = buf; 77262306a36Sopenharmony_ci ses_dev->page10_len = len; 77362306a36Sopenharmony_ci buf = NULL; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_cipage2_not_supported: 77662306a36Sopenharmony_ci if (components > 0) { 77762306a36Sopenharmony_ci scomp = kcalloc(components, sizeof(struct ses_component), GFP_KERNEL); 77862306a36Sopenharmony_ci if (!scomp) 77962306a36Sopenharmony_ci goto err_free; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev), 78362306a36Sopenharmony_ci components, &ses_enclosure_callbacks); 78462306a36Sopenharmony_ci if (IS_ERR(edev)) { 78562306a36Sopenharmony_ci err = PTR_ERR(edev); 78662306a36Sopenharmony_ci goto err_free; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci kfree(hdr_buf); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci edev->scratch = ses_dev; 79262306a36Sopenharmony_ci for (i = 0; i < components; i++) 79362306a36Sopenharmony_ci edev->component[i].scratch = scomp + i; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci ses_enclosure_data_process(edev, sdev, 1); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* see if there are any devices matching before 79862306a36Sopenharmony_ci * we found the enclosure */ 79962306a36Sopenharmony_ci shost_for_each_device(tmp_sdev, sdev->host) { 80062306a36Sopenharmony_ci if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev)) 80162306a36Sopenharmony_ci continue; 80262306a36Sopenharmony_ci ses_match_to_enclosure(edev, tmp_sdev, 0); 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci recv_failed: 80862306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n", 80962306a36Sopenharmony_ci page); 81062306a36Sopenharmony_ci err = -ENODEV; 81162306a36Sopenharmony_ci err_free: 81262306a36Sopenharmony_ci kfree(buf); 81362306a36Sopenharmony_ci kfree(scomp); 81462306a36Sopenharmony_ci kfree(ses_dev->page10); 81562306a36Sopenharmony_ci kfree(ses_dev->page2); 81662306a36Sopenharmony_ci kfree(ses_dev->page1); 81762306a36Sopenharmony_ci err_init_free: 81862306a36Sopenharmony_ci kfree(ses_dev); 81962306a36Sopenharmony_ci kfree(hdr_buf); 82062306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err); 82162306a36Sopenharmony_ci return err; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int ses_remove(struct device *dev) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic void ses_intf_remove_component(struct scsi_device *sdev) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct enclosure_device *edev, *prev = NULL; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { 83462306a36Sopenharmony_ci prev = edev; 83562306a36Sopenharmony_ci if (!enclosure_remove_device(edev, &sdev->sdev_gendev)) 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci if (edev) 83962306a36Sopenharmony_ci put_device(&edev->edev); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic void ses_intf_remove_enclosure(struct scsi_device *sdev) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct enclosure_device *edev; 84562306a36Sopenharmony_ci struct ses_device *ses_dev; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* exact match to this enclosure */ 84862306a36Sopenharmony_ci edev = enclosure_find(&sdev->sdev_gendev, NULL); 84962306a36Sopenharmony_ci if (!edev) 85062306a36Sopenharmony_ci return; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ses_dev = edev->scratch; 85362306a36Sopenharmony_ci edev->scratch = NULL; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci kfree(ses_dev->page10); 85662306a36Sopenharmony_ci kfree(ses_dev->page1); 85762306a36Sopenharmony_ci kfree(ses_dev->page2); 85862306a36Sopenharmony_ci kfree(ses_dev); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (edev->components) 86162306a36Sopenharmony_ci kfree(edev->component[0].scratch); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci put_device(&edev->edev); 86462306a36Sopenharmony_ci enclosure_unregister(edev); 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic void ses_intf_remove(struct device *cdev) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(cdev->parent); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (!scsi_device_enclosure(sdev)) 87262306a36Sopenharmony_ci ses_intf_remove_component(sdev); 87362306a36Sopenharmony_ci else 87462306a36Sopenharmony_ci ses_intf_remove_enclosure(sdev); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic struct class_interface ses_interface = { 87862306a36Sopenharmony_ci .add_dev = ses_intf_add, 87962306a36Sopenharmony_ci .remove_dev = ses_intf_remove, 88062306a36Sopenharmony_ci}; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic struct scsi_driver ses_template = { 88362306a36Sopenharmony_ci .gendrv = { 88462306a36Sopenharmony_ci .name = "ses", 88562306a36Sopenharmony_ci .owner = THIS_MODULE, 88662306a36Sopenharmony_ci .probe = ses_probe, 88762306a36Sopenharmony_ci .remove = ses_remove, 88862306a36Sopenharmony_ci }, 88962306a36Sopenharmony_ci}; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int __init ses_init(void) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci int err; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci err = scsi_register_interface(&ses_interface); 89662306a36Sopenharmony_ci if (err) 89762306a36Sopenharmony_ci return err; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci err = scsi_register_driver(&ses_template.gendrv); 90062306a36Sopenharmony_ci if (err) 90162306a36Sopenharmony_ci goto out_unreg; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci out_unreg: 90662306a36Sopenharmony_ci scsi_unregister_interface(&ses_interface); 90762306a36Sopenharmony_ci return err; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic void __exit ses_exit(void) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci scsi_unregister_driver(&ses_template.gendrv); 91362306a36Sopenharmony_ci scsi_unregister_interface(&ses_interface); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cimodule_init(ses_init); 91762306a36Sopenharmony_cimodule_exit(ses_exit); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ciMODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ciMODULE_AUTHOR("James Bottomley"); 92262306a36Sopenharmony_ciMODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver"); 92362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 924