ses.c revision 691b4773aa556d0975dbc25c93e6c8b839dad325
19927c68864e9c39cc317b4f559309ba29e642168James Bottomley/* 29927c68864e9c39cc317b4f559309ba29e642168James Bottomley * SCSI Enclosure Services 39927c68864e9c39cc317b4f559309ba29e642168James Bottomley * 49927c68864e9c39cc317b4f559309ba29e642168James Bottomley * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com> 59927c68864e9c39cc317b4f559309ba29e642168James Bottomley * 69927c68864e9c39cc317b4f559309ba29e642168James Bottomley**----------------------------------------------------------------------------- 79927c68864e9c39cc317b4f559309ba29e642168James Bottomley** 89927c68864e9c39cc317b4f559309ba29e642168James Bottomley** This program is free software; you can redistribute it and/or 99927c68864e9c39cc317b4f559309ba29e642168James Bottomley** modify it under the terms of the GNU General Public License 109927c68864e9c39cc317b4f559309ba29e642168James Bottomley** version 2 as published by the Free Software Foundation. 119927c68864e9c39cc317b4f559309ba29e642168James Bottomley** 129927c68864e9c39cc317b4f559309ba29e642168James Bottomley** This program is distributed in the hope that it will be useful, 139927c68864e9c39cc317b4f559309ba29e642168James Bottomley** but WITHOUT ANY WARRANTY; without even the implied warranty of 149927c68864e9c39cc317b4f559309ba29e642168James Bottomley** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 159927c68864e9c39cc317b4f559309ba29e642168James Bottomley** GNU General Public License for more details. 169927c68864e9c39cc317b4f559309ba29e642168James Bottomley** 179927c68864e9c39cc317b4f559309ba29e642168James Bottomley** You should have received a copy of the GNU General Public License 189927c68864e9c39cc317b4f559309ba29e642168James Bottomley** along with this program; if not, write to the Free Software 199927c68864e9c39cc317b4f559309ba29e642168James Bottomley** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 209927c68864e9c39cc317b4f559309ba29e642168James Bottomley** 219927c68864e9c39cc317b4f559309ba29e642168James Bottomley**----------------------------------------------------------------------------- 229927c68864e9c39cc317b4f559309ba29e642168James Bottomley*/ 239927c68864e9c39cc317b4f559309ba29e642168James Bottomley 249927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/module.h> 259927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/kernel.h> 269927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/enclosure.h> 279927c68864e9c39cc317b4f559309ba29e642168James Bottomley 289927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi.h> 299927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_cmnd.h> 309927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_dbg.h> 319927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_device.h> 329927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_driver.h> 339927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_host.h> 349927c68864e9c39cc317b4f559309ba29e642168James Bottomley 359927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_device { 36691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *page1; 37691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *page2; 38691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *page10; 399927c68864e9c39cc317b4f559309ba29e642168James Bottomley short page1_len; 409927c68864e9c39cc317b4f559309ba29e642168James Bottomley short page2_len; 419927c68864e9c39cc317b4f559309ba29e642168James Bottomley short page10_len; 429927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 439927c68864e9c39cc317b4f559309ba29e642168James Bottomley 449927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_component { 459927c68864e9c39cc317b4f559309ba29e642168James Bottomley u64 addr; 469927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *desc; 479927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 489927c68864e9c39cc317b4f559309ba29e642168James Bottomley 499927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_probe(struct device *dev) 509927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 519927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev = to_scsi_device(dev); 529927c68864e9c39cc317b4f559309ba29e642168James Bottomley int err = -ENODEV; 539927c68864e9c39cc317b4f559309ba29e642168James Bottomley 549927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (sdev->type != TYPE_ENCLOSURE) 559927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto out; 569927c68864e9c39cc317b4f559309ba29e642168James Bottomley 579927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = 0; 589927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n"); 599927c68864e9c39cc317b4f559309ba29e642168James Bottomley 609927c68864e9c39cc317b4f559309ba29e642168James Bottomley out: 619927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 629927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 639927c68864e9c39cc317b4f559309ba29e642168James Bottomley 649927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define SES_TIMEOUT 30 659927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define SES_RETRIES 3 669927c68864e9c39cc317b4f559309ba29e642168James Bottomley 679927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_recv_diag(struct scsi_device *sdev, int page_code, 689927c68864e9c39cc317b4f559309ba29e642168James Bottomley void *buf, int bufflen) 699927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 70691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char cmd[] = { 719927c68864e9c39cc317b4f559309ba29e642168James Bottomley RECEIVE_DIAGNOSTIC, 729927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1, /* Set PCV bit */ 739927c68864e9c39cc317b4f559309ba29e642168James Bottomley page_code, 749927c68864e9c39cc317b4f559309ba29e642168James Bottomley bufflen >> 8, 759927c68864e9c39cc317b4f559309ba29e642168James Bottomley bufflen & 0xff, 769927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0 779927c68864e9c39cc317b4f559309ba29e642168James Bottomley }; 789927c68864e9c39cc317b4f559309ba29e642168James Bottomley 799927c68864e9c39cc317b4f559309ba29e642168James Bottomley return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, 809927c68864e9c39cc317b4f559309ba29e642168James Bottomley NULL, SES_TIMEOUT, SES_RETRIES); 819927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 829927c68864e9c39cc317b4f559309ba29e642168James Bottomley 839927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_send_diag(struct scsi_device *sdev, int page_code, 849927c68864e9c39cc317b4f559309ba29e642168James Bottomley void *buf, int bufflen) 859927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 869927c68864e9c39cc317b4f559309ba29e642168James Bottomley u32 result; 879927c68864e9c39cc317b4f559309ba29e642168James Bottomley 88691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char cmd[] = { 899927c68864e9c39cc317b4f559309ba29e642168James Bottomley SEND_DIAGNOSTIC, 909927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0x10, /* Set PF bit */ 919927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0, 929927c68864e9c39cc317b4f559309ba29e642168James Bottomley bufflen >> 8, 939927c68864e9c39cc317b4f559309ba29e642168James Bottomley bufflen & 0xff, 949927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0 959927c68864e9c39cc317b4f559309ba29e642168James Bottomley }; 969927c68864e9c39cc317b4f559309ba29e642168James Bottomley 979927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen, 989927c68864e9c39cc317b4f559309ba29e642168James Bottomley NULL, SES_TIMEOUT, SES_RETRIES); 999927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 1009927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n", 1019927c68864e9c39cc317b4f559309ba29e642168James Bottomley result); 1029927c68864e9c39cc317b4f559309ba29e642168James Bottomley return result; 1039927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 1049927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1059927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_page2_descriptor(struct enclosure_device *edev, 1069927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp, 107691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc) 1089927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 1099927c68864e9c39cc317b4f559309ba29e642168James Bottomley int i, j, count = 0, descriptor = ecomp->number; 1109927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); 1119927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev = edev->scratch; 112691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; 113691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc_ptr = ses_dev->page2 + 8; 1149927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1159927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* Clear everything */ 1169927c68864e9c39cc317b4f559309ba29e642168James Bottomley memset(desc_ptr, 0, ses_dev->page2_len - 8); 1179927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { 1189927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (j = 0; j < type_ptr[1]; j++) { 1199927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr += 4; 1209927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && 1219927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) 1229927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 1239927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (count++ == descriptor) { 1249927c68864e9c39cc317b4f559309ba29e642168James Bottomley memcpy(desc_ptr, desc, 4); 1259927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* set select */ 1269927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr[0] |= 0x80; 1279927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* clear reserved, just in case */ 1289927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr[0] &= 0xf0; 1299927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1309927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1319927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1329927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1339927c68864e9c39cc317b4f559309ba29e642168James Bottomley return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); 1349927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 1359927c68864e9c39cc317b4f559309ba29e642168James Bottomley 136691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lustatic unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, 1379927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp) 1389927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 1399927c68864e9c39cc317b4f559309ba29e642168James Bottomley int i, j, count = 0, descriptor = ecomp->number; 1409927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); 1419927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev = edev->scratch; 142691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; 143691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc_ptr = ses_dev->page2 + 8; 1449927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1459927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); 1469927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1479927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { 1489927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (j = 0; j < type_ptr[1]; j++) { 1499927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr += 4; 1509927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && 1519927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) 1529927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 1539927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (count++ == descriptor) 1549927c68864e9c39cc317b4f559309ba29e642168James Bottomley return desc_ptr; 1559927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1569927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1579927c68864e9c39cc317b4f559309ba29e642168James Bottomley return NULL; 1589927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 1599927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1609927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_fault(struct enclosure_device *edev, 1619927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp) 1629927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 163691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc; 1649927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1659927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc = ses_get_page2_descriptor(edev, ecomp); 166691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc) 167691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ecomp->fault = (desc[3] & 0x60) >> 4; 1689927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 1699927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1709927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_fault(struct enclosure_device *edev, 1719927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp, 1729927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum enclosure_component_setting val) 1739927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 174691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char desc[4] = {0 }; 1759927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1769927c68864e9c39cc317b4f559309ba29e642168James Bottomley switch (val) { 1779927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_DISABLED: 1789927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* zero is disabled */ 1799927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 1809927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_ENABLED: 1819927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc[2] = 0x02; 1829927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 1839927c68864e9c39cc317b4f559309ba29e642168James Bottomley default: 1849927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* SES doesn't do the SGPIO blink settings */ 1859927c68864e9c39cc317b4f559309ba29e642168James Bottomley return -EINVAL; 1869927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 1879927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1889927c68864e9c39cc317b4f559309ba29e642168James Bottomley return ses_set_page2_descriptor(edev, ecomp, desc); 1899927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 1909927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1919927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_status(struct enclosure_device *edev, 1929927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp) 1939927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 194691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc; 1959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1969927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc = ses_get_page2_descriptor(edev, ecomp); 197691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc) 198691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ecomp->status = (desc[0] & 0x0f); 1999927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 2009927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2019927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_locate(struct enclosure_device *edev, 2029927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp) 2039927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 204691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *desc; 2059927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2069927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc = ses_get_page2_descriptor(edev, ecomp); 207691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc) 208691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ecomp->locate = (desc[2] & 0x02) ? 1 : 0; 2099927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 2109927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2119927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_locate(struct enclosure_device *edev, 2129927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp, 2139927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum enclosure_component_setting val) 2149927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 215691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char desc[4] = {0 }; 2169927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2179927c68864e9c39cc317b4f559309ba29e642168James Bottomley switch (val) { 2189927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_DISABLED: 2199927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* zero is disabled */ 2209927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 2219927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_ENABLED: 2229927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc[2] = 0x02; 2239927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 2249927c68864e9c39cc317b4f559309ba29e642168James Bottomley default: 2259927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* SES doesn't do the SGPIO blink settings */ 2269927c68864e9c39cc317b4f559309ba29e642168James Bottomley return -EINVAL; 2279927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 2289927c68864e9c39cc317b4f559309ba29e642168James Bottomley return ses_set_page2_descriptor(edev, ecomp, desc); 2299927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 2309927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2319927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_active(struct enclosure_device *edev, 2329927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp, 2339927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum enclosure_component_setting val) 2349927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 235691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char desc[4] = {0 }; 2369927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2379927c68864e9c39cc317b4f559309ba29e642168James Bottomley switch (val) { 2389927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_DISABLED: 2399927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* zero is disabled */ 2409927c68864e9c39cc317b4f559309ba29e642168James Bottomley ecomp->active = 0; 2419927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 2429927c68864e9c39cc317b4f559309ba29e642168James Bottomley case ENCLOSURE_SETTING_ENABLED: 2439927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc[2] = 0x80; 2449927c68864e9c39cc317b4f559309ba29e642168James Bottomley ecomp->active = 1; 2459927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 2469927c68864e9c39cc317b4f559309ba29e642168James Bottomley default: 2479927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* SES doesn't do the SGPIO blink settings */ 2489927c68864e9c39cc317b4f559309ba29e642168James Bottomley return -EINVAL; 2499927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 2509927c68864e9c39cc317b4f559309ba29e642168James Bottomley return ses_set_page2_descriptor(edev, ecomp, desc); 2519927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 2529927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2539927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct enclosure_component_callbacks ses_enclosure_callbacks = { 2549927c68864e9c39cc317b4f559309ba29e642168James Bottomley .get_fault = ses_get_fault, 2559927c68864e9c39cc317b4f559309ba29e642168James Bottomley .set_fault = ses_set_fault, 2569927c68864e9c39cc317b4f559309ba29e642168James Bottomley .get_status = ses_get_status, 2579927c68864e9c39cc317b4f559309ba29e642168James Bottomley .get_locate = ses_get_locate, 2589927c68864e9c39cc317b4f559309ba29e642168James Bottomley .set_locate = ses_set_locate, 2599927c68864e9c39cc317b4f559309ba29e642168James Bottomley .set_active = ses_set_active, 2609927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 2619927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2629927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_host_edev { 2639927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct Scsi_Host *shost; 2649927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_device *edev; 2659927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 2669927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2679927c68864e9c39cc317b4f559309ba29e642168James Bottomleyint ses_match_host(struct enclosure_device *edev, void *data) 2689927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 2699927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_host_edev *sed = data; 2709927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev; 2719927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2729927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scsi_is_sdev_device(edev->cdev.dev)) 2739927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 2749927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2759927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev = to_scsi_device(edev->cdev.dev); 2769927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2779927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (sdev->host != sed->shost) 2789927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 2799927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2809927c68864e9c39cc317b4f559309ba29e642168James Bottomley sed->edev = edev; 2819927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 1; 2829927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 2839927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2849927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_process_descriptor(struct enclosure_component *ecomp, 2859927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *desc) 2869927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 2879927c68864e9c39cc317b4f559309ba29e642168James Bottomley int eip = desc[0] & 0x10; 2889927c68864e9c39cc317b4f559309ba29e642168James Bottomley int invalid = desc[0] & 0x80; 2899927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum scsi_protocol proto = desc[0] & 0x0f; 2909927c68864e9c39cc317b4f559309ba29e642168James Bottomley u64 addr = 0; 2919927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_component *scomp = ecomp->scratch; 2929927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *d; 2939927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2949927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp->desc = desc; 2959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2969927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (invalid) 2979927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 2989927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2999927c68864e9c39cc317b4f559309ba29e642168James Bottomley switch (proto) { 3009927c68864e9c39cc317b4f559309ba29e642168James Bottomley case SCSI_PROTOCOL_SAS: 3019927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (eip) 3029927c68864e9c39cc317b4f559309ba29e642168James Bottomley d = desc + 8; 3039927c68864e9c39cc317b4f559309ba29e642168James Bottomley else 3049927c68864e9c39cc317b4f559309ba29e642168James Bottomley d = desc + 4; 3059927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* only take the phy0 addr */ 3069927c68864e9c39cc317b4f559309ba29e642168James Bottomley addr = (u64)d[12] << 56 | 3079927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[13] << 48 | 3089927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[14] << 40 | 3099927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[15] << 32 | 3109927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[16] << 24 | 3119927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[17] << 16 | 3129927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[18] << 8 | 3139927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[19]; 3149927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 3159927c68864e9c39cc317b4f559309ba29e642168James Bottomley default: 3169927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* FIXME: Need to add more protocols than just SAS */ 3179927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 3189927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3199927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp->addr = addr; 3209927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 3219927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3229927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct efd { 3239927c68864e9c39cc317b4f559309ba29e642168James Bottomley u64 addr; 3249927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct device *dev; 3259927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 3269927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3279927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_enclosure_find_by_addr(struct enclosure_device *edev, 3289927c68864e9c39cc317b4f559309ba29e642168James Bottomley void *data) 3299927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 3309927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct efd *efd = data; 3319927c68864e9c39cc317b4f559309ba29e642168James Bottomley int i; 3329927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_component *scomp; 3339927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3349927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!edev->component[0].scratch) 3359927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 3369927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3379927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < edev->components; i++) { 3389927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp = edev->component[i].scratch; 3399927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (scomp->addr != efd->addr) 3409927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 3419927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3429927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_add_device(edev, i, efd->dev); 3439927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 1; 3449927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3459927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 3469927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 3479927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3489927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define VPD_INQUIRY_SIZE 512 3499927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3509927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_match_to_enclosure(struct enclosure_device *edev, 3519927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev) 3529927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 3539927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *buf = kmalloc(VPD_INQUIRY_SIZE, GFP_KERNEL); 3549927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *desc; 3559927c68864e9c39cc317b4f559309ba29e642168James Bottomley int len; 3569927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct efd efd = { 3579927c68864e9c39cc317b4f559309ba29e642168James Bottomley .addr = 0, 3589927c68864e9c39cc317b4f559309ba29e642168James Bottomley }; 3599927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char cmd[] = { 3609927c68864e9c39cc317b4f559309ba29e642168James Bottomley INQUIRY, 3619927c68864e9c39cc317b4f559309ba29e642168James Bottomley 1, 3629927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0x83, 3639927c68864e9c39cc317b4f559309ba29e642168James Bottomley VPD_INQUIRY_SIZE >> 8, 3649927c68864e9c39cc317b4f559309ba29e642168James Bottomley VPD_INQUIRY_SIZE & 0xff, 3659927c68864e9c39cc317b4f559309ba29e642168James Bottomley 0 3669927c68864e9c39cc317b4f559309ba29e642168James Bottomley }; 3679927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3689927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 3699927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 3709927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3719927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, 3729927c68864e9c39cc317b4f559309ba29e642168James Bottomley VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES)) 3739927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto free; 3749927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3759927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (buf[2] << 8) + buf[3]; 3769927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc = buf + 4; 3779927c68864e9c39cc317b4f559309ba29e642168James Bottomley while (desc < buf + len) { 3789927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum scsi_protocol proto = desc[0] >> 4; 3799927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 code_set = desc[0] & 0x0f; 3809927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 piv = desc[1] & 0x80; 3819927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 assoc = (desc[1] & 0x30) >> 4; 3829927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 type = desc[1] & 0x0f; 3839927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 len = desc[3]; 3849927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3859927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (piv && code_set == 1 && assoc == 1 && code_set == 1 3869927c68864e9c39cc317b4f559309ba29e642168James Bottomley && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8) 3879927c68864e9c39cc317b4f559309ba29e642168James Bottomley efd.addr = (u64)desc[4] << 56 | 3889927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[5] << 48 | 3899927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[6] << 40 | 3909927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[7] << 32 | 3919927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[8] << 24 | 3929927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[9] << 16 | 3939927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[10] << 8 | 3949927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[11]; 3959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3969927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc += len + 4; 3979927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3989927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!efd.addr) 3999927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto free; 4009927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4019927c68864e9c39cc317b4f559309ba29e642168James Bottomley efd.dev = &sdev->sdev_gendev; 4029927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4039927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_for_each_device(ses_enclosure_find_by_addr, &efd); 4049927c68864e9c39cc317b4f559309ba29e642168James Bottomley free: 4059927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 4069927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 4079927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4089927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define INIT_ALLOC_SIZE 32 4099927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4109927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_intf_add(struct class_device *cdev, 4119927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct class_interface *intf) 4129927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 4139927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev = to_scsi_device(cdev->dev); 4149927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *tmp_sdev; 415691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL, 416691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu *addl_desc_ptr = NULL; 4179927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev; 4189927c68864e9c39cc317b4f559309ba29e642168James Bottomley u32 result; 419691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu int i, j, types, len, page7_len = 0, components = 0; 4209927c68864e9c39cc317b4f559309ba29e642168James Bottomley int err = -ENOMEM; 4219927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_device *edev; 4227c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu struct ses_component *scomp = NULL; 4239927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4249927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scsi_device_enclosure(sdev)) { 4259927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* not an enclosure, but might be in one */ 4267c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu edev = enclosure_find(&sdev->host->shost_gendev); 4279927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (edev) { 4289927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_match_to_enclosure(edev, sdev); 4299927c68864e9c39cc317b4f559309ba29e642168James Bottomley class_device_put(&edev->cdev); 4309927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4319927c68864e9c39cc317b4f559309ba29e642168James Bottomley return -ENODEV; 4329927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4339927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4349927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* TYPE_ENCLOSURE prints a message in probe */ 4359927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (sdev->type != TYPE_ENCLOSURE) 4369927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n"); 4379927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4389927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL); 4399927c68864e9c39cc317b4f559309ba29e642168James Bottomley hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); 4409927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!hdr_buf || !ses_dev) 4419927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_init_free; 4429927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4439927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE); 4449927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4459927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4469927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4479927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (hdr_buf[1] != 0) { 4489927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* FIXME: need subenclosure support; I've just never 4499927c68864e9c39cc317b4f559309ba29e642168James Bottomley * seen a device with subenclosures and it makes the 4509927c68864e9c39cc317b4f559309ba29e642168James Bottomley * traversal routines more complex */ 4519927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, 4529927c68864e9c39cc317b4f559309ba29e642168James Bottomley "FIXME driver has no support for subenclosures (%d)\n", 453691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu hdr_buf[1]); 4549927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4559927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4569927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4579927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 4589927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len, GFP_KERNEL); 4599927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 4609927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4619927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4629927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 1, buf, len); 4639927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4649927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4659927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4669927c68864e9c39cc317b4f559309ba29e642168James Bottomley types = buf[10]; 4679927c68864e9c39cc317b4f559309ba29e642168James Bottomley 468691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu type_ptr = buf + 12 + buf[11]; 4699927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4709927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < types; i++, type_ptr += 4) { 4719927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 4729927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) 4739927c68864e9c39cc317b4f559309ba29e642168James Bottomley components += type_ptr[1]; 4749927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4757c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu ses_dev->page1 = buf; 4767c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu ses_dev->page1_len = len; 4777c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu buf = NULL; 4789927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4799927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE); 4809927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4819927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4829927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4839927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 4849927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len, GFP_KERNEL); 4859927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 4869927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4879927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4889927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* make sure getting page 2 actually works */ 4899927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 2, buf, len); 4909927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4919927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4929927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev->page2 = buf; 4939927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev->page2_len = len; 4947c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu buf = NULL; 4959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4969927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* The additional information page --- allows us 4979927c68864e9c39cc317b4f559309ba29e642168James Bottomley * to match up the devices */ 4989927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE); 499691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!result) { 500691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 501691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 502691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu buf = kzalloc(len, GFP_KERNEL); 503691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!buf) 504691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu goto err_free; 505691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 506691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu result = ses_recv_diag(sdev, 10, buf, len); 507691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (result) 508691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu goto recv_failed; 509691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ses_dev->page10 = buf; 510691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ses_dev->page10_len = len; 511691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu buf = NULL; 512691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } 5139927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5147c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL); 5159927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scomp) 5167c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu goto err_free; 5179927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5189927c68864e9c39cc317b4f559309ba29e642168James Bottomley edev = enclosure_register(cdev->dev, sdev->sdev_gendev.bus_id, 5199927c68864e9c39cc317b4f559309ba29e642168James Bottomley components, &ses_enclosure_callbacks); 5209927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (IS_ERR(edev)) { 5219927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = PTR_ERR(edev); 5229927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 5239927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5249927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5259927c68864e9c39cc317b4f559309ba29e642168James Bottomley edev->scratch = ses_dev; 5269927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < components; i++) 5277c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu edev->component[i].scratch = scomp + i; 5289927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5299927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* Page 7 for the descriptors is optional */ 5309927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); 5319927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 5329927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto simple_populate; 5339927c68864e9c39cc317b4f559309ba29e642168James Bottomley 534691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 5359927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* add 1 for trailing '\0' we'll use */ 5369927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len + 1, GFP_KERNEL); 5377c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu if (!buf) 5387c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu goto simple_populate; 5399927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 7, buf, len); 5409927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) { 5419927c68864e9c39cc317b4f559309ba29e642168James Bottomley simple_populate: 5429927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 5439927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = NULL; 5449927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr = NULL; 5459927c68864e9c39cc317b4f559309ba29e642168James Bottomley addl_desc_ptr = NULL; 5469927c68864e9c39cc317b4f559309ba29e642168James Bottomley } else { 5479927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr = buf + 8; 5489927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (desc_ptr[2] << 8) + desc_ptr[3]; 5499927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* skip past overall descriptor */ 5509927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr += len + 4; 551691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (ses_dev->page10) 552691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu addl_desc_ptr = ses_dev->page10 + 8; 5539927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5549927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; 5559927c68864e9c39cc317b4f559309ba29e642168James Bottomley components = 0; 5569927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < types; i++, type_ptr += 4) { 5579927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (j = 0; j < type_ptr[1]; j++) { 5589927c68864e9c39cc317b4f559309ba29e642168James Bottomley char *name = NULL; 5599927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp; 5609927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5619927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (desc_ptr) { 562691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc_ptr >= buf + page7_len) { 563691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr = NULL; 564691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } else { 565691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu len = (desc_ptr[2] << 8) + desc_ptr[3]; 566691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr += 4; 567691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu /* Add trailing zero - pushes into 568691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu * reserved space */ 569691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr[len] = '\0'; 570691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu name = desc_ptr; 571691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } 5729927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 573691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 574691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { 575691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 576691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ecomp = enclosure_component_register(edev, 5779927c68864e9c39cc317b4f559309ba29e642168James Bottomley components++, 5789927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0], 5799927c68864e9c39cc317b4f559309ba29e642168James Bottomley name); 580691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 581691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!IS_ERR(ecomp) && addl_desc_ptr) 5829927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_process_descriptor(ecomp, 5839927c68864e9c39cc317b4f559309ba29e642168James Bottomley addl_desc_ptr); 5849927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 585691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc_ptr) 586691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr += len; 587691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 588691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (addl_desc_ptr) 589691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu addl_desc_ptr += addl_desc_ptr[1] + 2; 590691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 5919927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5929927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5939927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 5949927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(hdr_buf); 5959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5969927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* see if there are any devices matching before 5979927c68864e9c39cc317b4f559309ba29e642168James Bottomley * we found the enclosure */ 5989927c68864e9c39cc317b4f559309ba29e642168James Bottomley shost_for_each_device(tmp_sdev, sdev->host) { 5999927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev)) 6009927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 6019927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_match_to_enclosure(edev, tmp_sdev); 6029927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 6039927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6049927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 6059927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6069927c68864e9c39cc317b4f559309ba29e642168James Bottomley recv_failed: 6079927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n", 6089927c68864e9c39cc317b4f559309ba29e642168James Bottomley result); 6099927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = -ENODEV; 6109927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_free: 6119927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 6127c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu kfree(scomp); 6139927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page10); 6149927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page2); 6159927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page1); 6169927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_init_free: 6179927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev); 6189927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(hdr_buf); 6199927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err); 6209927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6219927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6229927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6239927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_remove(struct device *dev) 6249927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6259927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 6269927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6279927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6289927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_intf_remove(struct class_device *cdev, 6299927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct class_interface *intf) 6309927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6319927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev = to_scsi_device(cdev->dev); 6329927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_device *edev; 6339927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev; 6349927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6359927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scsi_device_enclosure(sdev)) 6369927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 6379927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6389927c68864e9c39cc317b4f559309ba29e642168James Bottomley edev = enclosure_find(cdev->dev); 6399927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!edev) 6409927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 6419927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6429927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev = edev->scratch; 6439927c68864e9c39cc317b4f559309ba29e642168James Bottomley edev->scratch = NULL; 6449927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6457c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu kfree(ses_dev->page10); 6469927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page1); 6479927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page2); 6489927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev); 6499927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6509927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(edev->component[0].scratch); 6519927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6529927c68864e9c39cc317b4f559309ba29e642168James Bottomley class_device_put(&edev->cdev); 6539927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_unregister(edev); 6549927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6559927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6569927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct class_interface ses_interface = { 6579927c68864e9c39cc317b4f559309ba29e642168James Bottomley .add = ses_intf_add, 6589927c68864e9c39cc317b4f559309ba29e642168James Bottomley .remove = ses_intf_remove, 6599927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 6609927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6619927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct scsi_driver ses_template = { 6629927c68864e9c39cc317b4f559309ba29e642168James Bottomley .owner = THIS_MODULE, 6639927c68864e9c39cc317b4f559309ba29e642168James Bottomley .gendrv = { 6649927c68864e9c39cc317b4f559309ba29e642168James Bottomley .name = "ses", 6659927c68864e9c39cc317b4f559309ba29e642168James Bottomley .probe = ses_probe, 6669927c68864e9c39cc317b4f559309ba29e642168James Bottomley .remove = ses_remove, 6679927c68864e9c39cc317b4f559309ba29e642168James Bottomley }, 6689927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 6699927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6709927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int __init ses_init(void) 6719927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6729927c68864e9c39cc317b4f559309ba29e642168James Bottomley int err; 6739927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6749927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = scsi_register_interface(&ses_interface); 6759927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (err) 6769927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6779927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6789927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = scsi_register_driver(&ses_template.gendrv); 6799927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (err) 6809927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto out_unreg; 6819927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6829927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 6839927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6849927c68864e9c39cc317b4f559309ba29e642168James Bottomley out_unreg: 6859927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_interface(&ses_interface); 6869927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6879927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6889927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6899927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void __exit ses_exit(void) 6909927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6919927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_driver(&ses_template.gendrv); 6929927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_interface(&ses_interface); 6939927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6949927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6959927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_init(ses_init); 6969927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_exit(ses_exit); 6979927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6989927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE); 6999927c68864e9c39cc317b4f559309ba29e642168James Bottomley 7009927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_AUTHOR("James Bottomley"); 7019927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver"); 7029927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_LICENSE("GPL v2"); 703