ses.c revision 7c46c20aef185c3782d28c5111dcf1df88bbab32
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 {
369927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *page1;
379927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *page2;
389927c68864e9c39cc317b4f559309ba29e642168James Bottomley	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{
709927c68864e9c39cc317b4f559309ba29e642168James Bottomley	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
889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	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,
1079927c68864e9c39cc317b4f559309ba29e642168James Bottomley				      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;
1129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
1139927c68864e9c39cc317b4f559309ba29e642168James Bottomley	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
1369927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic 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;
1429927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
1439927c68864e9c39cc317b4f559309ba29e642168James Bottomley	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{
1639927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *desc;
1649927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1659927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
1669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ecomp->fault = (desc[3] & 0x60) >> 4;
1679927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1689927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1699927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_fault(struct enclosure_device *edev,
1709927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
1719927c68864e9c39cc317b4f559309ba29e642168James Bottomley			 enum enclosure_component_setting val)
1729927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
1739927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char desc[4] = {0 };
1749927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1759927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
1769927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
1779927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
1789927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
1799927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
1809927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc[2] = 0x02;
1819927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
1829927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
1839927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
1849927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
1859927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
1869927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1879927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
1889927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1899927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1909927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_status(struct enclosure_device *edev,
1919927c68864e9c39cc317b4f559309ba29e642168James Bottomley			   struct enclosure_component *ecomp)
1929927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
1939927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *desc;
1949927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1959927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
1969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ecomp->status = (desc[0] & 0x0f);
1979927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1989927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1999927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_locate(struct enclosure_device *edev,
2009927c68864e9c39cc317b4f559309ba29e642168James Bottomley			   struct enclosure_component *ecomp)
2019927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2029927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char *desc;
2039927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2049927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
2059927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ecomp->locate = (desc[2] & 0x02) ? 1 : 0;
2069927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2079927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2089927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_locate(struct enclosure_device *edev,
2099927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
2109927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  enum enclosure_component_setting val)
2119927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char desc[4] = {0 };
2139927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2149927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
2159927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
2169927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
2179927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2189927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
2199927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc[2] = 0x02;
2209927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2219927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
2229927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
2239927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
2249927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
2259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
2269927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2279927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2289927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_active(struct enclosure_device *edev,
2299927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
2309927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  enum enclosure_component_setting val)
2319927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2329927c68864e9c39cc317b4f559309ba29e642168James Bottomley	char desc[4] = {0 };
2339927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
2359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
2369927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
2379927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ecomp->active = 0;
2389927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2399927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
2409927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc[2] = 0x80;
2419927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ecomp->active = 1;
2429927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2439927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
2449927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
2459927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
2469927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
2479927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
2489927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2499927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2509927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct enclosure_component_callbacks ses_enclosure_callbacks = {
2519927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_fault		= ses_get_fault,
2529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_fault		= ses_set_fault,
2539927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_status		= ses_get_status,
2549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_locate		= ses_get_locate,
2559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_locate		= ses_set_locate,
2569927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_active		= ses_set_active,
2579927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
2589927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2599927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_host_edev {
2609927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct Scsi_Host *shost;
2619927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
2629927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
2639927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2649927c68864e9c39cc317b4f559309ba29e642168James Bottomleyint ses_match_host(struct enclosure_device *edev, void *data)
2659927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_host_edev *sed = data;
2679927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *sdev;
2689927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2699927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scsi_is_sdev_device(edev->cdev.dev))
2709927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
2719927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2729927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev = to_scsi_device(edev->cdev.dev);
2739927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2749927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (sdev->host != sed->shost)
2759927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
2769927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2779927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sed->edev = edev;
2789927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 1;
2799927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2809927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2819927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_process_descriptor(struct enclosure_component *ecomp,
2829927c68864e9c39cc317b4f559309ba29e642168James Bottomley				   unsigned char *desc)
2839927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2849927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int eip = desc[0] & 0x10;
2859927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int invalid = desc[0] & 0x80;
2869927c68864e9c39cc317b4f559309ba29e642168James Bottomley	enum scsi_protocol proto = desc[0] & 0x0f;
2879927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u64 addr = 0;
2889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_component *scomp = ecomp->scratch;
2899927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *d;
2909927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2919927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scomp->desc = desc;
2929927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2939927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (invalid)
2949927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
2959927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (proto) {
2979927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case SCSI_PROTOCOL_SAS:
2989927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (eip)
2999927c68864e9c39cc317b4f559309ba29e642168James Bottomley			d = desc + 8;
3009927c68864e9c39cc317b4f559309ba29e642168James Bottomley		else
3019927c68864e9c39cc317b4f559309ba29e642168James Bottomley			d = desc + 4;
3029927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* only take the phy0 addr */
3039927c68864e9c39cc317b4f559309ba29e642168James Bottomley		addr = (u64)d[12] << 56 |
3049927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[13] << 48 |
3059927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[14] << 40 |
3069927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[15] << 32 |
3079927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[16] << 24 |
3089927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[17] << 16 |
3099927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[18] << 8 |
3109927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[19];
3119927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
3129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
3139927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* FIXME: Need to add more protocols than just SAS */
3149927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
3159927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
3169927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scomp->addr = addr;
3179927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
3189927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3199927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct efd {
3209927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u64 addr;
3219927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct device *dev;
3229927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
3239927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3249927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_enclosure_find_by_addr(struct enclosure_device *edev,
3259927c68864e9c39cc317b4f559309ba29e642168James Bottomley				      void *data)
3269927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
3279927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct efd *efd = data;
3289927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int i;
3299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_component *scomp;
3309927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3319927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!edev->component[0].scratch)
3329927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
3339927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < edev->components; i++) {
3359927c68864e9c39cc317b4f559309ba29e642168James Bottomley		scomp = edev->component[i].scratch;
3369927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (scomp->addr != efd->addr)
3379927c68864e9c39cc317b4f559309ba29e642168James Bottomley			continue;
3389927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3399927c68864e9c39cc317b4f559309ba29e642168James Bottomley		enclosure_add_device(edev, i, efd->dev);
3409927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 1;
3419927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
3429927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
3439927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
3449927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3459927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define VPD_INQUIRY_SIZE 512
3469927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3479927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_match_to_enclosure(struct enclosure_device *edev,
3489927c68864e9c39cc317b4f559309ba29e642168James Bottomley				   struct scsi_device *sdev)
3499927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
3509927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *buf = kmalloc(VPD_INQUIRY_SIZE, GFP_KERNEL);
3519927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *desc;
3529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int len;
3539927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct efd efd = {
3549927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.addr = 0,
3559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	};
3569927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char cmd[] = {
3579927c68864e9c39cc317b4f559309ba29e642168James Bottomley		INQUIRY,
3589927c68864e9c39cc317b4f559309ba29e642168James Bottomley		1,
3599927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0x83,
3609927c68864e9c39cc317b4f559309ba29e642168James Bottomley		VPD_INQUIRY_SIZE >> 8,
3619927c68864e9c39cc317b4f559309ba29e642168James Bottomley		VPD_INQUIRY_SIZE & 0xff,
3629927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0
3639927c68864e9c39cc317b4f559309ba29e642168James Bottomley	};
3649927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3659927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
3669927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
3679927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3689927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
3699927c68864e9c39cc317b4f559309ba29e642168James Bottomley			     VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES))
3709927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto free;
3719927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3729927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (buf[2] << 8) + buf[3];
3739927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = buf + 4;
3749927c68864e9c39cc317b4f559309ba29e642168James Bottomley	while (desc < buf + len) {
3759927c68864e9c39cc317b4f559309ba29e642168James Bottomley		enum scsi_protocol proto = desc[0] >> 4;
3769927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 code_set = desc[0] & 0x0f;
3779927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 piv = desc[1] & 0x80;
3789927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 assoc = (desc[1] & 0x30) >> 4;
3799927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 type = desc[1] & 0x0f;
3809927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 len = desc[3];
3819927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3829927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (piv && code_set == 1 && assoc == 1 && code_set == 1
3839927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8)
3849927c68864e9c39cc317b4f559309ba29e642168James Bottomley			efd.addr = (u64)desc[4] << 56 |
3859927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[5] << 48 |
3869927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[6] << 40 |
3879927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[7] << 32 |
3889927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[8] << 24 |
3899927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[9] << 16 |
3909927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[10] << 8 |
3919927c68864e9c39cc317b4f559309ba29e642168James Bottomley				(u64)desc[11];
3929927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3939927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc += len + 4;
3949927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
3959927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!efd.addr)
3969927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto free;
3979927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3989927c68864e9c39cc317b4f559309ba29e642168James Bottomley	efd.dev = &sdev->sdev_gendev;
3999927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4009927c68864e9c39cc317b4f559309ba29e642168James Bottomley	enclosure_for_each_device(ses_enclosure_find_by_addr, &efd);
4019927c68864e9c39cc317b4f559309ba29e642168James Bottomley free:
4029927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(buf);
4039927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
4049927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4059927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define INIT_ALLOC_SIZE 32
4069927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4079927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_intf_add(struct class_device *cdev,
4089927c68864e9c39cc317b4f559309ba29e642168James Bottomley			struct class_interface *intf)
4099927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
4109927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *sdev = to_scsi_device(cdev->dev);
4119927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *tmp_sdev;
4129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr,
4139927c68864e9c39cc317b4f559309ba29e642168James Bottomley		*addl_desc_ptr;
4149927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev;
4159927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u32 result;
4169927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int i, j, types, len, components = 0;
4179927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int err = -ENOMEM;
4189927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
4197c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	struct ses_component *scomp = NULL;
4209927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4219927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scsi_device_enclosure(sdev)) {
4229927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* not an enclosure, but might be in one */
4237c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		edev = enclosure_find(&sdev->host->shost_gendev);
4249927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (edev) {
4259927c68864e9c39cc317b4f559309ba29e642168James Bottomley			ses_match_to_enclosure(edev, sdev);
4269927c68864e9c39cc317b4f559309ba29e642168James Bottomley			class_device_put(&edev->cdev);
4279927c68864e9c39cc317b4f559309ba29e642168James Bottomley		}
4289927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -ENODEV;
4299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
4309927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4319927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* TYPE_ENCLOSURE prints a message in probe */
4329927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (sdev->type != TYPE_ENCLOSURE)
4339927c68864e9c39cc317b4f559309ba29e642168James Bottomley		sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n");
4349927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL);
4369927c68864e9c39cc317b4f559309ba29e642168James Bottomley	hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
4379927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!hdr_buf || !ses_dev)
4389927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_init_free;
4399927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4409927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE);
4419927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
4429927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
4439927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4449927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (hdr_buf[1] != 0) {
4459927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* FIXME: need subenclosure support; I've just never
4469927c68864e9c39cc317b4f559309ba29e642168James Bottomley		 * seen a device with subenclosures and it makes the
4479927c68864e9c39cc317b4f559309ba29e642168James Bottomley		 * traversal routines more complex */
4489927c68864e9c39cc317b4f559309ba29e642168James Bottomley		sdev_printk(KERN_ERR, sdev,
4499927c68864e9c39cc317b4f559309ba29e642168James Bottomley			"FIXME driver has no support for subenclosures (%d)\n",
4509927c68864e9c39cc317b4f559309ba29e642168James Bottomley			buf[1]);
4519927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
4529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
4539927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
4559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len, GFP_KERNEL);
4569927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
4579927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
4589927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4599927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 1, buf, len);
4609927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
4619927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
4629927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4639927c68864e9c39cc317b4f559309ba29e642168James Bottomley	types = buf[10];
4649927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = buf[11];
4659927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	type_ptr = buf + 12 + len;
4679927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4689927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < types; i++, type_ptr += 4) {
4699927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
4709927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE)
4719927c68864e9c39cc317b4f559309ba29e642168James Bottomley			components += type_ptr[1];
4729927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
4737c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	ses_dev->page1 = buf;
4747c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	ses_dev->page1_len = len;
4757c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	buf = NULL;
4769927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4779927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE);
4789927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
4799927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
4809927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4819927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
4829927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len, GFP_KERNEL);
4839927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
4849927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
4859927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4869927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* make sure getting page 2 actually works */
4879927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 2, buf, len);
4889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
4899927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
4909927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page2 = buf;
4919927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page2_len = len;
4927c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	buf = NULL;
4939927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4949927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* The additional information page --- allows us
4959927c68864e9c39cc317b4f559309ba29e642168James Bottomley	 * to match up the devices */
4969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE);
4979927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
4989927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto no_page10;
4999927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5009927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
5019927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len, GFP_KERNEL);
5029927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
5039927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
5049927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5059927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 10, buf, len);
5069927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5079927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
5089927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page10 = buf;
5099927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page10_len = len;
5107c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	buf = NULL;
5119927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5129927c68864e9c39cc317b4f559309ba29e642168James Bottomley no_page10:
5137c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL);
5149927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scomp)
5157c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		goto err_free;
5169927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5179927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev = enclosure_register(cdev->dev, sdev->sdev_gendev.bus_id,
5189927c68864e9c39cc317b4f559309ba29e642168James Bottomley				  components, &ses_enclosure_callbacks);
5199927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (IS_ERR(edev)) {
5209927c68864e9c39cc317b4f559309ba29e642168James Bottomley		err = PTR_ERR(edev);
5219927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
5229927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5239927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5249927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev->scratch = ses_dev;
5259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < components; i++)
5267c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		edev->component[i].scratch = scomp + i;
5279927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5289927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* Page 7 for the descriptors is optional */
5299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
5309927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5319927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto simple_populate;
5329927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5339927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
5349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* add 1 for trailing '\0' we'll use */
5359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len + 1, GFP_KERNEL);
5367c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	if (!buf)
5377c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		goto simple_populate;
5389927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 7, buf, len);
5399927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result) {
5409927c68864e9c39cc317b4f559309ba29e642168James Bottomley simple_populate:
5419927c68864e9c39cc317b4f559309ba29e642168James Bottomley		kfree(buf);
5429927c68864e9c39cc317b4f559309ba29e642168James Bottomley		buf = NULL;
5439927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc_ptr = NULL;
5449927c68864e9c39cc317b4f559309ba29e642168James Bottomley		addl_desc_ptr = NULL;
5459927c68864e9c39cc317b4f559309ba29e642168James Bottomley	} else {
5469927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc_ptr = buf + 8;
5479927c68864e9c39cc317b4f559309ba29e642168James Bottomley		len = (desc_ptr[2] << 8) + desc_ptr[3];
5489927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* skip past overall descriptor */
5499927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc_ptr += len + 4;
5509927c68864e9c39cc317b4f559309ba29e642168James Bottomley		addl_desc_ptr = ses_dev->page10 + 8;
5519927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
5539927c68864e9c39cc317b4f559309ba29e642168James Bottomley	components = 0;
5549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < types; i++, type_ptr += 4) {
5559927c68864e9c39cc317b4f559309ba29e642168James Bottomley		for (j = 0; j < type_ptr[1]; j++) {
5569927c68864e9c39cc317b4f559309ba29e642168James Bottomley			char *name = NULL;
5579927c68864e9c39cc317b4f559309ba29e642168James Bottomley			struct enclosure_component *ecomp;
5589927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5599927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (desc_ptr) {
5609927c68864e9c39cc317b4f559309ba29e642168James Bottomley				len = (desc_ptr[2] << 8) + desc_ptr[3];
5619927c68864e9c39cc317b4f559309ba29e642168James Bottomley				desc_ptr += 4;
5629927c68864e9c39cc317b4f559309ba29e642168James Bottomley				/* Add trailing zero - pushes into
5639927c68864e9c39cc317b4f559309ba29e642168James Bottomley				 * reserved space */
5649927c68864e9c39cc317b4f559309ba29e642168James Bottomley				desc_ptr[len] = '\0';
5659927c68864e9c39cc317b4f559309ba29e642168James Bottomley				name = desc_ptr;
5669927c68864e9c39cc317b4f559309ba29e642168James Bottomley			}
5679927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
5689927c68864e9c39cc317b4f559309ba29e642168James Bottomley			    type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
5699927c68864e9c39cc317b4f559309ba29e642168James Bottomley				continue;
5709927c68864e9c39cc317b4f559309ba29e642168James Bottomley			ecomp =	enclosure_component_register(edev,
5719927c68864e9c39cc317b4f559309ba29e642168James Bottomley							     components++,
5729927c68864e9c39cc317b4f559309ba29e642168James Bottomley							     type_ptr[0],
5739927c68864e9c39cc317b4f559309ba29e642168James Bottomley							     name);
5749927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (desc_ptr) {
5759927c68864e9c39cc317b4f559309ba29e642168James Bottomley				desc_ptr += len;
5769927c68864e9c39cc317b4f559309ba29e642168James Bottomley				if (!IS_ERR(ecomp))
5779927c68864e9c39cc317b4f559309ba29e642168James Bottomley					ses_process_descriptor(ecomp,
5789927c68864e9c39cc317b4f559309ba29e642168James Bottomley							       addl_desc_ptr);
5799927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5809927c68864e9c39cc317b4f559309ba29e642168James Bottomley				if (addl_desc_ptr)
5819927c68864e9c39cc317b4f559309ba29e642168James Bottomley					addl_desc_ptr += addl_desc_ptr[1] + 2;
5829927c68864e9c39cc317b4f559309ba29e642168James Bottomley			}
5839927c68864e9c39cc317b4f559309ba29e642168James Bottomley		}
5849927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5859927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(buf);
5869927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(hdr_buf);
5879927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* see if there are any devices matching before
5899927c68864e9c39cc317b4f559309ba29e642168James Bottomley	 * we found the enclosure */
5909927c68864e9c39cc317b4f559309ba29e642168James Bottomley	shost_for_each_device(tmp_sdev, sdev->host) {
5919927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
5929927c68864e9c39cc317b4f559309ba29e642168James Bottomley			continue;
5939927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ses_match_to_enclosure(edev, tmp_sdev);
5949927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5959927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
5979927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5989927c68864e9c39cc317b4f559309ba29e642168James Bottomley recv_failed:
5999927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n",
6009927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    result);
6019927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = -ENODEV;
6029927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_free:
6039927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(buf);
6047c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	kfree(scomp);
6059927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page10);
6069927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page2);
6079927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page1);
6089927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_init_free:
6099927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev);
6109927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(hdr_buf);
6119927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err);
6129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return err;
6139927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6149927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6159927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_remove(struct device *dev)
6169927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6179927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
6189927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6199927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6209927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_intf_remove(struct class_device *cdev,
6219927c68864e9c39cc317b4f559309ba29e642168James Bottomley			    struct class_interface *intf)
6229927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6239927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *sdev = to_scsi_device(cdev->dev);
6249927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
6259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev;
6269927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6279927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scsi_device_enclosure(sdev))
6289927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
6299927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6309927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev = enclosure_find(cdev->dev);
6319927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!edev)
6329927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
6339927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev = edev->scratch;
6359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev->scratch = NULL;
6369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6377c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	kfree(ses_dev->page10);
6389927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page1);
6399927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page2);
6409927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev);
6419927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6429927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(edev->component[0].scratch);
6439927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6449927c68864e9c39cc317b4f559309ba29e642168James Bottomley	class_device_put(&edev->cdev);
6459927c68864e9c39cc317b4f559309ba29e642168James Bottomley	enclosure_unregister(edev);
6469927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6479927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6489927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct class_interface ses_interface = {
6499927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.add	= ses_intf_add,
6509927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.remove	= ses_intf_remove,
6519927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
6529927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6539927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct scsi_driver ses_template = {
6549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.owner			= THIS_MODULE,
6559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.gendrv = {
6569927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.name		= "ses",
6579927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.probe		= ses_probe,
6589927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.remove		= ses_remove,
6599927c68864e9c39cc317b4f559309ba29e642168James Bottomley	},
6609927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
6619927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6629927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int __init ses_init(void)
6639927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6649927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int err;
6659927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = scsi_register_interface(&ses_interface);
6679927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (err)
6689927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return err;
6699927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6709927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = scsi_register_driver(&ses_template.gendrv);
6719927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (err)
6729927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto out_unreg;
6739927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6749927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
6759927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6769927c68864e9c39cc317b4f559309ba29e642168James Bottomley out_unreg:
6779927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_interface(&ses_interface);
6789927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return err;
6799927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6809927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6819927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void __exit ses_exit(void)
6829927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6839927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_driver(&ses_template.gendrv);
6849927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_interface(&ses_interface);
6859927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6869927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6879927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_init(ses_init);
6889927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_exit(ses_exit);
6899927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6909927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE);
6919927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6929927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_AUTHOR("James Bottomley");
6939927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver");
6949927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_LICENSE("GPL v2");
695