ses.c revision 43d8eb9cfd0aea93be32181c64e18191b69c211c
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 64c95e62ce8905aab62fed224eaaa9b8558a0ef652Matthew Wilcox#define SES_TIMEOUT (30 * HZ) 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, 80f4f4e47e4af6b02dd1c425b931c65d0165356e33FUJITA Tomonori NULL, SES_TIMEOUT, SES_RETRIES, NULL); 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, 98f4f4e47e4af6b02dd1c425b931c65d0165356e33FUJITA Tomonori NULL, SES_TIMEOUT, SES_RETRIES, NULL); 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; 110ee959b00c335d7780136c5abda37809191fe52c3Tony Jones struct scsi_device *sdev = to_scsi_device(edev->edev.parent); 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; 140ee959b00c335d7780136c5abda37809191fe52c3Tony Jones struct scsi_device *sdev = to_scsi_device(edev->edev.parent); 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 267e0aae1a53133f0d7833c8f358a0ccc7055fc5b28Adrian Bunk#if 0 2689927c68864e9c39cc317b4f559309ba29e642168James Bottomleyint ses_match_host(struct enclosure_device *edev, void *data) 2699927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 2709927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_host_edev *sed = data; 2719927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev; 2729927c68864e9c39cc317b4f559309ba29e642168James Bottomley 273ee959b00c335d7780136c5abda37809191fe52c3Tony Jones if (!scsi_is_sdev_device(edev->edev.parent)) 2749927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 2759927c68864e9c39cc317b4f559309ba29e642168James Bottomley 276ee959b00c335d7780136c5abda37809191fe52c3Tony Jones sdev = to_scsi_device(edev->edev.parent); 2779927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2789927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (sdev->host != sed->shost) 2799927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 2809927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2819927c68864e9c39cc317b4f559309ba29e642168James Bottomley sed->edev = edev; 2829927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 1; 2839927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 284e0aae1a53133f0d7833c8f358a0ccc7055fc5b28Adrian Bunk#endif /* 0 */ 2859927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2869927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_process_descriptor(struct enclosure_component *ecomp, 2879927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *desc) 2889927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 2899927c68864e9c39cc317b4f559309ba29e642168James Bottomley int eip = desc[0] & 0x10; 2909927c68864e9c39cc317b4f559309ba29e642168James Bottomley int invalid = desc[0] & 0x80; 2919927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum scsi_protocol proto = desc[0] & 0x0f; 2929927c68864e9c39cc317b4f559309ba29e642168James Bottomley u64 addr = 0; 2939927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_component *scomp = ecomp->scratch; 2949927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *d; 2959927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2969927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp->desc = desc; 2979927c68864e9c39cc317b4f559309ba29e642168James Bottomley 2989927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (invalid) 2999927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 3009927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3019927c68864e9c39cc317b4f559309ba29e642168James Bottomley switch (proto) { 3029927c68864e9c39cc317b4f559309ba29e642168James Bottomley case SCSI_PROTOCOL_SAS: 3039927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (eip) 3049927c68864e9c39cc317b4f559309ba29e642168James Bottomley d = desc + 8; 3059927c68864e9c39cc317b4f559309ba29e642168James Bottomley else 3069927c68864e9c39cc317b4f559309ba29e642168James Bottomley d = desc + 4; 3079927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* only take the phy0 addr */ 3089927c68864e9c39cc317b4f559309ba29e642168James Bottomley addr = (u64)d[12] << 56 | 3099927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[13] << 48 | 3109927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[14] << 40 | 3119927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[15] << 32 | 3129927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[16] << 24 | 3139927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[17] << 16 | 3149927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[18] << 8 | 3159927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)d[19]; 3169927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 3179927c68864e9c39cc317b4f559309ba29e642168James Bottomley default: 3189927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* FIXME: Need to add more protocols than just SAS */ 3199927c68864e9c39cc317b4f559309ba29e642168James Bottomley break; 3209927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3219927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp->addr = addr; 3229927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 3239927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3249927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct efd { 3259927c68864e9c39cc317b4f559309ba29e642168James Bottomley u64 addr; 3269927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct device *dev; 3279927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 3289927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3299927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_enclosure_find_by_addr(struct enclosure_device *edev, 3309927c68864e9c39cc317b4f559309ba29e642168James Bottomley void *data) 3319927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 3329927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct efd *efd = data; 3339927c68864e9c39cc317b4f559309ba29e642168James Bottomley int i; 3349927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_component *scomp; 3359927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3369927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!edev->component[0].scratch) 3379927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 3389927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3399927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < edev->components; i++) { 3409927c68864e9c39cc317b4f559309ba29e642168James Bottomley scomp = edev->component[i].scratch; 3419927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (scomp->addr != efd->addr) 3429927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 3439927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3449927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_add_device(edev, i, efd->dev); 3459927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 1; 3469927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3479927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 3489927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 3499927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3509927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_match_to_enclosure(struct enclosure_device *edev, 3519927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *sdev) 3529927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 35340c3460f3cad1672f22baadcdbe20b9b3200cc20Matthew Wilcox unsigned char *buf; 3549927c68864e9c39cc317b4f559309ba29e642168James Bottomley unsigned char *desc; 35540c3460f3cad1672f22baadcdbe20b9b3200cc20Matthew Wilcox unsigned int vpd_len; 3569927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct efd efd = { 3579927c68864e9c39cc317b4f559309ba29e642168James Bottomley .addr = 0, 3589927c68864e9c39cc317b4f559309ba29e642168James Bottomley }; 3599927c68864e9c39cc317b4f559309ba29e642168James Bottomley 36040c3460f3cad1672f22baadcdbe20b9b3200cc20Matthew Wilcox buf = scsi_get_vpd_page(sdev, 0x83); 3619927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 3629927c68864e9c39cc317b4f559309ba29e642168James Bottomley return; 3639927c68864e9c39cc317b4f559309ba29e642168James Bottomley 36440c3460f3cad1672f22baadcdbe20b9b3200cc20Matthew Wilcox vpd_len = ((buf[2] << 8) | buf[3]) + 4; 365671a99c8eb2f1dde08ac5538d8cd912047c61ddfJames Bottomley 3669927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc = buf + 4; 367671a99c8eb2f1dde08ac5538d8cd912047c61ddfJames Bottomley while (desc < buf + vpd_len) { 3689927c68864e9c39cc317b4f559309ba29e642168James Bottomley enum scsi_protocol proto = desc[0] >> 4; 3699927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 code_set = desc[0] & 0x0f; 3709927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 piv = desc[1] & 0x80; 3719927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 assoc = (desc[1] & 0x30) >> 4; 3729927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 type = desc[1] & 0x0f; 3739927c68864e9c39cc317b4f559309ba29e642168James Bottomley u8 len = desc[3]; 3749927c68864e9c39cc317b4f559309ba29e642168James Bottomley 375b3f1f9aa082b2ab86dec4db3d8b1566af345387eRoel Kluin if (piv && code_set == 1 && assoc == 1 3769927c68864e9c39cc317b4f559309ba29e642168James Bottomley && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8) 3779927c68864e9c39cc317b4f559309ba29e642168James Bottomley efd.addr = (u64)desc[4] << 56 | 3789927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[5] << 48 | 3799927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[6] << 40 | 3809927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[7] << 32 | 3819927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[8] << 24 | 3829927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[9] << 16 | 3839927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[10] << 8 | 3849927c68864e9c39cc317b4f559309ba29e642168James Bottomley (u64)desc[11]; 3859927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3869927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc += len + 4; 3879927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 3889927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!efd.addr) 3899927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto free; 3909927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3919927c68864e9c39cc317b4f559309ba29e642168James Bottomley efd.dev = &sdev->sdev_gendev; 3929927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3939927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_for_each_device(ses_enclosure_find_by_addr, &efd); 3949927c68864e9c39cc317b4f559309ba29e642168James Bottomley free: 3959927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 3969927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 3979927c68864e9c39cc317b4f559309ba29e642168James Bottomley 3989927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define INIT_ALLOC_SIZE 32 3999927c68864e9c39cc317b4f559309ba29e642168James Bottomley 400ee959b00c335d7780136c5abda37809191fe52c3Tony Jonesstatic int ses_intf_add(struct device *cdev, 4019927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct class_interface *intf) 4029927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 403ee959b00c335d7780136c5abda37809191fe52c3Tony Jones struct scsi_device *sdev = to_scsi_device(cdev->parent); 4049927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct scsi_device *tmp_sdev; 405691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL, 406691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu *addl_desc_ptr = NULL; 4079927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev; 4089927c68864e9c39cc317b4f559309ba29e642168James Bottomley u32 result; 409691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu int i, j, types, len, page7_len = 0, components = 0; 4109927c68864e9c39cc317b4f559309ba29e642168James Bottomley int err = -ENOMEM; 4119927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_device *edev; 4127c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu struct ses_component *scomp = NULL; 4139927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4149927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scsi_device_enclosure(sdev)) { 4159927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* not an enclosure, but might be in one */ 416163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley struct enclosure_device *prev = NULL; 417163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley 418163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { 4199927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_match_to_enclosure(edev, sdev); 420163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley prev = edev; 4219927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4229927c68864e9c39cc317b4f559309ba29e642168James Bottomley return -ENODEV; 4239927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4249927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4259927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* TYPE_ENCLOSURE prints a message in probe */ 4269927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (sdev->type != TYPE_ENCLOSURE) 4279927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n"); 4289927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4299927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL); 4309927c68864e9c39cc317b4f559309ba29e642168James Bottomley hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); 4319927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!hdr_buf || !ses_dev) 4329927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_init_free; 4339927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4349927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE); 4359927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4369927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4379927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4389927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (hdr_buf[1] != 0) { 4399927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* FIXME: need subenclosure support; I've just never 4409927c68864e9c39cc317b4f559309ba29e642168James Bottomley * seen a device with subenclosures and it makes the 4419927c68864e9c39cc317b4f559309ba29e642168James Bottomley * traversal routines more complex */ 4429927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, 4439927c68864e9c39cc317b4f559309ba29e642168James Bottomley "FIXME driver has no support for subenclosures (%d)\n", 444691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu hdr_buf[1]); 4459927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4469927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4479927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4489927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 4499927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len, GFP_KERNEL); 4509927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 4519927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4529927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4539927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 1, buf, len); 4549927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4559927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4569927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4579927c68864e9c39cc317b4f559309ba29e642168James Bottomley types = buf[10]; 4589927c68864e9c39cc317b4f559309ba29e642168James Bottomley 459691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu type_ptr = buf + 12 + buf[11]; 4609927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4619927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < types; i++, type_ptr += 4) { 4629927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 4639927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) 4649927c68864e9c39cc317b4f559309ba29e642168James Bottomley components += type_ptr[1]; 4659927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 4667c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu ses_dev->page1 = buf; 4677c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu ses_dev->page1_len = len; 4687c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu buf = NULL; 4699927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4709927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE); 4719927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4729927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4739927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4749927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 4759927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len, GFP_KERNEL); 4769927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!buf) 4779927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 4789927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4799927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* make sure getting page 2 actually works */ 4809927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 2, buf, len); 4819927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 4829927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto recv_failed; 4839927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev->page2 = buf; 4849927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_dev->page2_len = len; 4857c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu buf = NULL; 4869927c68864e9c39cc317b4f559309ba29e642168James Bottomley 4879927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* The additional information page --- allows us 4889927c68864e9c39cc317b4f559309ba29e642168James Bottomley * to match up the devices */ 4899927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE); 490691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!result) { 491691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 492691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 493691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu buf = kzalloc(len, GFP_KERNEL); 494691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!buf) 495691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu goto err_free; 496691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 497691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu result = ses_recv_diag(sdev, 10, buf, len); 498691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (result) 499691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu goto recv_failed; 500691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ses_dev->page10 = buf; 501691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ses_dev->page10_len = len; 502691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu buf = NULL; 503691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } 5049927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5057c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL); 5069927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (!scomp) 5077c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu goto err_free; 5089927c68864e9c39cc317b4f559309ba29e642168James Bottomley 50971610f55fa4db63dbf5385929a47c9fb2451f332Kay Sievers edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev), 5109927c68864e9c39cc317b4f559309ba29e642168James Bottomley components, &ses_enclosure_callbacks); 5119927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (IS_ERR(edev)) { 5129927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = PTR_ERR(edev); 5139927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto err_free; 5149927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5159927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5169927c68864e9c39cc317b4f559309ba29e642168James Bottomley edev->scratch = ses_dev; 5179927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < components; i++) 5187c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu edev->component[i].scratch = scomp + i; 5199927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5209927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* Page 7 for the descriptors is optional */ 5219927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); 5229927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) 5239927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto simple_populate; 5249927c68864e9c39cc317b4f559309ba29e642168James Bottomley 525691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; 5269927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* add 1 for trailing '\0' we'll use */ 5279927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = kzalloc(len + 1, GFP_KERNEL); 5287c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu if (!buf) 5297c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu goto simple_populate; 5309927c68864e9c39cc317b4f559309ba29e642168James Bottomley result = ses_recv_diag(sdev, 7, buf, len); 5319927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (result) { 5329927c68864e9c39cc317b4f559309ba29e642168James Bottomley simple_populate: 5339927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 5349927c68864e9c39cc317b4f559309ba29e642168James Bottomley buf = NULL; 5359927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr = NULL; 5369927c68864e9c39cc317b4f559309ba29e642168James Bottomley addl_desc_ptr = NULL; 5379927c68864e9c39cc317b4f559309ba29e642168James Bottomley } else { 5389927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr = buf + 8; 5399927c68864e9c39cc317b4f559309ba29e642168James Bottomley len = (desc_ptr[2] << 8) + desc_ptr[3]; 5409927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* skip past overall descriptor */ 5419927c68864e9c39cc317b4f559309ba29e642168James Bottomley desc_ptr += len + 4; 542691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (ses_dev->page10) 543691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu addl_desc_ptr = ses_dev->page10 + 8; 5449927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5459927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; 5469927c68864e9c39cc317b4f559309ba29e642168James Bottomley components = 0; 5479927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (i = 0; i < types; i++, type_ptr += 4) { 5489927c68864e9c39cc317b4f559309ba29e642168James Bottomley for (j = 0; j < type_ptr[1]; j++) { 5499927c68864e9c39cc317b4f559309ba29e642168James Bottomley char *name = NULL; 5509927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_component *ecomp; 5519927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5529927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (desc_ptr) { 553691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc_ptr >= buf + page7_len) { 554691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr = NULL; 555691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } else { 556691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu len = (desc_ptr[2] << 8) + desc_ptr[3]; 557691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr += 4; 558691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu /* Add trailing zero - pushes into 559691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu * reserved space */ 560691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr[len] = '\0'; 561691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu name = desc_ptr; 562691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu } 5639927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 564691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || 565691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { 566691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 567691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu ecomp = enclosure_component_register(edev, 5689927c68864e9c39cc317b4f559309ba29e642168James Bottomley components++, 5699927c68864e9c39cc317b4f559309ba29e642168James Bottomley type_ptr[0], 5709927c68864e9c39cc317b4f559309ba29e642168James Bottomley name); 571691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 572691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (!IS_ERR(ecomp) && addl_desc_ptr) 5739927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_process_descriptor(ecomp, 5749927c68864e9c39cc317b4f559309ba29e642168James Bottomley addl_desc_ptr); 5759927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 576691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (desc_ptr) 577691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu desc_ptr += len; 578691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 579691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu if (addl_desc_ptr) 580691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu addl_desc_ptr += addl_desc_ptr[1] + 2; 581691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu 5829927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5839927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5849927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 5859927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(hdr_buf); 5869927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5879927c68864e9c39cc317b4f559309ba29e642168James Bottomley /* see if there are any devices matching before 5889927c68864e9c39cc317b4f559309ba29e642168James Bottomley * we found the enclosure */ 5899927c68864e9c39cc317b4f559309ba29e642168James Bottomley shost_for_each_device(tmp_sdev, sdev->host) { 5909927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev)) 5919927c68864e9c39cc317b4f559309ba29e642168James Bottomley continue; 5929927c68864e9c39cc317b4f559309ba29e642168James Bottomley ses_match_to_enclosure(edev, tmp_sdev); 5939927c68864e9c39cc317b4f559309ba29e642168James Bottomley } 5949927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5959927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 5969927c68864e9c39cc317b4f559309ba29e642168James Bottomley 5979927c68864e9c39cc317b4f559309ba29e642168James Bottomley recv_failed: 5989927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n", 5999927c68864e9c39cc317b4f559309ba29e642168James Bottomley result); 6009927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = -ENODEV; 6019927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_free: 6029927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(buf); 6037c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu kfree(scomp); 6049927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page10); 6059927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page2); 6069927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev->page1); 6079927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_init_free: 6089927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(ses_dev); 6099927c68864e9c39cc317b4f559309ba29e642168James Bottomley kfree(hdr_buf); 6109927c68864e9c39cc317b4f559309ba29e642168James Bottomley sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err); 6119927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6129927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6139927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6149927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_remove(struct device *dev) 6159927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6169927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 6179927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6189927c68864e9c39cc317b4f559309ba29e642168James Bottomley 61943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove_component(struct scsi_device *sdev) 62043d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley{ 62143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley struct enclosure_device *edev, *prev = NULL; 62243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley 62343d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { 62443d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley prev = edev; 62543d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley if (!enclosure_remove_device(edev, &sdev->sdev_gendev)) 62643d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley break; 62743d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley } 62843d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley if (edev) 62943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley put_device(&edev->edev); 63043d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley} 63143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley 63243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove_enclosure(struct scsi_device *sdev) 6339927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6349927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct enclosure_device *edev; 6359927c68864e9c39cc317b4f559309ba29e642168James Bottomley struct ses_device *ses_dev; 6369927c68864e9c39cc317b4f559309ba29e642168James Bottomley 637163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley /* exact match to this enclosure */ 63843d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley edev = enclosure_find(&sdev->sdev_gendev, NULL); 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 652ee959b00c335d7780136c5abda37809191fe52c3Tony Jones put_device(&edev->edev); 6539927c68864e9c39cc317b4f559309ba29e642168James Bottomley enclosure_unregister(edev); 6549927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6559927c68864e9c39cc317b4f559309ba29e642168James Bottomley 65643d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove(struct device *cdev, 65743d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley struct class_interface *intf) 65843d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley{ 65943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley struct scsi_device *sdev = to_scsi_device(cdev->parent); 66043d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley 66143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley if (!scsi_device_enclosure(sdev)) 66243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley ses_intf_remove_component(sdev); 66343d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley else 66443d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley ses_intf_remove_enclosure(sdev); 66543d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley} 66643d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley 6679927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct class_interface ses_interface = { 668ee959b00c335d7780136c5abda37809191fe52c3Tony Jones .add_dev = ses_intf_add, 669ee959b00c335d7780136c5abda37809191fe52c3Tony Jones .remove_dev = ses_intf_remove, 6709927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 6719927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6729927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct scsi_driver ses_template = { 6739927c68864e9c39cc317b4f559309ba29e642168James Bottomley .owner = THIS_MODULE, 6749927c68864e9c39cc317b4f559309ba29e642168James Bottomley .gendrv = { 6759927c68864e9c39cc317b4f559309ba29e642168James Bottomley .name = "ses", 6769927c68864e9c39cc317b4f559309ba29e642168James Bottomley .probe = ses_probe, 6779927c68864e9c39cc317b4f559309ba29e642168James Bottomley .remove = ses_remove, 6789927c68864e9c39cc317b4f559309ba29e642168James Bottomley }, 6799927c68864e9c39cc317b4f559309ba29e642168James Bottomley}; 6809927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6819927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int __init ses_init(void) 6829927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 6839927c68864e9c39cc317b4f559309ba29e642168James Bottomley int err; 6849927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6859927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = scsi_register_interface(&ses_interface); 6869927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (err) 6879927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6889927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6899927c68864e9c39cc317b4f559309ba29e642168James Bottomley err = scsi_register_driver(&ses_template.gendrv); 6909927c68864e9c39cc317b4f559309ba29e642168James Bottomley if (err) 6919927c68864e9c39cc317b4f559309ba29e642168James Bottomley goto out_unreg; 6929927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6939927c68864e9c39cc317b4f559309ba29e642168James Bottomley return 0; 6949927c68864e9c39cc317b4f559309ba29e642168James Bottomley 6959927c68864e9c39cc317b4f559309ba29e642168James Bottomley out_unreg: 6969927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_interface(&ses_interface); 6979927c68864e9c39cc317b4f559309ba29e642168James Bottomley return err; 6989927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 6999927c68864e9c39cc317b4f559309ba29e642168James Bottomley 7009927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void __exit ses_exit(void) 7019927c68864e9c39cc317b4f559309ba29e642168James Bottomley{ 7029927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_driver(&ses_template.gendrv); 7039927c68864e9c39cc317b4f559309ba29e642168James Bottomley scsi_unregister_interface(&ses_interface); 7049927c68864e9c39cc317b4f559309ba29e642168James Bottomley} 7059927c68864e9c39cc317b4f559309ba29e642168James Bottomley 7069927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_init(ses_init); 7079927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_exit(ses_exit); 7089927c68864e9c39cc317b4f559309ba29e642168James Bottomley 7099927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE); 7109927c68864e9c39cc317b4f559309ba29e642168James Bottomley 7119927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_AUTHOR("James Bottomley"); 7129927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver"); 7139927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_LICENSE("GPL v2"); 714