diff options
Diffstat (limited to 'drivers/scsi/ses.c')
-rw-r--r-- | drivers/scsi/ses.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c new file mode 100644 index 000000000000..2a6e4f472eaa --- /dev/null +++ b/drivers/scsi/ses.c | |||
@@ -0,0 +1,689 @@ | |||
1 | /* | ||
2 | * SCSI Enclosure Services | ||
3 | * | ||
4 | * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com> | ||
5 | * | ||
6 | **----------------------------------------------------------------------------- | ||
7 | ** | ||
8 | ** This program is free software; you can redistribute it and/or | ||
9 | ** modify it under the terms of the GNU General Public License | ||
10 | ** version 2 as published by the Free Software Foundation. | ||
11 | ** | ||
12 | ** This program is distributed in the hope that it will be useful, | ||
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | ** GNU General Public License for more details. | ||
16 | ** | ||
17 | ** You should have received a copy of the GNU General Public License | ||
18 | ** along with this program; if not, write to the Free Software | ||
19 | ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | ** | ||
21 | **----------------------------------------------------------------------------- | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/enclosure.h> | ||
27 | |||
28 | #include <scsi/scsi.h> | ||
29 | #include <scsi/scsi_cmnd.h> | ||
30 | #include <scsi/scsi_dbg.h> | ||
31 | #include <scsi/scsi_device.h> | ||
32 | #include <scsi/scsi_driver.h> | ||
33 | #include <scsi/scsi_host.h> | ||
34 | |||
35 | struct ses_device { | ||
36 | char *page1; | ||
37 | char *page2; | ||
38 | char *page10; | ||
39 | short page1_len; | ||
40 | short page2_len; | ||
41 | short page10_len; | ||
42 | }; | ||
43 | |||
44 | struct ses_component { | ||
45 | u64 addr; | ||
46 | unsigned char *desc; | ||
47 | }; | ||
48 | |||
49 | static int ses_probe(struct device *dev) | ||
50 | { | ||
51 | struct scsi_device *sdev = to_scsi_device(dev); | ||
52 | int err = -ENODEV; | ||
53 | |||
54 | if (sdev->type != TYPE_ENCLOSURE) | ||
55 | goto out; | ||
56 | |||
57 | err = 0; | ||
58 | sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n"); | ||
59 | |||
60 | out: | ||
61 | return err; | ||
62 | } | ||
63 | |||
64 | #define SES_TIMEOUT 30 | ||
65 | #define SES_RETRIES 3 | ||
66 | |||
67 | static int ses_recv_diag(struct scsi_device *sdev, int page_code, | ||
68 | void *buf, int bufflen) | ||
69 | { | ||
70 | char cmd[] = { | ||
71 | RECEIVE_DIAGNOSTIC, | ||
72 | 1, /* Set PCV bit */ | ||
73 | page_code, | ||
74 | bufflen >> 8, | ||
75 | bufflen & 0xff, | ||
76 | 0 | ||
77 | }; | ||
78 | |||
79 | return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, | ||
80 | NULL, SES_TIMEOUT, SES_RETRIES); | ||
81 | } | ||
82 | |||
83 | static int ses_send_diag(struct scsi_device *sdev, int page_code, | ||
84 | void *buf, int bufflen) | ||
85 | { | ||
86 | u32 result; | ||
87 | |||
88 | char cmd[] = { | ||
89 | SEND_DIAGNOSTIC, | ||
90 | 0x10, /* Set PF bit */ | ||
91 | 0, | ||
92 | bufflen >> 8, | ||
93 | bufflen & 0xff, | ||
94 | 0 | ||
95 | }; | ||
96 | |||
97 | result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen, | ||
98 | NULL, SES_TIMEOUT, SES_RETRIES); | ||
99 | if (result) | ||
100 | sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n", | ||
101 | result); | ||
102 | return result; | ||
103 | } | ||
104 | |||
105 | static int ses_set_page2_descriptor(struct enclosure_device *edev, | ||
106 | struct enclosure_component *ecomp, | ||
107 | char *desc) | ||
108 | { | ||
109 | int i, j, count = 0, descriptor = ecomp->number; | ||
110 | struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); | ||
111 | struct ses_device *ses_dev = edev->scratch; | ||
112 | char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; | ||
113 | char *desc_ptr = ses_dev->page2 + 8; | ||
114 | |||
115 | /* Clear everything */ | ||
116 | memset(desc_ptr, 0, ses_dev->page2_len - 8); | ||
117 | for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { | ||
118 | for (j = 0; j < type_ptr[1]; j++) { | ||
119 | desc_ptr += 4; | ||
120 | if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && | ||
121 | type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) | ||
122 | continue; | ||
123 | if (count++ == descriptor) { | ||
124 | memcpy(desc_ptr, desc, 4); | ||
125 | /* set select */ | ||
126 | desc_ptr[0] |= 0x80; | ||
127 | /* clear reserved, just in case */ | ||
128 | desc_ptr[0] &= 0xf0; | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); | ||
134 | } | ||
135 | |||
136 | static char *ses_get_page2_descriptor(struct enclosure_device *edev, | ||
137 | struct enclosure_component *ecomp) | ||
138 | { | ||
139 | int i, j, count = 0, descriptor = ecomp->number; | ||
140 | struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); | ||
141 | struct ses_device *ses_dev = edev->scratch; | ||
142 | char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; | ||
143 | char *desc_ptr = ses_dev->page2 + 8; | ||
144 | |||
145 | ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); | ||
146 | |||
147 | for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { | ||
148 | for (j = 0; j < type_ptr[1]; j++) { | ||
149 | desc_ptr += 4; | ||
150 | if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && | ||
151 | type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) | ||
152 | continue; | ||
153 | if (count++ == descriptor) | ||
154 | return desc_ptr; | ||
155 | } | ||
156 | } | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | static void ses_get_fault(struct enclosure_device *edev, | ||
161 | struct enclosure_component *ecomp) | ||
162 | { | ||
163 | char *desc; | ||
164 | |||
165 | desc = ses_get_page2_descriptor(edev, ecomp); | ||
166 | ecomp->fault = (desc[3] & 0x60) >> 4; | ||
167 | } | ||
168 | |||
169 | static int ses_set_fault(struct enclosure_device *edev, | ||
170 | struct enclosure_component *ecomp, | ||
171 | enum enclosure_component_setting val) | ||
172 | { | ||
173 | char desc[4] = {0 }; | ||
174 | |||
175 | switch (val) { | ||
176 | case ENCLOSURE_SETTING_DISABLED: | ||
177 | /* zero is disabled */ | ||
178 | break; | ||
179 | case ENCLOSURE_SETTING_ENABLED: | ||
180 | desc[2] = 0x02; | ||
181 | break; | ||
182 | default: | ||
183 | /* SES doesn't do the SGPIO blink settings */ | ||
184 | return -EINVAL; | ||
185 | } | ||
186 | |||
187 | return ses_set_page2_descriptor(edev, ecomp, desc); | ||
188 | } | ||
189 | |||
190 | static void ses_get_status(struct enclosure_device *edev, | ||
191 | struct enclosure_component *ecomp) | ||
192 | { | ||
193 | char *desc; | ||
194 | |||
195 | desc = ses_get_page2_descriptor(edev, ecomp); | ||
196 | ecomp->status = (desc[0] & 0x0f); | ||
197 | } | ||
198 | |||
199 | static void ses_get_locate(struct enclosure_device *edev, | ||
200 | struct enclosure_component *ecomp) | ||
201 | { | ||
202 | char *desc; | ||
203 | |||
204 | desc = ses_get_page2_descriptor(edev, ecomp); | ||
205 | ecomp->locate = (desc[2] & 0x02) ? 1 : 0; | ||
206 | } | ||
207 | |||
208 | static int ses_set_locate(struct enclosure_device *edev, | ||
209 | struct enclosure_component *ecomp, | ||
210 | enum enclosure_component_setting val) | ||
211 | { | ||
212 | char desc[4] = {0 }; | ||
213 | |||
214 | switch (val) { | ||
215 | case ENCLOSURE_SETTING_DISABLED: | ||
216 | /* zero is disabled */ | ||
217 | break; | ||
218 | case ENCLOSURE_SETTING_ENABLED: | ||
219 | desc[2] = 0x02; | ||
220 | break; | ||
221 | default: | ||
222 | /* SES doesn't do the SGPIO blink settings */ | ||
223 | return -EINVAL; | ||
224 | } | ||
225 | return ses_set_page2_descriptor(edev, ecomp, desc); | ||
226 | } | ||
227 | |||
228 | static int ses_set_active(struct enclosure_device *edev, | ||
229 | struct enclosure_component *ecomp, | ||
230 | enum enclosure_component_setting val) | ||
231 | { | ||
232 | char desc[4] = {0 }; | ||
233 | |||
234 | switch (val) { | ||
235 | case ENCLOSURE_SETTING_DISABLED: | ||
236 | /* zero is disabled */ | ||
237 | ecomp->active = 0; | ||
238 | break; | ||
239 | case ENCLOSURE_SETTING_ENABLED: | ||
240 | desc[2] = 0x80; | ||
241 | ecomp->active = 1; | ||
242 | break; | ||
243 | default: | ||
244 | /* SES doesn't do the SGPIO blink settings */ | ||
245 | return -EINVAL; | ||
246 | } | ||
247 | return ses_set_page2_descriptor(edev, ecomp, desc); | ||
248 | } | ||
249 | |||
250 | static struct enclosure_component_callbacks ses_enclosure_callbacks = { | ||
251 | .get_fault = ses_get_fault, | ||
252 | .set_fault = ses_set_fault, | ||
253 | .get_status = ses_get_status, | ||
254 | .get_locate = ses_get_locate, | ||
255 | .set_locate = ses_set_locate, | ||
256 | .set_active = ses_set_active, | ||
257 | }; | ||
258 | |||
259 | struct ses_host_edev { | ||
260 | struct Scsi_Host *shost; | ||
261 | struct enclosure_device *edev; | ||
262 | }; | ||
263 | |||
264 | int ses_match_host(struct enclosure_device *edev, void *data) | ||
265 | { | ||
266 | struct ses_host_edev *sed = data; | ||
267 | struct scsi_device *sdev; | ||
268 | |||
269 | if (!scsi_is_sdev_device(edev->cdev.dev)) | ||
270 | return 0; | ||
271 | |||
272 | sdev = to_scsi_device(edev->cdev.dev); | ||
273 | |||
274 | if (sdev->host != sed->shost) | ||
275 | return 0; | ||
276 | |||
277 | sed->edev = edev; | ||
278 | return 1; | ||
279 | } | ||
280 | |||
281 | static void ses_process_descriptor(struct enclosure_component *ecomp, | ||
282 | unsigned char *desc) | ||
283 | { | ||
284 | int eip = desc[0] & 0x10; | ||
285 | int invalid = desc[0] & 0x80; | ||
286 | enum scsi_protocol proto = desc[0] & 0x0f; | ||
287 | u64 addr = 0; | ||
288 | struct ses_component *scomp = ecomp->scratch; | ||
289 | unsigned char *d; | ||
290 | |||
291 | scomp->desc = desc; | ||
292 | |||
293 | if (invalid) | ||
294 | return; | ||
295 | |||
296 | switch (proto) { | ||
297 | case SCSI_PROTOCOL_SAS: | ||
298 | if (eip) | ||
299 | d = desc + 8; | ||
300 | else | ||
301 | d = desc + 4; | ||
302 | /* only take the phy0 addr */ | ||
303 | addr = (u64)d[12] << 56 | | ||
304 | (u64)d[13] << 48 | | ||
305 | (u64)d[14] << 40 | | ||
306 | (u64)d[15] << 32 | | ||
307 | (u64)d[16] << 24 | | ||
308 | (u64)d[17] << 16 | | ||
309 | (u64)d[18] << 8 | | ||
310 | (u64)d[19]; | ||
311 | break; | ||
312 | default: | ||
313 | /* FIXME: Need to add more protocols than just SAS */ | ||
314 | break; | ||
315 | } | ||
316 | scomp->addr = addr; | ||
317 | } | ||
318 | |||
319 | struct efd { | ||
320 | u64 addr; | ||
321 | struct device *dev; | ||
322 | }; | ||
323 | |||
324 | static int ses_enclosure_find_by_addr(struct enclosure_device *edev, | ||
325 | void *data) | ||
326 | { | ||
327 | struct efd *efd = data; | ||
328 | int i; | ||
329 | struct ses_component *scomp; | ||
330 | |||
331 | if (!edev->component[0].scratch) | ||
332 | return 0; | ||
333 | |||
334 | for (i = 0; i < edev->components; i++) { | ||
335 | scomp = edev->component[i].scratch; | ||
336 | if (scomp->addr != efd->addr) | ||
337 | continue; | ||
338 | |||
339 | enclosure_add_device(edev, i, efd->dev); | ||
340 | return 1; | ||
341 | } | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | #define VPD_INQUIRY_SIZE 512 | ||
346 | |||
347 | static void ses_match_to_enclosure(struct enclosure_device *edev, | ||
348 | struct scsi_device *sdev) | ||
349 | { | ||
350 | unsigned char *buf = kmalloc(VPD_INQUIRY_SIZE, GFP_KERNEL); | ||
351 | unsigned char *desc; | ||
352 | int len; | ||
353 | struct efd efd = { | ||
354 | .addr = 0, | ||
355 | }; | ||
356 | unsigned char cmd[] = { | ||
357 | INQUIRY, | ||
358 | 1, | ||
359 | 0x83, | ||
360 | VPD_INQUIRY_SIZE >> 8, | ||
361 | VPD_INQUIRY_SIZE & 0xff, | ||
362 | 0 | ||
363 | }; | ||
364 | |||
365 | if (!buf) | ||
366 | return; | ||
367 | |||
368 | if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, | ||
369 | VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES)) | ||
370 | goto free; | ||
371 | |||
372 | len = (buf[2] << 8) + buf[3]; | ||
373 | desc = buf + 4; | ||
374 | while (desc < buf + len) { | ||
375 | enum scsi_protocol proto = desc[0] >> 4; | ||
376 | u8 code_set = desc[0] & 0x0f; | ||
377 | u8 piv = desc[1] & 0x80; | ||
378 | u8 assoc = (desc[1] & 0x30) >> 4; | ||
379 | u8 type = desc[1] & 0x0f; | ||
380 | u8 len = desc[3]; | ||
381 | |||
382 | if (piv && code_set == 1 && assoc == 1 && code_set == 1 | ||
383 | && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8) | ||
384 | efd.addr = (u64)desc[4] << 56 | | ||
385 | (u64)desc[5] << 48 | | ||
386 | (u64)desc[6] << 40 | | ||
387 | (u64)desc[7] << 32 | | ||
388 | (u64)desc[8] << 24 | | ||
389 | (u64)desc[9] << 16 | | ||
390 | (u64)desc[10] << 8 | | ||
391 | (u64)desc[11]; | ||
392 | |||
393 | desc += len + 4; | ||
394 | } | ||
395 | if (!efd.addr) | ||
396 | goto free; | ||
397 | |||
398 | efd.dev = &sdev->sdev_gendev; | ||
399 | |||
400 | enclosure_for_each_device(ses_enclosure_find_by_addr, &efd); | ||
401 | free: | ||
402 | kfree(buf); | ||
403 | } | ||
404 | |||
405 | #define INIT_ALLOC_SIZE 32 | ||
406 | |||
407 | static int ses_intf_add(struct class_device *cdev, | ||
408 | struct class_interface *intf) | ||
409 | { | ||
410 | struct scsi_device *sdev = to_scsi_device(cdev->dev); | ||
411 | struct scsi_device *tmp_sdev; | ||
412 | unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr, | ||
413 | *addl_desc_ptr; | ||
414 | struct ses_device *ses_dev; | ||
415 | u32 result; | ||
416 | int i, j, types, len, components = 0; | ||
417 | int err = -ENOMEM; | ||
418 | struct enclosure_device *edev; | ||
419 | struct ses_component *scomp; | ||
420 | |||
421 | if (!scsi_device_enclosure(sdev)) { | ||
422 | /* not an enclosure, but might be in one */ | ||
423 | edev = enclosure_find(&sdev->host->shost_gendev); | ||
424 | if (edev) { | ||
425 | ses_match_to_enclosure(edev, sdev); | ||
426 | class_device_put(&edev->cdev); | ||
427 | } | ||
428 | return -ENODEV; | ||
429 | } | ||
430 | |||
431 | /* TYPE_ENCLOSURE prints a message in probe */ | ||
432 | if (sdev->type != TYPE_ENCLOSURE) | ||
433 | sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n"); | ||
434 | |||
435 | ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL); | ||
436 | hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); | ||
437 | if (!hdr_buf || !ses_dev) | ||
438 | goto err_init_free; | ||
439 | |||
440 | result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE); | ||
441 | if (result) | ||
442 | goto recv_failed; | ||
443 | |||
444 | if (hdr_buf[1] != 0) { | ||
445 | /* FIXME: need subenclosure support; I've just never | ||
446 | * seen a device with subenclosures and it makes the | ||
447 | * traversal routines more complex */ | ||
448 | sdev_printk(KERN_ERR, sdev, | ||
449 | "FIXME driver has no support for subenclosures (%d)\n", | ||
450 | buf[1]); | ||
451 | goto err_free; | ||
452 | } | ||
453 | |||
454 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | ||
455 | buf = kzalloc(len, GFP_KERNEL); | ||
456 | if (!buf) | ||
457 | goto err_free; | ||
458 | |||
459 | ses_dev->page1 = buf; | ||
460 | ses_dev->page1_len = len; | ||
461 | |||
462 | result = ses_recv_diag(sdev, 1, buf, len); | ||
463 | if (result) | ||
464 | goto recv_failed; | ||
465 | |||
466 | types = buf[10]; | ||
467 | len = buf[11]; | ||
468 | |||
469 | type_ptr = buf + 12 + len; | ||
470 | |||
471 | for (i = 0; i < types; i++, type_ptr += 4) { | ||
472 | if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || | ||
473 | type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) | ||
474 | components += type_ptr[1]; | ||
475 | } | ||
476 | |||
477 | result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE); | ||
478 | if (result) | ||
479 | goto recv_failed; | ||
480 | |||
481 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | ||
482 | buf = kzalloc(len, GFP_KERNEL); | ||
483 | if (!buf) | ||
484 | goto err_free; | ||
485 | |||
486 | /* make sure getting page 2 actually works */ | ||
487 | result = ses_recv_diag(sdev, 2, buf, len); | ||
488 | if (result) | ||
489 | goto recv_failed; | ||
490 | ses_dev->page2 = buf; | ||
491 | ses_dev->page2_len = len; | ||
492 | |||
493 | /* The additional information page --- allows us | ||
494 | * to match up the devices */ | ||
495 | result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE); | ||
496 | if (result) | ||
497 | goto no_page10; | ||
498 | |||
499 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | ||
500 | buf = kzalloc(len, GFP_KERNEL); | ||
501 | if (!buf) | ||
502 | goto err_free; | ||
503 | |||
504 | result = ses_recv_diag(sdev, 10, buf, len); | ||
505 | if (result) | ||
506 | goto recv_failed; | ||
507 | ses_dev->page10 = buf; | ||
508 | ses_dev->page10_len = len; | ||
509 | |||
510 | no_page10: | ||
511 | scomp = kmalloc(sizeof(struct ses_component) * components, GFP_KERNEL); | ||
512 | if (!scomp) | ||
513 | goto err_free; | ||
514 | |||
515 | edev = enclosure_register(cdev->dev, sdev->sdev_gendev.bus_id, | ||
516 | components, &ses_enclosure_callbacks); | ||
517 | if (IS_ERR(edev)) { | ||
518 | err = PTR_ERR(edev); | ||
519 | goto err_free; | ||
520 | } | ||
521 | |||
522 | edev->scratch = ses_dev; | ||
523 | for (i = 0; i < components; i++) | ||
524 | edev->component[i].scratch = scomp++; | ||
525 | |||
526 | /* Page 7 for the descriptors is optional */ | ||
527 | buf = NULL; | ||
528 | result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); | ||
529 | if (result) | ||
530 | goto simple_populate; | ||
531 | |||
532 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | ||
533 | /* add 1 for trailing '\0' we'll use */ | ||
534 | buf = kzalloc(len + 1, GFP_KERNEL); | ||
535 | result = ses_recv_diag(sdev, 7, buf, len); | ||
536 | if (result) { | ||
537 | simple_populate: | ||
538 | kfree(buf); | ||
539 | buf = NULL; | ||
540 | desc_ptr = NULL; | ||
541 | addl_desc_ptr = NULL; | ||
542 | } else { | ||
543 | desc_ptr = buf + 8; | ||
544 | len = (desc_ptr[2] << 8) + desc_ptr[3]; | ||
545 | /* skip past overall descriptor */ | ||
546 | desc_ptr += len + 4; | ||
547 | addl_desc_ptr = ses_dev->page10 + 8; | ||
548 | } | ||
549 | type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; | ||
550 | components = 0; | ||
551 | for (i = 0; i < types; i++, type_ptr += 4) { | ||
552 | for (j = 0; j < type_ptr[1]; j++) { | ||
553 | char *name = NULL; | ||
554 | struct enclosure_component *ecomp; | ||
555 | |||
556 | if (desc_ptr) { | ||
557 | len = (desc_ptr[2] << 8) + desc_ptr[3]; | ||
558 | desc_ptr += 4; | ||
559 | /* Add trailing zero - pushes into | ||
560 | * reserved space */ | ||
561 | desc_ptr[len] = '\0'; | ||
562 | name = desc_ptr; | ||
563 | } | ||
564 | if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && | ||
565 | type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) | ||
566 | continue; | ||
567 | ecomp = enclosure_component_register(edev, | ||
568 | components++, | ||
569 | type_ptr[0], | ||
570 | name); | ||
571 | if (desc_ptr) { | ||
572 | desc_ptr += len; | ||
573 | if (!IS_ERR(ecomp)) | ||
574 | ses_process_descriptor(ecomp, | ||
575 | addl_desc_ptr); | ||
576 | |||
577 | if (addl_desc_ptr) | ||
578 | addl_desc_ptr += addl_desc_ptr[1] + 2; | ||
579 | } | ||
580 | } | ||
581 | } | ||
582 | kfree(buf); | ||
583 | kfree(hdr_buf); | ||
584 | |||
585 | /* see if there are any devices matching before | ||
586 | * we found the enclosure */ | ||
587 | shost_for_each_device(tmp_sdev, sdev->host) { | ||
588 | if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev)) | ||
589 | continue; | ||
590 | ses_match_to_enclosure(edev, tmp_sdev); | ||
591 | } | ||
592 | |||
593 | return 0; | ||
594 | |||
595 | recv_failed: | ||
596 | sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n", | ||
597 | result); | ||
598 | err = -ENODEV; | ||
599 | err_free: | ||
600 | kfree(buf); | ||
601 | kfree(ses_dev->page10); | ||
602 | kfree(ses_dev->page2); | ||
603 | kfree(ses_dev->page1); | ||
604 | err_init_free: | ||
605 | kfree(ses_dev); | ||
606 | kfree(hdr_buf); | ||
607 | sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err); | ||
608 | return err; | ||
609 | } | ||
610 | |||
611 | static int ses_remove(struct device *dev) | ||
612 | { | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | static void ses_intf_remove(struct class_device *cdev, | ||
617 | struct class_interface *intf) | ||
618 | { | ||
619 | struct scsi_device *sdev = to_scsi_device(cdev->dev); | ||
620 | struct enclosure_device *edev; | ||
621 | struct ses_device *ses_dev; | ||
622 | |||
623 | if (!scsi_device_enclosure(sdev)) | ||
624 | return; | ||
625 | |||
626 | edev = enclosure_find(cdev->dev); | ||
627 | if (!edev) | ||
628 | return; | ||
629 | |||
630 | ses_dev = edev->scratch; | ||
631 | edev->scratch = NULL; | ||
632 | |||
633 | kfree(ses_dev->page1); | ||
634 | kfree(ses_dev->page2); | ||
635 | kfree(ses_dev); | ||
636 | |||
637 | kfree(edev->component[0].scratch); | ||
638 | |||
639 | class_device_put(&edev->cdev); | ||
640 | enclosure_unregister(edev); | ||
641 | } | ||
642 | |||
643 | static struct class_interface ses_interface = { | ||
644 | .add = ses_intf_add, | ||
645 | .remove = ses_intf_remove, | ||
646 | }; | ||
647 | |||
648 | static struct scsi_driver ses_template = { | ||
649 | .owner = THIS_MODULE, | ||
650 | .gendrv = { | ||
651 | .name = "ses", | ||
652 | .probe = ses_probe, | ||
653 | .remove = ses_remove, | ||
654 | }, | ||
655 | }; | ||
656 | |||
657 | static int __init ses_init(void) | ||
658 | { | ||
659 | int err; | ||
660 | |||
661 | err = scsi_register_interface(&ses_interface); | ||
662 | if (err) | ||
663 | return err; | ||
664 | |||
665 | err = scsi_register_driver(&ses_template.gendrv); | ||
666 | if (err) | ||
667 | goto out_unreg; | ||
668 | |||
669 | return 0; | ||
670 | |||
671 | out_unreg: | ||
672 | scsi_unregister_interface(&ses_interface); | ||
673 | return err; | ||
674 | } | ||
675 | |||
676 | static void __exit ses_exit(void) | ||
677 | { | ||
678 | scsi_unregister_driver(&ses_template.gendrv); | ||
679 | scsi_unregister_interface(&ses_interface); | ||
680 | } | ||
681 | |||
682 | module_init(ses_init); | ||
683 | module_exit(ses_exit); | ||
684 | |||
685 | MODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE); | ||
686 | |||
687 | MODULE_AUTHOR("James Bottomley"); | ||
688 | MODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver"); | ||
689 | MODULE_LICENSE("GPL v2"); | ||