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
245a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
259927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/module.h>
269927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/kernel.h>
279927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <linux/enclosure.h>
28c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke#include <asm/unaligned.h>
299927c68864e9c39cc317b4f559309ba29e642168James Bottomley
309927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi.h>
319927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_cmnd.h>
329927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_dbg.h>
339927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_device.h>
349927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_driver.h>
359927c68864e9c39cc317b4f559309ba29e642168James Bottomley#include <scsi/scsi_host.h>
369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
379927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_device {
38691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *page1;
398c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	unsigned char *page1_types;
40691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *page2;
41691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *page10;
429927c68864e9c39cc317b4f559309ba29e642168James Bottomley	short page1_len;
438c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	short page1_num_types;
449927c68864e9c39cc317b4f559309ba29e642168James Bottomley	short page2_len;
459927c68864e9c39cc317b4f559309ba29e642168James Bottomley	short page10_len;
469927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
479927c68864e9c39cc317b4f559309ba29e642168James Bottomley
489927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_component {
499927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u64 addr;
509927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *desc;
519927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
529927c68864e9c39cc317b4f559309ba29e642168James Bottomley
539927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_probe(struct device *dev)
549927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *sdev = to_scsi_device(dev);
569927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int err = -ENODEV;
579927c68864e9c39cc317b4f559309ba29e642168James Bottomley
589927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (sdev->type != TYPE_ENCLOSURE)
599927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto out;
609927c68864e9c39cc317b4f559309ba29e642168James Bottomley
619927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = 0;
629927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n");
639927c68864e9c39cc317b4f559309ba29e642168James Bottomley
649927c68864e9c39cc317b4f559309ba29e642168James Bottomley out:
659927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return err;
669927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
679927c68864e9c39cc317b4f559309ba29e642168James Bottomley
68c95e62ce8905aab62fed224eaaa9b8558a0ef652Matthew Wilcox#define SES_TIMEOUT (30 * HZ)
699927c68864e9c39cc317b4f559309ba29e642168James Bottomley#define SES_RETRIES 3
709927c68864e9c39cc317b4f559309ba29e642168James Bottomley
719927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_recv_diag(struct scsi_device *sdev, int page_code,
729927c68864e9c39cc317b4f559309ba29e642168James Bottomley			 void *buf, int bufflen)
739927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
74691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char cmd[] = {
759927c68864e9c39cc317b4f559309ba29e642168James Bottomley		RECEIVE_DIAGNOSTIC,
769927c68864e9c39cc317b4f559309ba29e642168James Bottomley		1,		/* Set PCV bit */
779927c68864e9c39cc317b4f559309ba29e642168James Bottomley		page_code,
789927c68864e9c39cc317b4f559309ba29e642168James Bottomley		bufflen >> 8,
799927c68864e9c39cc317b4f559309ba29e642168James Bottomley		bufflen & 0xff,
809927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0
819927c68864e9c39cc317b4f559309ba29e642168James Bottomley	};
829927c68864e9c39cc317b4f559309ba29e642168James Bottomley
839927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
84f4f4e47e4af6b02dd1c425b931c65d0165356e33FUJITA Tomonori				NULL, SES_TIMEOUT, SES_RETRIES, NULL);
859927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
869927c68864e9c39cc317b4f559309ba29e642168James Bottomley
879927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_send_diag(struct scsi_device *sdev, int page_code,
889927c68864e9c39cc317b4f559309ba29e642168James Bottomley			 void *buf, int bufflen)
899927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
909927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u32 result;
919927c68864e9c39cc317b4f559309ba29e642168James Bottomley
92691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char cmd[] = {
939927c68864e9c39cc317b4f559309ba29e642168James Bottomley		SEND_DIAGNOSTIC,
949927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0x10,		/* Set PF bit */
959927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0,
969927c68864e9c39cc317b4f559309ba29e642168James Bottomley		bufflen >> 8,
979927c68864e9c39cc317b4f559309ba29e642168James Bottomley		bufflen & 0xff,
989927c68864e9c39cc317b4f559309ba29e642168James Bottomley		0
999927c68864e9c39cc317b4f559309ba29e642168James Bottomley	};
1009927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1019927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen,
102f4f4e47e4af6b02dd1c425b931c65d0165356e33FUJITA Tomonori				  NULL, SES_TIMEOUT, SES_RETRIES, NULL);
1039927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
1049927c68864e9c39cc317b4f559309ba29e642168James Bottomley		sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
1059927c68864e9c39cc317b4f559309ba29e642168James Bottomley			    result);
1069927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return result;
1079927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1089927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1099927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_page2_descriptor(struct enclosure_device *edev,
1109927c68864e9c39cc317b4f559309ba29e642168James Bottomley				      struct enclosure_component *ecomp,
111691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu				      unsigned char *desc)
1129927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
1139927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int i, j, count = 0, descriptor = ecomp->number;
114ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
1159927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev = edev->scratch;
1168c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	unsigned char *type_ptr = ses_dev->page1_types;
117691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *desc_ptr = ses_dev->page2 + 8;
1189927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1199927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* Clear everything */
1209927c68864e9c39cc317b4f559309ba29e642168James Bottomley	memset(desc_ptr, 0, ses_dev->page2_len - 8);
1218c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) {
1229927c68864e9c39cc317b4f559309ba29e642168James Bottomley		for (j = 0; j < type_ptr[1]; j++) {
1239927c68864e9c39cc317b4f559309ba29e642168James Bottomley			desc_ptr += 4;
1249927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
1259927c68864e9c39cc317b4f559309ba29e642168James Bottomley			    type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
1269927c68864e9c39cc317b4f559309ba29e642168James Bottomley				continue;
1279927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (count++ == descriptor) {
1289927c68864e9c39cc317b4f559309ba29e642168James Bottomley				memcpy(desc_ptr, desc, 4);
1299927c68864e9c39cc317b4f559309ba29e642168James Bottomley				/* set select */
1309927c68864e9c39cc317b4f559309ba29e642168James Bottomley				desc_ptr[0] |= 0x80;
1319927c68864e9c39cc317b4f559309ba29e642168James Bottomley				/* clear reserved, just in case */
1329927c68864e9c39cc317b4f559309ba29e642168James Bottomley				desc_ptr[0] &= 0xf0;
1339927c68864e9c39cc317b4f559309ba29e642168James Bottomley			}
1349927c68864e9c39cc317b4f559309ba29e642168James Bottomley		}
1359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
1369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1379927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
1389927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1399927c68864e9c39cc317b4f559309ba29e642168James Bottomley
140691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lustatic unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev,
1419927c68864e9c39cc317b4f559309ba29e642168James Bottomley				      struct enclosure_component *ecomp)
1429927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
1439927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int i, j, count = 0, descriptor = ecomp->number;
144ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
1459927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev = edev->scratch;
1468c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	unsigned char *type_ptr = ses_dev->page1_types;
147691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *desc_ptr = ses_dev->page2 + 8;
1489927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1499927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
1509927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1518c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) {
1529927c68864e9c39cc317b4f559309ba29e642168James Bottomley		for (j = 0; j < type_ptr[1]; j++) {
1539927c68864e9c39cc317b4f559309ba29e642168James Bottomley			desc_ptr += 4;
1549927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
1559927c68864e9c39cc317b4f559309ba29e642168James Bottomley			    type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
1569927c68864e9c39cc317b4f559309ba29e642168James Bottomley				continue;
1579927c68864e9c39cc317b4f559309ba29e642168James Bottomley			if (count++ == descriptor)
1589927c68864e9c39cc317b4f559309ba29e642168James Bottomley				return desc_ptr;
1599927c68864e9c39cc317b4f559309ba29e642168James Bottomley		}
1609927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
1619927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return NULL;
1629927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1639927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1642a350cab9daf9a46322d83b091bb05cf54ccf6abDouglas Gilbert/* For device slot and array device slot elements, byte 3 bit 6
1652a350cab9daf9a46322d83b091bb05cf54ccf6abDouglas Gilbert * is "fault sensed" while byte 3 bit 5 is "fault reqstd". As this
1662a350cab9daf9a46322d83b091bb05cf54ccf6abDouglas Gilbert * code stands these bits are shifted 4 positions right so in
1672a350cab9daf9a46322d83b091bb05cf54ccf6abDouglas Gilbert * sysfs they will appear as bits 2 and 1 respectively. Strange. */
1689927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_fault(struct enclosure_device *edev,
1699927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp)
1709927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
171691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *desc;
1729927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1739927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
174691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	if (desc)
175691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		ecomp->fault = (desc[3] & 0x60) >> 4;
1769927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1779927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1789927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_fault(struct enclosure_device *edev,
1799927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
1809927c68864e9c39cc317b4f559309ba29e642168James Bottomley			 enum enclosure_component_setting val)
1819927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
182691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char desc[4] = {0 };
1839927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1849927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
1859927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
1869927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
1879927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
1889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
1892a350cab9daf9a46322d83b091bb05cf54ccf6abDouglas Gilbert		desc[3] = 0x20;
1909927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
1919927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
1929927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
1939927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
1949927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
1959927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
1979927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
1989927c68864e9c39cc317b4f559309ba29e642168James Bottomley
1999927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_status(struct enclosure_device *edev,
2009927c68864e9c39cc317b4f559309ba29e642168James Bottomley			   struct enclosure_component *ecomp)
2019927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
202691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *desc;
2039927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2049927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
205691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	if (desc)
206691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		ecomp->status = (desc[0] & 0x0f);
2079927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2089927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2099927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_get_locate(struct enclosure_device *edev,
2109927c68864e9c39cc317b4f559309ba29e642168James Bottomley			   struct enclosure_component *ecomp)
2119927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
212691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char *desc;
2139927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2149927c68864e9c39cc317b4f559309ba29e642168James Bottomley	desc = ses_get_page2_descriptor(edev, ecomp);
215691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	if (desc)
216691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		ecomp->locate = (desc[2] & 0x02) ? 1 : 0;
2179927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2189927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2199927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_locate(struct enclosure_device *edev,
2209927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
2219927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  enum enclosure_component_setting val)
2229927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
223691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char desc[4] = {0 };
2249927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
2269927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
2279927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
2289927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
2309927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc[2] = 0x02;
2319927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2329927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
2339927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
2349927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
2359927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
2369927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
2379927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2389927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2399927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_set_active(struct enclosure_device *edev,
2409927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  struct enclosure_component *ecomp,
2419927c68864e9c39cc317b4f559309ba29e642168James Bottomley			  enum enclosure_component_setting val)
2429927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
243691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	unsigned char desc[4] = {0 };
2449927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2459927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (val) {
2469927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_DISABLED:
2479927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* zero is disabled */
2489927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ecomp->active = 0;
2499927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2509927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case ENCLOSURE_SETTING_ENABLED:
2519927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc[2] = 0x80;
2529927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ecomp->active = 1;
2539927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
2549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
2559927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* SES doesn't do the SGPIO blink settings */
2569927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -EINVAL;
2579927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
2589927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return ses_set_page2_descriptor(edev, ecomp, desc);
2599927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
2609927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2619927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct enclosure_component_callbacks ses_enclosure_callbacks = {
2629927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_fault		= ses_get_fault,
2639927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_fault		= ses_set_fault,
2649927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_status		= ses_get_status,
2659927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.get_locate		= ses_get_locate,
2669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_locate		= ses_set_locate,
2679927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.set_active		= ses_set_active,
2689927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
2699927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2709927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct ses_host_edev {
2719927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct Scsi_Host *shost;
2729927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
2739927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
2749927c68864e9c39cc317b4f559309ba29e642168James Bottomley
275e0aae1a53133f0d7833c8f358a0ccc7055fc5b28Adrian Bunk#if 0
2769927c68864e9c39cc317b4f559309ba29e642168James Bottomleyint ses_match_host(struct enclosure_device *edev, void *data)
2779927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2789927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_host_edev *sed = data;
2799927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *sdev;
2809927c68864e9c39cc317b4f559309ba29e642168James Bottomley
281ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	if (!scsi_is_sdev_device(edev->edev.parent))
2829927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
2839927c68864e9c39cc317b4f559309ba29e642168James Bottomley
284ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	sdev = to_scsi_device(edev->edev.parent);
2859927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2869927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (sdev->host != sed->shost)
2879927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
2889927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2899927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sed->edev = edev;
2909927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 1;
2919927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
292e0aae1a53133f0d7833c8f358a0ccc7055fc5b28Adrian Bunk#endif  /*  0  */
2939927c68864e9c39cc317b4f559309ba29e642168James Bottomley
2949927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_process_descriptor(struct enclosure_component *ecomp,
2959927c68864e9c39cc317b4f559309ba29e642168James Bottomley				   unsigned char *desc)
2969927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
2979927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int eip = desc[0] & 0x10;
2989927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int invalid = desc[0] & 0x80;
2999927c68864e9c39cc317b4f559309ba29e642168James Bottomley	enum scsi_protocol proto = desc[0] & 0x0f;
3009927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u64 addr = 0;
3019927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_component *scomp = ecomp->scratch;
3029927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *d;
3039927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3049927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scomp->desc = desc;
3059927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3069927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (invalid)
3079927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
3089927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3099927c68864e9c39cc317b4f559309ba29e642168James Bottomley	switch (proto) {
3109927c68864e9c39cc317b4f559309ba29e642168James Bottomley	case SCSI_PROTOCOL_SAS:
3119927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (eip)
3129927c68864e9c39cc317b4f559309ba29e642168James Bottomley			d = desc + 8;
3139927c68864e9c39cc317b4f559309ba29e642168James Bottomley		else
3149927c68864e9c39cc317b4f559309ba29e642168James Bottomley			d = desc + 4;
3159927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* only take the phy0 addr */
3169927c68864e9c39cc317b4f559309ba29e642168James Bottomley		addr = (u64)d[12] << 56 |
3179927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[13] << 48 |
3189927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[14] << 40 |
3199927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[15] << 32 |
3209927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[16] << 24 |
3219927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[17] << 16 |
3229927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[18] << 8 |
3239927c68864e9c39cc317b4f559309ba29e642168James Bottomley			(u64)d[19];
3249927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
3259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	default:
3269927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* FIXME: Need to add more protocols than just SAS */
3279927c68864e9c39cc317b4f559309ba29e642168James Bottomley		break;
3289927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
3299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scomp->addr = addr;
3309927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
3319927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3329927c68864e9c39cc317b4f559309ba29e642168James Bottomleystruct efd {
3339927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u64 addr;
3349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct device *dev;
3359927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
3369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3379927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_enclosure_find_by_addr(struct enclosure_device *edev,
3389927c68864e9c39cc317b4f559309ba29e642168James Bottomley				      void *data)
3399927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
3409927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct efd *efd = data;
3419927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int i;
3429927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_component *scomp;
3439927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3449927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!edev->component[0].scratch)
3459927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 0;
3469927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3479927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < edev->components; i++) {
3489927c68864e9c39cc317b4f559309ba29e642168James Bottomley		scomp = edev->component[i].scratch;
3499927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (scomp->addr != efd->addr)
3509927c68864e9c39cc317b4f559309ba29e642168James Bottomley			continue;
3519927c68864e9c39cc317b4f559309ba29e642168James Bottomley
3529927c68864e9c39cc317b4f559309ba29e642168James Bottomley		enclosure_add_device(edev, i, efd->dev);
3539927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return 1;
3549927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
3559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
3569927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
3579927c68864e9c39cc317b4f559309ba29e642168James Bottomley
35821fab1d0595eacf781705ec3509012a28f298245James Bottomley#define INIT_ALLOC_SIZE 32
35921fab1d0595eacf781705ec3509012a28f298245James Bottomley
36021fab1d0595eacf781705ec3509012a28f298245James Bottomleystatic void ses_enclosure_data_process(struct enclosure_device *edev,
36121fab1d0595eacf781705ec3509012a28f298245James Bottomley				       struct scsi_device *sdev,
36221fab1d0595eacf781705ec3509012a28f298245James Bottomley				       int create)
36321fab1d0595eacf781705ec3509012a28f298245James Bottomley{
36421fab1d0595eacf781705ec3509012a28f298245James Bottomley	u32 result;
36521fab1d0595eacf781705ec3509012a28f298245James Bottomley	unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL;
36621fab1d0595eacf781705ec3509012a28f298245James Bottomley	int i, j, page7_len, len, components;
36721fab1d0595eacf781705ec3509012a28f298245James Bottomley	struct ses_device *ses_dev = edev->scratch;
3688c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	int types = ses_dev->page1_num_types;
36921fab1d0595eacf781705ec3509012a28f298245James Bottomley	unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
37021fab1d0595eacf781705ec3509012a28f298245James Bottomley
37121fab1d0595eacf781705ec3509012a28f298245James Bottomley	if (!hdr_buf)
37221fab1d0595eacf781705ec3509012a28f298245James Bottomley		goto simple_populate;
37321fab1d0595eacf781705ec3509012a28f298245James Bottomley
37421fab1d0595eacf781705ec3509012a28f298245James Bottomley	/* re-read page 10 */
37521fab1d0595eacf781705ec3509012a28f298245James Bottomley	if (ses_dev->page10)
37621fab1d0595eacf781705ec3509012a28f298245James Bottomley		ses_recv_diag(sdev, 10, ses_dev->page10, ses_dev->page10_len);
37721fab1d0595eacf781705ec3509012a28f298245James Bottomley	/* Page 7 for the descriptors is optional */
37821fab1d0595eacf781705ec3509012a28f298245James Bottomley	result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
37921fab1d0595eacf781705ec3509012a28f298245James Bottomley	if (result)
38021fab1d0595eacf781705ec3509012a28f298245James Bottomley		goto simple_populate;
38121fab1d0595eacf781705ec3509012a28f298245James Bottomley
38221fab1d0595eacf781705ec3509012a28f298245James Bottomley	page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
38321fab1d0595eacf781705ec3509012a28f298245James Bottomley	/* add 1 for trailing '\0' we'll use */
38421fab1d0595eacf781705ec3509012a28f298245James Bottomley	buf = kzalloc(len + 1, GFP_KERNEL);
38521fab1d0595eacf781705ec3509012a28f298245James Bottomley	if (!buf)
38621fab1d0595eacf781705ec3509012a28f298245James Bottomley		goto simple_populate;
38721fab1d0595eacf781705ec3509012a28f298245James Bottomley	result = ses_recv_diag(sdev, 7, buf, len);
38821fab1d0595eacf781705ec3509012a28f298245James Bottomley	if (result) {
38921fab1d0595eacf781705ec3509012a28f298245James Bottomley simple_populate:
39021fab1d0595eacf781705ec3509012a28f298245James Bottomley		kfree(buf);
39121fab1d0595eacf781705ec3509012a28f298245James Bottomley		buf = NULL;
39221fab1d0595eacf781705ec3509012a28f298245James Bottomley		desc_ptr = NULL;
39321fab1d0595eacf781705ec3509012a28f298245James Bottomley		len = 0;
39421fab1d0595eacf781705ec3509012a28f298245James Bottomley		page7_len = 0;
39521fab1d0595eacf781705ec3509012a28f298245James Bottomley	} else {
39621fab1d0595eacf781705ec3509012a28f298245James Bottomley		desc_ptr = buf + 8;
39721fab1d0595eacf781705ec3509012a28f298245James Bottomley		len = (desc_ptr[2] << 8) + desc_ptr[3];
39821fab1d0595eacf781705ec3509012a28f298245James Bottomley		/* skip past overall descriptor */
39921fab1d0595eacf781705ec3509012a28f298245James Bottomley		desc_ptr += len + 4;
40021fab1d0595eacf781705ec3509012a28f298245James Bottomley	}
401877a55979c189c590e819a61cbbe2b7947875f17John Hughes	if (ses_dev->page10)
402877a55979c189c590e819a61cbbe2b7947875f17John Hughes		addl_desc_ptr = ses_dev->page10 + 8;
4038c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	type_ptr = ses_dev->page1_types;
40421fab1d0595eacf781705ec3509012a28f298245James Bottomley	components = 0;
40521fab1d0595eacf781705ec3509012a28f298245James Bottomley	for (i = 0; i < types; i++, type_ptr += 4) {
40621fab1d0595eacf781705ec3509012a28f298245James Bottomley		for (j = 0; j < type_ptr[1]; j++) {
40721fab1d0595eacf781705ec3509012a28f298245James Bottomley			char *name = NULL;
40821fab1d0595eacf781705ec3509012a28f298245James Bottomley			struct enclosure_component *ecomp;
40921fab1d0595eacf781705ec3509012a28f298245James Bottomley
41021fab1d0595eacf781705ec3509012a28f298245James Bottomley			if (desc_ptr) {
41121fab1d0595eacf781705ec3509012a28f298245James Bottomley				if (desc_ptr >= buf + page7_len) {
41221fab1d0595eacf781705ec3509012a28f298245James Bottomley					desc_ptr = NULL;
41321fab1d0595eacf781705ec3509012a28f298245James Bottomley				} else {
41421fab1d0595eacf781705ec3509012a28f298245James Bottomley					len = (desc_ptr[2] << 8) + desc_ptr[3];
41521fab1d0595eacf781705ec3509012a28f298245James Bottomley					desc_ptr += 4;
41621fab1d0595eacf781705ec3509012a28f298245James Bottomley					/* Add trailing zero - pushes into
41721fab1d0595eacf781705ec3509012a28f298245James Bottomley					 * reserved space */
41821fab1d0595eacf781705ec3509012a28f298245James Bottomley					desc_ptr[len] = '\0';
41921fab1d0595eacf781705ec3509012a28f298245James Bottomley					name = desc_ptr;
42021fab1d0595eacf781705ec3509012a28f298245James Bottomley				}
42121fab1d0595eacf781705ec3509012a28f298245James Bottomley			}
42221fab1d0595eacf781705ec3509012a28f298245James Bottomley			if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
42321fab1d0595eacf781705ec3509012a28f298245James Bottomley			    type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
42421fab1d0595eacf781705ec3509012a28f298245James Bottomley
42521fab1d0595eacf781705ec3509012a28f298245James Bottomley				if (create)
42621fab1d0595eacf781705ec3509012a28f298245James Bottomley					ecomp =	enclosure_component_register(edev,
42721fab1d0595eacf781705ec3509012a28f298245James Bottomley									     components++,
42821fab1d0595eacf781705ec3509012a28f298245James Bottomley									     type_ptr[0],
42921fab1d0595eacf781705ec3509012a28f298245James Bottomley									     name);
43021fab1d0595eacf781705ec3509012a28f298245James Bottomley				else
43121fab1d0595eacf781705ec3509012a28f298245James Bottomley					ecomp = &edev->component[components++];
43221fab1d0595eacf781705ec3509012a28f298245James Bottomley
43321fab1d0595eacf781705ec3509012a28f298245James Bottomley				if (!IS_ERR(ecomp) && addl_desc_ptr)
43421fab1d0595eacf781705ec3509012a28f298245James Bottomley					ses_process_descriptor(ecomp,
43521fab1d0595eacf781705ec3509012a28f298245James Bottomley							       addl_desc_ptr);
43621fab1d0595eacf781705ec3509012a28f298245James Bottomley			}
43721fab1d0595eacf781705ec3509012a28f298245James Bottomley			if (desc_ptr)
43821fab1d0595eacf781705ec3509012a28f298245James Bottomley				desc_ptr += len;
43921fab1d0595eacf781705ec3509012a28f298245James Bottomley
44021fab1d0595eacf781705ec3509012a28f298245James Bottomley			if (addl_desc_ptr)
44121fab1d0595eacf781705ec3509012a28f298245James Bottomley				addl_desc_ptr += addl_desc_ptr[1] + 2;
44221fab1d0595eacf781705ec3509012a28f298245James Bottomley
44321fab1d0595eacf781705ec3509012a28f298245James Bottomley		}
44421fab1d0595eacf781705ec3509012a28f298245James Bottomley	}
44521fab1d0595eacf781705ec3509012a28f298245James Bottomley	kfree(buf);
44621fab1d0595eacf781705ec3509012a28f298245James Bottomley	kfree(hdr_buf);
44721fab1d0595eacf781705ec3509012a28f298245James Bottomley}
44821fab1d0595eacf781705ec3509012a28f298245James Bottomley
4499927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void ses_match_to_enclosure(struct enclosure_device *edev,
4509927c68864e9c39cc317b4f559309ba29e642168James Bottomley				   struct scsi_device *sdev)
4519927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
4529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	unsigned char *desc;
4539927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct efd efd = {
4549927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.addr = 0,
4559927c68864e9c39cc317b4f559309ba29e642168James Bottomley	};
4569927c68864e9c39cc317b4f559309ba29e642168James Bottomley
45721fab1d0595eacf781705ec3509012a28f298245James Bottomley	ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
45821fab1d0595eacf781705ec3509012a28f298245James Bottomley
459c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke	if (!sdev->vpd_pg83_len)
460c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke		return;
461671a99c8eb2f1dde08ac5538d8cd912047c61ddfJames Bottomley
462c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke	desc = sdev->vpd_pg83 + 4;
463c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke	while (desc < sdev->vpd_pg83 + sdev->vpd_pg83_len) {
4649927c68864e9c39cc317b4f559309ba29e642168James Bottomley		enum scsi_protocol proto = desc[0] >> 4;
4659927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 code_set = desc[0] & 0x0f;
4669927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 piv = desc[1] & 0x80;
4679927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 assoc = (desc[1] & 0x30) >> 4;
4689927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 type = desc[1] & 0x0f;
4699927c68864e9c39cc317b4f559309ba29e642168James Bottomley		u8 len = desc[3];
4709927c68864e9c39cc317b4f559309ba29e642168James Bottomley
471b3f1f9aa082b2ab86dec4db3d8b1566af345387eRoel Kluin		if (piv && code_set == 1 && assoc == 1
4729927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8)
473c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke			efd.addr = get_unaligned_be64(&desc[4]);
4749927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4759927c68864e9c39cc317b4f559309ba29e642168James Bottomley		desc += len + 4;
4769927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
477c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke	if (efd.addr) {
478c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke		efd.dev = &sdev->sdev_gendev;
4799927c68864e9c39cc317b4f559309ba29e642168James Bottomley
480c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke		enclosure_for_each_device(ses_enclosure_find_by_addr, &efd);
481c38c007af04b928b5285da8cc44fbe2f4810e24eHannes Reinecke	}
4829927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
4839927c68864e9c39cc317b4f559309ba29e642168James Bottomley
484ee959b00c335d7780136c5abda37809191fe52c3Tony Jonesstatic int ses_intf_add(struct device *cdev,
4859927c68864e9c39cc317b4f559309ba29e642168James Bottomley			struct class_interface *intf)
4869927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
487ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	struct scsi_device *sdev = to_scsi_device(cdev->parent);
4889927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct scsi_device *tmp_sdev;
48921fab1d0595eacf781705ec3509012a28f298245James Bottomley	unsigned char *buf = NULL, *hdr_buf, *type_ptr;
4909927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev;
4919927c68864e9c39cc317b4f559309ba29e642168James Bottomley	u32 result;
49221fab1d0595eacf781705ec3509012a28f298245James Bottomley	int i, types, len, components = 0;
4939927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int err = -ENOMEM;
4948c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	int num_enclosures;
4959927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
4967c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	struct ses_component *scomp = NULL;
4979927c68864e9c39cc317b4f559309ba29e642168James Bottomley
4989927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scsi_device_enclosure(sdev)) {
4999927c68864e9c39cc317b4f559309ba29e642168James Bottomley		/* not an enclosure, but might be in one */
500163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley		struct enclosure_device *prev = NULL;
501163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley
502163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley		while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
5039927c68864e9c39cc317b4f559309ba29e642168James Bottomley			ses_match_to_enclosure(edev, sdev);
504163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley			prev = edev;
5059927c68864e9c39cc317b4f559309ba29e642168James Bottomley		}
5069927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return -ENODEV;
5079927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5089927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5099927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* TYPE_ENCLOSURE prints a message in probe */
5109927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (sdev->type != TYPE_ENCLOSURE)
5119927c68864e9c39cc317b4f559309ba29e642168James Bottomley		sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n");
5129927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5139927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL);
5149927c68864e9c39cc317b4f559309ba29e642168James Bottomley	hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
5159927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!hdr_buf || !ses_dev)
5169927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_init_free;
5179927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5189927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE);
5199927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5209927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
5219927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5229927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
5239927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len, GFP_KERNEL);
5249927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
5259927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
5269927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5279927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 1, buf, len);
5289927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5299927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
5309927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5318c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	types = 0;
5329927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5338c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	/* we always have one main enclosure and the rest are referred
5348c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	 * to as secondary subenclosures */
5358c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	num_enclosures = buf[1] + 1;
5369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5378c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	/* begin at the enclosure descriptor */
5388c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	type_ptr = buf + 8;
5398c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	/* skip all the enclosure descriptors */
5408c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) {
5418c3adc796f32fa4878ec843f8960717ba377e024James Bottomley		types += type_ptr[2];
5428c3adc796f32fa4878ec843f8960717ba377e024James Bottomley		type_ptr += type_ptr[3] + 4;
5438c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	}
5448c3adc796f32fa4878ec843f8960717ba377e024James Bottomley
5458c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	ses_dev->page1_types = type_ptr;
5468c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	ses_dev->page1_num_types = types;
5478c3adc796f32fa4878ec843f8960717ba377e024James Bottomley
5488c3adc796f32fa4878ec843f8960717ba377e024James Bottomley	for (i = 0; i < types && type_ptr < buf + len; i++, type_ptr += 4) {
5499927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
5509927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE)
5519927c68864e9c39cc317b4f559309ba29e642168James Bottomley			components += type_ptr[1];
5529927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
5537c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	ses_dev->page1 = buf;
5547c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	ses_dev->page1_len = len;
5557c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	buf = NULL;
5569927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5579927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE);
5589927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5599927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
5609927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5619927c68864e9c39cc317b4f559309ba29e642168James Bottomley	len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
5629927c68864e9c39cc317b4f559309ba29e642168James Bottomley	buf = kzalloc(len, GFP_KERNEL);
5639927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!buf)
5649927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
5659927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* make sure getting page 2 actually works */
5679927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 2, buf, len);
5689927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (result)
5699927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto recv_failed;
5709927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page2 = buf;
5719927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev->page2_len = len;
5727c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	buf = NULL;
5739927c68864e9c39cc317b4f559309ba29e642168James Bottomley
5749927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* The additional information page --- allows us
5759927c68864e9c39cc317b4f559309ba29e642168James Bottomley	 * to match up the devices */
5769927c68864e9c39cc317b4f559309ba29e642168James Bottomley	result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE);
577691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	if (!result) {
578691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu
579691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
580691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		buf = kzalloc(len, GFP_KERNEL);
581691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		if (!buf)
582691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu			goto err_free;
583691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu
584691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		result = ses_recv_diag(sdev, 10, buf, len);
585691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		if (result)
586691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu			goto recv_failed;
587691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		ses_dev->page10 = buf;
588691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		ses_dev->page10_len = len;
589691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu		buf = NULL;
590691b4773aa556d0975dbc25c93e6c8b839dad325Yinghai Lu	}
5917c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL);
5929927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!scomp)
5937c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		goto err_free;
5949927c68864e9c39cc317b4f559309ba29e642168James Bottomley
59571610f55fa4db63dbf5385929a47c9fb2451f332Kay Sievers	edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev),
5969927c68864e9c39cc317b4f559309ba29e642168James Bottomley				  components, &ses_enclosure_callbacks);
5979927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (IS_ERR(edev)) {
5989927c68864e9c39cc317b4f559309ba29e642168James Bottomley		err = PTR_ERR(edev);
5999927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto err_free;
6009927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
6019927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6029b3a6549b2602ca30f58715a0071e29f9898cae9Julia Lawall	kfree(hdr_buf);
6039b3a6549b2602ca30f58715a0071e29f9898cae9Julia Lawall
6049927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev->scratch = ses_dev;
6059927c68864e9c39cc317b4f559309ba29e642168James Bottomley	for (i = 0; i < components; i++)
6067c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu		edev->component[i].scratch = scomp + i;
6079927c68864e9c39cc317b4f559309ba29e642168James Bottomley
60821fab1d0595eacf781705ec3509012a28f298245James Bottomley	ses_enclosure_data_process(edev, sdev, 1);
6099927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6109927c68864e9c39cc317b4f559309ba29e642168James Bottomley	/* see if there are any devices matching before
6119927c68864e9c39cc317b4f559309ba29e642168James Bottomley	 * we found the enclosure */
6129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	shost_for_each_device(tmp_sdev, sdev->host) {
6139927c68864e9c39cc317b4f559309ba29e642168James Bottomley		if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
6149927c68864e9c39cc317b4f559309ba29e642168James Bottomley			continue;
6159927c68864e9c39cc317b4f559309ba29e642168James Bottomley		ses_match_to_enclosure(edev, tmp_sdev);
6169927c68864e9c39cc317b4f559309ba29e642168James Bottomley	}
6179927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6189927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
6199927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6209927c68864e9c39cc317b4f559309ba29e642168James Bottomley recv_failed:
6219927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n",
6229927c68864e9c39cc317b4f559309ba29e642168James Bottomley		    result);
6239927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = -ENODEV;
6249927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_free:
6259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(buf);
6267c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	kfree(scomp);
6279927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page10);
6289927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page2);
6299927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page1);
6309927c68864e9c39cc317b4f559309ba29e642168James Bottomley err_init_free:
6319927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev);
6329927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(hdr_buf);
6339927c68864e9c39cc317b4f559309ba29e642168James Bottomley	sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err);
6349927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return err;
6359927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6369927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6379927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int ses_remove(struct device *dev)
6389927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6399927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
6409927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6419927c68864e9c39cc317b4f559309ba29e642168James Bottomley
64243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove_component(struct scsi_device *sdev)
64343d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley{
64443d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	struct enclosure_device *edev, *prev = NULL;
64543d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley
64643d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
64743d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley		prev = edev;
64843d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley		if (!enclosure_remove_device(edev, &sdev->sdev_gendev))
64943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley			break;
65043d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	}
65143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	if (edev)
65243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley		put_device(&edev->edev);
65343d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley}
65443d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley
65543d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove_enclosure(struct scsi_device *sdev)
6569927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
6579927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct enclosure_device *edev;
6589927c68864e9c39cc317b4f559309ba29e642168James Bottomley	struct ses_device *ses_dev;
6599927c68864e9c39cc317b4f559309ba29e642168James Bottomley
660163f52b6cf3a639df6a72c7937e0eb88b20f1ef3James Bottomley	/*  exact match to this enclosure */
66143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	edev = enclosure_find(&sdev->sdev_gendev, NULL);
6629927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (!edev)
6639927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return;
6649927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6659927c68864e9c39cc317b4f559309ba29e642168James Bottomley	ses_dev = edev->scratch;
6669927c68864e9c39cc317b4f559309ba29e642168James Bottomley	edev->scratch = NULL;
6679927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6687c46c20aef185c3782d28c5111dcf1df88bbab32Yinghai Lu	kfree(ses_dev->page10);
6699927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page1);
6709927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev->page2);
6719927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(ses_dev);
6729927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6739927c68864e9c39cc317b4f559309ba29e642168James Bottomley	kfree(edev->component[0].scratch);
6749927c68864e9c39cc317b4f559309ba29e642168James Bottomley
675ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	put_device(&edev->edev);
6769927c68864e9c39cc317b4f559309ba29e642168James Bottomley	enclosure_unregister(edev);
6779927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
6789927c68864e9c39cc317b4f559309ba29e642168James Bottomley
67943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomleystatic void ses_intf_remove(struct device *cdev,
68043d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley			    struct class_interface *intf)
68143d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley{
68243d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	struct scsi_device *sdev = to_scsi_device(cdev->parent);
68343d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley
68443d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	if (!scsi_device_enclosure(sdev))
68543d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley		ses_intf_remove_component(sdev);
68643d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley	else
68743d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley		ses_intf_remove_enclosure(sdev);
68843d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley}
68943d8eb9cfd0aea93be32181c64e18191b69c211cJames Bottomley
6909927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct class_interface ses_interface = {
691ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	.add_dev	= ses_intf_add,
692ee959b00c335d7780136c5abda37809191fe52c3Tony Jones	.remove_dev	= ses_intf_remove,
6939927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
6949927c68864e9c39cc317b4f559309ba29e642168James Bottomley
6959927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic struct scsi_driver ses_template = {
6969927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.owner			= THIS_MODULE,
6979927c68864e9c39cc317b4f559309ba29e642168James Bottomley	.gendrv = {
6989927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.name		= "ses",
6999927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.probe		= ses_probe,
7009927c68864e9c39cc317b4f559309ba29e642168James Bottomley		.remove		= ses_remove,
7019927c68864e9c39cc317b4f559309ba29e642168James Bottomley	},
7029927c68864e9c39cc317b4f559309ba29e642168James Bottomley};
7039927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7049927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic int __init ses_init(void)
7059927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
7069927c68864e9c39cc317b4f559309ba29e642168James Bottomley	int err;
7079927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7089927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = scsi_register_interface(&ses_interface);
7099927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (err)
7109927c68864e9c39cc317b4f559309ba29e642168James Bottomley		return err;
7119927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7129927c68864e9c39cc317b4f559309ba29e642168James Bottomley	err = scsi_register_driver(&ses_template.gendrv);
7139927c68864e9c39cc317b4f559309ba29e642168James Bottomley	if (err)
7149927c68864e9c39cc317b4f559309ba29e642168James Bottomley		goto out_unreg;
7159927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7169927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return 0;
7179927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7189927c68864e9c39cc317b4f559309ba29e642168James Bottomley out_unreg:
7199927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_interface(&ses_interface);
7209927c68864e9c39cc317b4f559309ba29e642168James Bottomley	return err;
7219927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
7229927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7239927c68864e9c39cc317b4f559309ba29e642168James Bottomleystatic void __exit ses_exit(void)
7249927c68864e9c39cc317b4f559309ba29e642168James Bottomley{
7259927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_driver(&ses_template.gendrv);
7269927c68864e9c39cc317b4f559309ba29e642168James Bottomley	scsi_unregister_interface(&ses_interface);
7279927c68864e9c39cc317b4f559309ba29e642168James Bottomley}
7289927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7299927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_init(ses_init);
7309927c68864e9c39cc317b4f559309ba29e642168James Bottomleymodule_exit(ses_exit);
7319927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7329927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE);
7339927c68864e9c39cc317b4f559309ba29e642168James Bottomley
7349927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_AUTHOR("James Bottomley");
7359927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver");
7369927c68864e9c39cc317b4f559309ba29e642168James BottomleyMODULE_LICENSE("GPL v2");
737