diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2008-05-01 17:49:59 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-06-05 10:23:40 -0400 |
commit | f6dd337ee4c440f29a873da3779eb3af44bd1623 (patch) | |
tree | 4bcadf05bb0a3971454319a070e90dc260e4924b /drivers/scsi | |
parent | fbd7ab3eb53a3b88fefa7873139a62e439860155 (diff) |
[SCSI] scsi_dh: add hp sw device handler
This patch provides the device handler to support the older hp boxes
which cannot be upgraded.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/device_handler/Kconfig | 8 | ||||
-rw-r--r-- | drivers/scsi/device_handler/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 202 |
3 files changed, 211 insertions, 0 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 404f37732627..61578d84fa6e 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig | |||
@@ -16,3 +16,11 @@ config SCSI_DH_RDAC | |||
16 | depends on SCSI_DH | 16 | depends on SCSI_DH |
17 | help | 17 | help |
18 | If you have a LSI RDAC select y. Otherwise, say N. | 18 | If you have a LSI RDAC select y. Otherwise, say N. |
19 | |||
20 | config SCSI_DH_HP_SW | ||
21 | tristate "HP/COMPAQ MSA Device Handler" | ||
22 | depends on SCSI_DH | ||
23 | help | ||
24 | If you have a HP/COMPAQ MSA device that requires START_STOP to | ||
25 | be sent to start it and cannot upgrade the firmware then select y. | ||
26 | Otherwise, say N. | ||
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 1a42b3498318..08ec1943f19e 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile | |||
@@ -3,3 +3,4 @@ | |||
3 | # | 3 | # |
4 | obj-$(CONFIG_SCSI_DH) += scsi_dh.o | 4 | obj-$(CONFIG_SCSI_DH) += scsi_dh.o |
5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o | 5 | obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o |
6 | obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o | ||
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c new file mode 100644 index 000000000000..12ceab7b3662 --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be | ||
3 | * upgraded. | ||
4 | * | ||
5 | * Copyright (C) 2006 Red Hat, Inc. All rights reserved. | ||
6 | * Copyright (C) 2006 Mike Christie | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; see the file COPYING. If not, write to | ||
20 | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <scsi/scsi.h> | ||
24 | #include <scsi/scsi_dbg.h> | ||
25 | #include <scsi/scsi_eh.h> | ||
26 | #include <scsi/scsi_dh.h> | ||
27 | |||
28 | #define HP_SW_NAME "hp_sw" | ||
29 | |||
30 | #define HP_SW_TIMEOUT (60 * HZ) | ||
31 | #define HP_SW_RETRIES 3 | ||
32 | |||
33 | struct hp_sw_dh_data { | ||
34 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | ||
35 | int retries; | ||
36 | }; | ||
37 | |||
38 | static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) | ||
39 | { | ||
40 | struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; | ||
41 | BUG_ON(scsi_dh_data == NULL); | ||
42 | return ((struct hp_sw_dh_data *) scsi_dh_data->buf); | ||
43 | } | ||
44 | |||
45 | static int hp_sw_done(struct scsi_device *sdev) | ||
46 | { | ||
47 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
48 | struct scsi_sense_hdr sshdr; | ||
49 | int rc; | ||
50 | |||
51 | sdev_printk(KERN_INFO, sdev, "hp_sw_done\n"); | ||
52 | |||
53 | rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); | ||
54 | if (!rc) | ||
55 | goto done; | ||
56 | switch (sshdr.sense_key) { | ||
57 | case NOT_READY: | ||
58 | if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { | ||
59 | rc = SCSI_DH_RETRY; | ||
60 | h->retries++; | ||
61 | break; | ||
62 | } | ||
63 | /* fall through */ | ||
64 | default: | ||
65 | h->retries++; | ||
66 | rc = SCSI_DH_IMM_RETRY; | ||
67 | } | ||
68 | |||
69 | done: | ||
70 | if (rc == SCSI_DH_OK || rc == SCSI_DH_IO) | ||
71 | h->retries = 0; | ||
72 | else if (h->retries > HP_SW_RETRIES) { | ||
73 | h->retries = 0; | ||
74 | rc = SCSI_DH_IO; | ||
75 | } | ||
76 | return rc; | ||
77 | } | ||
78 | |||
79 | static int hp_sw_activate(struct scsi_device *sdev) | ||
80 | { | ||
81 | struct hp_sw_dh_data *h = get_hp_sw_data(sdev); | ||
82 | struct request *req; | ||
83 | int ret = SCSI_DH_RES_TEMP_UNAVAIL; | ||
84 | |||
85 | req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC); | ||
86 | if (!req) | ||
87 | goto done; | ||
88 | |||
89 | sdev_printk(KERN_INFO, sdev, "sending START_STOP."); | ||
90 | |||
91 | req->cmd_type = REQ_TYPE_BLOCK_PC; | ||
92 | req->cmd_flags |= REQ_FAILFAST; | ||
93 | req->cmd_len = COMMAND_SIZE(START_STOP); | ||
94 | memset(req->cmd, 0, MAX_COMMAND_SIZE); | ||
95 | req->cmd[0] = START_STOP; | ||
96 | req->cmd[4] = 1; /* Start spin cycle */ | ||
97 | req->timeout = HP_SW_TIMEOUT; | ||
98 | req->sense = h->sense; | ||
99 | memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
100 | req->sense_len = 0; | ||
101 | |||
102 | ret = blk_execute_rq(req->q, NULL, req, 1); | ||
103 | if (!ret) /* SUCCESS */ | ||
104 | ret = hp_sw_done(sdev); | ||
105 | else | ||
106 | ret = SCSI_DH_IO; | ||
107 | done: | ||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | static const struct { | ||
112 | char *vendor; | ||
113 | char *model; | ||
114 | } hp_sw_dh_data_list[] = { | ||
115 | {"COMPAQ", "MSA"}, | ||
116 | {"HP", "HSV"}, | ||
117 | {"DEC", "HSG80"}, | ||
118 | {NULL, NULL}, | ||
119 | }; | ||
120 | |||
121 | static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *); | ||
122 | |||
123 | static struct scsi_device_handler hp_sw_dh = { | ||
124 | .name = HP_SW_NAME, | ||
125 | .module = THIS_MODULE, | ||
126 | .nb.notifier_call = hp_sw_bus_notify, | ||
127 | .activate = hp_sw_activate, | ||
128 | }; | ||
129 | |||
130 | static int hp_sw_bus_notify(struct notifier_block *nb, | ||
131 | unsigned long action, void *data) | ||
132 | { | ||
133 | struct device *dev = data; | ||
134 | struct scsi_device *sdev = to_scsi_device(dev); | ||
135 | struct scsi_dh_data *scsi_dh_data; | ||
136 | int i, found = 0; | ||
137 | unsigned long flags; | ||
138 | |||
139 | if (action == BUS_NOTIFY_ADD_DEVICE) { | ||
140 | for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { | ||
141 | if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, | ||
142 | strlen(hp_sw_dh_data_list[i].vendor)) && | ||
143 | !strncmp(sdev->model, hp_sw_dh_data_list[i].model, | ||
144 | strlen(hp_sw_dh_data_list[i].model))) { | ||
145 | found = 1; | ||
146 | break; | ||
147 | } | ||
148 | } | ||
149 | if (!found) | ||
150 | goto out; | ||
151 | |||
152 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | ||
153 | + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); | ||
154 | if (!scsi_dh_data) { | ||
155 | sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n", | ||
156 | HP_SW_NAME); | ||
157 | goto out; | ||
158 | } | ||
159 | |||
160 | scsi_dh_data->scsi_dh = &hp_sw_dh; | ||
161 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
162 | sdev->scsi_dh_data = scsi_dh_data; | ||
163 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
164 | try_module_get(THIS_MODULE); | ||
165 | |||
166 | sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME); | ||
167 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | ||
168 | if (sdev->scsi_dh_data == NULL || | ||
169 | sdev->scsi_dh_data->scsi_dh != &hp_sw_dh) | ||
170 | goto out; | ||
171 | |||
172 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
173 | scsi_dh_data = sdev->scsi_dh_data; | ||
174 | sdev->scsi_dh_data = NULL; | ||
175 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | ||
176 | module_put(THIS_MODULE); | ||
177 | |||
178 | sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME); | ||
179 | |||
180 | kfree(scsi_dh_data); | ||
181 | } | ||
182 | |||
183 | out: | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int __init hp_sw_init(void) | ||
188 | { | ||
189 | return scsi_register_device_handler(&hp_sw_dh); | ||
190 | } | ||
191 | |||
192 | static void __exit hp_sw_exit(void) | ||
193 | { | ||
194 | scsi_unregister_device_handler(&hp_sw_dh); | ||
195 | } | ||
196 | |||
197 | module_init(hp_sw_init); | ||
198 | module_exit(hp_sw_exit); | ||
199 | |||
200 | MODULE_DESCRIPTION("HP MSA 1000"); | ||
201 | MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); | ||
202 | MODULE_LICENSE("GPL"); | ||