]>
Commit | Line | Data |
---|---|---|
82c29810 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9927c688 JB |
2 | /* |
3 | * SCSI Enclosure Services | |
4 | * | |
5 | * Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com> | |
5ee7e1f1 | 6 | */ |
9927c688 | 7 | |
5a0e3ad6 | 8 | #include <linux/slab.h> |
9927c688 JB |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> | |
11 | #include <linux/enclosure.h> | |
c38c007a | 12 | #include <asm/unaligned.h> |
9927c688 JB |
13 | |
14 | #include <scsi/scsi.h> | |
15 | #include <scsi/scsi_cmnd.h> | |
16 | #include <scsi/scsi_dbg.h> | |
17 | #include <scsi/scsi_device.h> | |
18 | #include <scsi/scsi_driver.h> | |
19 | #include <scsi/scsi_host.h> | |
20 | ||
3f8d6f2a JB |
21 | #include <scsi/scsi_transport_sas.h> |
22 | ||
9927c688 | 23 | struct ses_device { |
691b4773 | 24 | unsigned char *page1; |
8c3adc79 | 25 | unsigned char *page1_types; |
691b4773 YL |
26 | unsigned char *page2; |
27 | unsigned char *page10; | |
9927c688 | 28 | short page1_len; |
8c3adc79 | 29 | short page1_num_types; |
9927c688 JB |
30 | short page2_len; |
31 | short page10_len; | |
32 | }; | |
33 | ||
34 | struct ses_component { | |
35 | u64 addr; | |
9927c688 JB |
36 | }; |
37 | ||
dc56ce12 HR |
38 | static bool ses_page2_supported(struct enclosure_device *edev) |
39 | { | |
40 | struct ses_device *ses_dev = edev->scratch; | |
41 | ||
42 | return (ses_dev->page2 != NULL); | |
43 | } | |
44 | ||
9927c688 JB |
45 | static int ses_probe(struct device *dev) |
46 | { | |
47 | struct scsi_device *sdev = to_scsi_device(dev); | |
48 | int err = -ENODEV; | |
49 | ||
50 | if (sdev->type != TYPE_ENCLOSURE) | |
51 | goto out; | |
52 | ||
53 | err = 0; | |
54 | sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n"); | |
55 | ||
56 | out: | |
57 | return err; | |
58 | } | |
59 | ||
c95e62ce | 60 | #define SES_TIMEOUT (30 * HZ) |
9927c688 JB |
61 | #define SES_RETRIES 3 |
62 | ||
08024885 SL |
63 | static void init_device_slot_control(unsigned char *dest_desc, |
64 | struct enclosure_component *ecomp, | |
65 | unsigned char *status) | |
66 | { | |
67 | memcpy(dest_desc, status, 4); | |
68 | dest_desc[0] = 0; | |
69 | /* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */ | |
70 | if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE) | |
71 | dest_desc[1] = 0; | |
72 | dest_desc[2] &= 0xde; | |
73 | dest_desc[3] &= 0x3c; | |
74 | } | |
75 | ||
76 | ||
9927c688 JB |
77 | static int ses_recv_diag(struct scsi_device *sdev, int page_code, |
78 | void *buf, int bufflen) | |
79 | { | |
3417c1b5 | 80 | int ret; |
691b4773 | 81 | unsigned char cmd[] = { |
9927c688 JB |
82 | RECEIVE_DIAGNOSTIC, |
83 | 1, /* Set PCV bit */ | |
84 | page_code, | |
85 | bufflen >> 8, | |
86 | bufflen & 0xff, | |
87 | 0 | |
88 | }; | |
3417c1b5 | 89 | unsigned char recv_page_code; |
9927c688 | 90 | |
3417c1b5 | 91 | ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, |
f4f4e47e | 92 | NULL, SES_TIMEOUT, SES_RETRIES, NULL); |
424f727b | 93 | if (unlikely(ret)) |
3417c1b5 JB |
94 | return ret; |
95 | ||
96 | recv_page_code = ((unsigned char *)buf)[0]; | |
97 | ||
98 | if (likely(recv_page_code == page_code)) | |
99 | return ret; | |
100 | ||
101 | /* successful diagnostic but wrong page code. This happens to some | |
102 | * USB devices, just print a message and pretend there was an error */ | |
103 | ||
104 | sdev_printk(KERN_ERR, sdev, | |
105 | "Wrong diagnostic page; asked for %d got %u\n", | |
106 | page_code, recv_page_code); | |
107 | ||
108 | return -EINVAL; | |
9927c688 JB |
109 | } |
110 | ||
111 | static int ses_send_diag(struct scsi_device *sdev, int page_code, | |
112 | void *buf, int bufflen) | |
113 | { | |
114 | u32 result; | |
115 | ||
691b4773 | 116 | unsigned char cmd[] = { |
9927c688 JB |
117 | SEND_DIAGNOSTIC, |
118 | 0x10, /* Set PF bit */ | |
119 | 0, | |
120 | bufflen >> 8, | |
121 | bufflen & 0xff, | |
122 | 0 | |
123 | }; | |
124 | ||
125 | result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen, | |
f4f4e47e | 126 | NULL, SES_TIMEOUT, SES_RETRIES, NULL); |
9927c688 JB |
127 | if (result) |
128 | sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n", | |
129 | result); | |
130 | return result; | |
131 | } | |
132 | ||
133 | static int ses_set_page2_descriptor(struct enclosure_device *edev, | |
134 | struct enclosure_component *ecomp, | |
691b4773 | 135 | unsigned char *desc) |
9927c688 JB |
136 | { |
137 | int i, j, count = 0, descriptor = ecomp->number; | |
ee959b00 | 138 | struct scsi_device *sdev = to_scsi_device(edev->edev.parent); |
9927c688 | 139 | struct ses_device *ses_dev = edev->scratch; |
8c3adc79 | 140 | unsigned char *type_ptr = ses_dev->page1_types; |
691b4773 | 141 | unsigned char *desc_ptr = ses_dev->page2 + 8; |
9927c688 JB |
142 | |
143 | /* Clear everything */ | |
144 | memset(desc_ptr, 0, ses_dev->page2_len - 8); | |
8c3adc79 | 145 | for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { |
9927c688 JB |
146 | for (j = 0; j < type_ptr[1]; j++) { |
147 | desc_ptr += 4; | |
148 | if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && | |
149 | type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) | |
150 | continue; | |
151 | if (count++ == descriptor) { | |
152 | memcpy(desc_ptr, desc, 4); | |
153 | /* set select */ | |
154 | desc_ptr[0] |= 0x80; | |
155 | /* clear reserved, just in case */ | |
156 | desc_ptr[0] &= 0xf0; | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); | |
162 | } | |
163 | ||
691b4773 | 164 | static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, |
9927c688 JB |
165 | struct enclosure_component *ecomp) |
166 | { | |
167 | int i, j, count = 0, descriptor = ecomp->number; | |
ee959b00 | 168 | struct scsi_device *sdev = to_scsi_device(edev->edev.parent); |
9927c688 | 169 | struct ses_device *ses_dev = edev->scratch; |
8c3adc79 | 170 | unsigned char *type_ptr = ses_dev->page1_types; |
691b4773 | 171 | unsigned char *desc_ptr = ses_dev->page2 + 8; |
9927c688 | 172 | |
acf8ab9a HR |
173 | if (ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len) < 0) |
174 | return NULL; | |
9927c688 | 175 | |
8c3adc79 | 176 | for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { |
9927c688 JB |
177 | for (j = 0; j < type_ptr[1]; j++) { |
178 | desc_ptr += 4; | |
179 | if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && | |
180 | type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) | |
181 | continue; | |
182 | if (count++ == descriptor) | |
183 | return desc_ptr; | |
184 | } | |
185 | } | |
186 | return NULL; | |
187 | } | |
188 | ||
2a350cab DG |
189 | /* For device slot and array device slot elements, byte 3 bit 6 |
190 | * is "fault sensed" while byte 3 bit 5 is "fault reqstd". As this | |
191 | * code stands these bits are shifted 4 positions right so in | |
192 | * sysfs they will appear as bits 2 and 1 respectively. Strange. */ | |
9927c688 JB |
193 | static void ses_get_fault(struct enclosure_device *edev, |
194 | struct enclosure_component *ecomp) | |
195 | { | |
691b4773 | 196 | unsigned char *desc; |
9927c688 | 197 | |
dc56ce12 HR |
198 | if (!ses_page2_supported(edev)) { |
199 | ecomp->fault = 0; | |
200 | return; | |
201 | } | |
9927c688 | 202 | desc = ses_get_page2_descriptor(edev, ecomp); |
691b4773 YL |
203 | if (desc) |
204 | ecomp->fault = (desc[3] & 0x60) >> 4; | |
9927c688 JB |
205 | } |
206 | ||
207 | static int ses_set_fault(struct enclosure_device *edev, | |
208 | struct enclosure_component *ecomp, | |
209 | enum enclosure_component_setting val) | |
210 | { | |
08024885 SL |
211 | unsigned char desc[4]; |
212 | unsigned char *desc_ptr; | |
213 | ||
dc56ce12 HR |
214 | if (!ses_page2_supported(edev)) |
215 | return -EINVAL; | |
216 | ||
08024885 SL |
217 | desc_ptr = ses_get_page2_descriptor(edev, ecomp); |
218 | ||
219 | if (!desc_ptr) | |
220 | return -EIO; | |
221 | ||
222 | init_device_slot_control(desc, ecomp, desc_ptr); | |
9927c688 JB |
223 | |
224 | switch (val) { | |
225 | case ENCLOSURE_SETTING_DISABLED: | |
08024885 | 226 | desc[3] &= 0xdf; |
9927c688 JB |
227 | break; |
228 | case ENCLOSURE_SETTING_ENABLED: | |
08024885 | 229 | desc[3] |= 0x20; |
9927c688 JB |
230 | break; |
231 | default: | |
232 | /* SES doesn't do the SGPIO blink settings */ | |
233 | return -EINVAL; | |
234 | } | |
235 | ||
236 | return ses_set_page2_descriptor(edev, ecomp, desc); | |
237 | } | |
238 | ||
239 | static void ses_get_status(struct enclosure_device *edev, | |
240 | struct enclosure_component *ecomp) | |
241 | { | |
691b4773 | 242 | unsigned char *desc; |
9927c688 | 243 | |
dc56ce12 HR |
244 | if (!ses_page2_supported(edev)) { |
245 | ecomp->status = 0; | |
246 | return; | |
247 | } | |
9927c688 | 248 | desc = ses_get_page2_descriptor(edev, ecomp); |
691b4773 YL |
249 | if (desc) |
250 | ecomp->status = (desc[0] & 0x0f); | |
9927c688 JB |
251 | } |
252 | ||
253 | static void ses_get_locate(struct enclosure_device *edev, | |
254 | struct enclosure_component *ecomp) | |
255 | { | |
691b4773 | 256 | unsigned char *desc; |
9927c688 | 257 | |
dc56ce12 HR |
258 | if (!ses_page2_supported(edev)) { |
259 | ecomp->locate = 0; | |
260 | return; | |
261 | } | |
9927c688 | 262 | desc = ses_get_page2_descriptor(edev, ecomp); |
691b4773 YL |
263 | if (desc) |
264 | ecomp->locate = (desc[2] & 0x02) ? 1 : 0; | |
9927c688 JB |
265 | } |
266 | ||
267 | static int ses_set_locate(struct enclosure_device *edev, | |
268 | struct enclosure_component *ecomp, | |
269 | enum enclosure_component_setting val) | |
270 | { | |
08024885 SL |
271 | unsigned char desc[4]; |
272 | unsigned char *desc_ptr; | |
273 | ||
dc56ce12 HR |
274 | if (!ses_page2_supported(edev)) |
275 | return -EINVAL; | |
276 | ||
08024885 SL |
277 | desc_ptr = ses_get_page2_descriptor(edev, ecomp); |
278 | ||
279 | if (!desc_ptr) | |
280 | return -EIO; | |
281 | ||
282 | init_device_slot_control(desc, ecomp, desc_ptr); | |
9927c688 JB |
283 | |
284 | switch (val) { | |
285 | case ENCLOSURE_SETTING_DISABLED: | |
08024885 | 286 | desc[2] &= 0xfd; |
9927c688 JB |
287 | break; |
288 | case ENCLOSURE_SETTING_ENABLED: | |
08024885 | 289 | desc[2] |= 0x02; |
9927c688 JB |
290 | break; |
291 | default: | |
292 | /* SES doesn't do the SGPIO blink settings */ | |
293 | return -EINVAL; | |
294 | } | |
295 | return ses_set_page2_descriptor(edev, ecomp, desc); | |
296 | } | |
297 | ||
298 | static int ses_set_active(struct enclosure_device *edev, | |
299 | struct enclosure_component *ecomp, | |
300 | enum enclosure_component_setting val) | |
301 | { | |
08024885 SL |
302 | unsigned char desc[4]; |
303 | unsigned char *desc_ptr; | |
304 | ||
dc56ce12 HR |
305 | if (!ses_page2_supported(edev)) |
306 | return -EINVAL; | |
307 | ||
08024885 SL |
308 | desc_ptr = ses_get_page2_descriptor(edev, ecomp); |
309 | ||
310 | if (!desc_ptr) | |
311 | return -EIO; | |
312 | ||
313 | init_device_slot_control(desc, ecomp, desc_ptr); | |
9927c688 JB |
314 | |
315 | switch (val) { | |
316 | case ENCLOSURE_SETTING_DISABLED: | |
08024885 | 317 | desc[2] &= 0x7f; |
9927c688 JB |
318 | ecomp->active = 0; |
319 | break; | |
320 | case ENCLOSURE_SETTING_ENABLED: | |
08024885 | 321 | desc[2] |= 0x80; |
9927c688 JB |
322 | ecomp->active = 1; |
323 | break; | |
324 | default: | |
325 | /* SES doesn't do the SGPIO blink settings */ | |
326 | return -EINVAL; | |
327 | } | |
328 | return ses_set_page2_descriptor(edev, ecomp, desc); | |
329 | } | |
330 | ||
967f7bab DW |
331 | static int ses_show_id(struct enclosure_device *edev, char *buf) |
332 | { | |
333 | struct ses_device *ses_dev = edev->scratch; | |
334 | unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4); | |
335 | ||
336 | return sprintf(buf, "%#llx\n", id); | |
337 | } | |
338 | ||
08024885 SL |
339 | static void ses_get_power_status(struct enclosure_device *edev, |
340 | struct enclosure_component *ecomp) | |
341 | { | |
342 | unsigned char *desc; | |
343 | ||
dc56ce12 HR |
344 | if (!ses_page2_supported(edev)) { |
345 | ecomp->power_status = 0; | |
346 | return; | |
347 | } | |
348 | ||
08024885 SL |
349 | desc = ses_get_page2_descriptor(edev, ecomp); |
350 | if (desc) | |
351 | ecomp->power_status = (desc[3] & 0x10) ? 0 : 1; | |
352 | } | |
353 | ||
354 | static int ses_set_power_status(struct enclosure_device *edev, | |
355 | struct enclosure_component *ecomp, | |
356 | int val) | |
357 | { | |
358 | unsigned char desc[4]; | |
359 | unsigned char *desc_ptr; | |
360 | ||
dc56ce12 HR |
361 | if (!ses_page2_supported(edev)) |
362 | return -EINVAL; | |
363 | ||
08024885 SL |
364 | desc_ptr = ses_get_page2_descriptor(edev, ecomp); |
365 | ||
366 | if (!desc_ptr) | |
367 | return -EIO; | |
368 | ||
369 | init_device_slot_control(desc, ecomp, desc_ptr); | |
370 | ||
371 | switch (val) { | |
372 | /* power = 1 is device_off = 0 and vice versa */ | |
373 | case 0: | |
374 | desc[3] |= 0x10; | |
375 | break; | |
376 | case 1: | |
377 | desc[3] &= 0xef; | |
378 | break; | |
379 | default: | |
380 | return -EINVAL; | |
381 | } | |
382 | ecomp->power_status = val; | |
383 | return ses_set_page2_descriptor(edev, ecomp, desc); | |
384 | } | |
385 | ||
9927c688 JB |
386 | static struct enclosure_component_callbacks ses_enclosure_callbacks = { |
387 | .get_fault = ses_get_fault, | |
388 | .set_fault = ses_set_fault, | |
389 | .get_status = ses_get_status, | |
390 | .get_locate = ses_get_locate, | |
391 | .set_locate = ses_set_locate, | |
08024885 SL |
392 | .get_power_status = ses_get_power_status, |
393 | .set_power_status = ses_set_power_status, | |
9927c688 | 394 | .set_active = ses_set_active, |
967f7bab | 395 | .show_id = ses_show_id, |
9927c688 JB |
396 | }; |
397 | ||
398 | struct ses_host_edev { | |
399 | struct Scsi_Host *shost; | |
400 | struct enclosure_device *edev; | |
401 | }; | |
402 | ||
e0aae1a5 | 403 | #if 0 |
9927c688 JB |
404 | int ses_match_host(struct enclosure_device *edev, void *data) |
405 | { | |
406 | struct ses_host_edev *sed = data; | |
407 | struct scsi_device *sdev; | |
408 | ||
ee959b00 | 409 | if (!scsi_is_sdev_device(edev->edev.parent)) |
9927c688 JB |
410 | return 0; |
411 | ||
ee959b00 | 412 | sdev = to_scsi_device(edev->edev.parent); |
9927c688 JB |
413 | |
414 | if (sdev->host != sed->shost) | |
415 | return 0; | |
416 | ||
417 | sed->edev = edev; | |
418 | return 1; | |
419 | } | |
e0aae1a5 | 420 | #endif /* 0 */ |
9927c688 JB |
421 | |
422 | static void ses_process_descriptor(struct enclosure_component *ecomp, | |
423 | unsigned char *desc) | |
424 | { | |
425 | int eip = desc[0] & 0x10; | |
426 | int invalid = desc[0] & 0x80; | |
427 | enum scsi_protocol proto = desc[0] & 0x0f; | |
428 | u64 addr = 0; | |
921ce7f5 | 429 | int slot = -1; |
9927c688 JB |
430 | struct ses_component *scomp = ecomp->scratch; |
431 | unsigned char *d; | |
432 | ||
9927c688 JB |
433 | if (invalid) |
434 | return; | |
435 | ||
436 | switch (proto) { | |
921ce7f5 DW |
437 | case SCSI_PROTOCOL_FCP: |
438 | if (eip) { | |
439 | d = desc + 4; | |
440 | slot = d[3]; | |
441 | } | |
442 | break; | |
9927c688 | 443 | case SCSI_PROTOCOL_SAS: |
921ce7f5 DW |
444 | if (eip) { |
445 | d = desc + 4; | |
446 | slot = d[3]; | |
9927c688 | 447 | d = desc + 8; |
921ce7f5 | 448 | } else |
9927c688 JB |
449 | d = desc + 4; |
450 | /* only take the phy0 addr */ | |
451 | addr = (u64)d[12] << 56 | | |
452 | (u64)d[13] << 48 | | |
453 | (u64)d[14] << 40 | | |
454 | (u64)d[15] << 32 | | |
455 | (u64)d[16] << 24 | | |
456 | (u64)d[17] << 16 | | |
457 | (u64)d[18] << 8 | | |
458 | (u64)d[19]; | |
459 | break; | |
460 | default: | |
461 | /* FIXME: Need to add more protocols than just SAS */ | |
462 | break; | |
463 | } | |
921ce7f5 | 464 | ecomp->slot = slot; |
9927c688 JB |
465 | scomp->addr = addr; |
466 | } | |
467 | ||
468 | struct efd { | |
469 | u64 addr; | |
470 | struct device *dev; | |
471 | }; | |
472 | ||
473 | static int ses_enclosure_find_by_addr(struct enclosure_device *edev, | |
474 | void *data) | |
475 | { | |
476 | struct efd *efd = data; | |
477 | int i; | |
478 | struct ses_component *scomp; | |
479 | ||
480 | if (!edev->component[0].scratch) | |
481 | return 0; | |
482 | ||
483 | for (i = 0; i < edev->components; i++) { | |
484 | scomp = edev->component[i].scratch; | |
485 | if (scomp->addr != efd->addr) | |
486 | continue; | |
487 | ||
15a0fbbc DW |
488 | if (enclosure_add_device(edev, i, efd->dev) == 0) |
489 | kobject_uevent(&efd->dev->kobj, KOBJ_CHANGE); | |
9927c688 JB |
490 | return 1; |
491 | } | |
492 | return 0; | |
493 | } | |
494 | ||
21fab1d0 JB |
495 | #define INIT_ALLOC_SIZE 32 |
496 | ||
497 | static void ses_enclosure_data_process(struct enclosure_device *edev, | |
498 | struct scsi_device *sdev, | |
499 | int create) | |
500 | { | |
501 | u32 result; | |
502 | unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL; | |
503 | int i, j, page7_len, len, components; | |
504 | struct ses_device *ses_dev = edev->scratch; | |
8c3adc79 | 505 | int types = ses_dev->page1_num_types; |
21fab1d0 JB |
506 | unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); |
507 | ||
508 | if (!hdr_buf) | |
509 | goto simple_populate; | |
510 | ||
511 | /* re-read page 10 */ | |
512 | if (ses_dev->page10) | |
513 | ses_recv_diag(sdev, 10, ses_dev->page10, ses_dev->page10_len); | |
514 | /* Page 7 for the descriptors is optional */ | |
515 | result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); | |
516 | if (result) | |
517 | goto simple_populate; | |
518 | ||
519 | page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | |
520 | /* add 1 for trailing '\0' we'll use */ | |
521 | buf = kzalloc(len + 1, GFP_KERNEL); | |
522 | if (!buf) | |
523 | goto simple_populate; | |
524 | result = ses_recv_diag(sdev, 7, buf, len); | |
525 | if (result) { | |
526 | simple_populate: | |
527 | kfree(buf); | |
528 | buf = NULL; | |
529 | desc_ptr = NULL; | |
530 | len = 0; | |
531 | page7_len = 0; | |
532 | } else { | |
533 | desc_ptr = buf + 8; | |
534 | len = (desc_ptr[2] << 8) + desc_ptr[3]; | |
535 | /* skip past overall descriptor */ | |
536 | desc_ptr += len + 4; | |
21fab1d0 | 537 | } |
877a5597 JH |
538 | if (ses_dev->page10) |
539 | addl_desc_ptr = ses_dev->page10 + 8; | |
8c3adc79 | 540 | type_ptr = ses_dev->page1_types; |
21fab1d0 JB |
541 | components = 0; |
542 | for (i = 0; i < types; i++, type_ptr += 4) { | |
543 | for (j = 0; j < type_ptr[1]; j++) { | |
544 | char *name = NULL; | |
545 | struct enclosure_component *ecomp; | |
546 | ||
547 | if (desc_ptr) { | |
548 | if (desc_ptr >= buf + page7_len) { | |
549 | desc_ptr = NULL; | |
550 | } else { | |
551 | len = (desc_ptr[2] << 8) + desc_ptr[3]; | |
552 | desc_ptr += 4; | |
553 | /* Add trailing zero - pushes into | |
554 | * reserved space */ | |
555 | desc_ptr[len] = '\0'; | |
556 | name = desc_ptr; | |
557 | } | |
558 | } | |
559 | if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || | |
560 | type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { | |
561 | ||
562 | if (create) | |
ed09dcc8 DW |
563 | ecomp = enclosure_component_alloc( |
564 | edev, | |
565 | components++, | |
566 | type_ptr[0], | |
567 | name); | |
21fab1d0 JB |
568 | else |
569 | ecomp = &edev->component[components++]; | |
570 | ||
ed09dcc8 DW |
571 | if (!IS_ERR(ecomp)) { |
572 | if (addl_desc_ptr) | |
573 | ses_process_descriptor( | |
574 | ecomp, | |
575 | addl_desc_ptr); | |
576 | if (create) | |
577 | enclosure_component_register( | |
578 | ecomp); | |
579 | } | |
21fab1d0 JB |
580 | } |
581 | if (desc_ptr) | |
582 | desc_ptr += len; | |
583 | ||
5e103356 JB |
584 | if (addl_desc_ptr && |
585 | /* only find additional descriptions for specific devices */ | |
586 | (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || | |
587 | type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE || | |
588 | type_ptr[0] == ENCLOSURE_COMPONENT_SAS_EXPANDER || | |
589 | /* these elements are optional */ | |
590 | type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_TARGET_PORT || | |
591 | type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT || | |
592 | type_ptr[0] == ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS)) | |
21fab1d0 JB |
593 | addl_desc_ptr += addl_desc_ptr[1] + 2; |
594 | ||
595 | } | |
596 | } | |
597 | kfree(buf); | |
598 | kfree(hdr_buf); | |
599 | } | |
600 | ||
9927c688 | 601 | static void ses_match_to_enclosure(struct enclosure_device *edev, |
9c0a5002 LD |
602 | struct scsi_device *sdev, |
603 | int refresh) | |
9927c688 | 604 | { |
9c0a5002 | 605 | struct scsi_device *edev_sdev = to_scsi_device(edev->edev.parent); |
9927c688 JB |
606 | struct efd efd = { |
607 | .addr = 0, | |
608 | }; | |
9927c688 | 609 | |
9c0a5002 LD |
610 | if (refresh) |
611 | ses_enclosure_data_process(edev, edev_sdev, 0); | |
21fab1d0 | 612 | |
9373eba6 | 613 | if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent)) |
3f8d6f2a | 614 | efd.addr = sas_get_address(sdev); |
9927c688 | 615 | |
c38c007a HR |
616 | if (efd.addr) { |
617 | efd.dev = &sdev->sdev_gendev; | |
9927c688 | 618 | |
c38c007a HR |
619 | enclosure_for_each_device(ses_enclosure_find_by_addr, &efd); |
620 | } | |
9927c688 JB |
621 | } |
622 | ||
ee959b00 | 623 | static int ses_intf_add(struct device *cdev, |
9927c688 JB |
624 | struct class_interface *intf) |
625 | { | |
ee959b00 | 626 | struct scsi_device *sdev = to_scsi_device(cdev->parent); |
9927c688 | 627 | struct scsi_device *tmp_sdev; |
81b59d75 | 628 | unsigned char *buf = NULL, *hdr_buf, *type_ptr, page; |
9927c688 JB |
629 | struct ses_device *ses_dev; |
630 | u32 result; | |
21fab1d0 | 631 | int i, types, len, components = 0; |
9927c688 | 632 | int err = -ENOMEM; |
8c3adc79 | 633 | int num_enclosures; |
9927c688 | 634 | struct enclosure_device *edev; |
7c46c20a | 635 | struct ses_component *scomp = NULL; |
9927c688 JB |
636 | |
637 | if (!scsi_device_enclosure(sdev)) { | |
638 | /* not an enclosure, but might be in one */ | |
163f52b6 JB |
639 | struct enclosure_device *prev = NULL; |
640 | ||
641 | while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { | |
9c0a5002 | 642 | ses_match_to_enclosure(edev, sdev, 1); |
163f52b6 | 643 | prev = edev; |
9927c688 JB |
644 | } |
645 | return -ENODEV; | |
646 | } | |
647 | ||
648 | /* TYPE_ENCLOSURE prints a message in probe */ | |
649 | if (sdev->type != TYPE_ENCLOSURE) | |
650 | sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n"); | |
651 | ||
652 | ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL); | |
653 | hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); | |
654 | if (!hdr_buf || !ses_dev) | |
655 | goto err_init_free; | |
656 | ||
81b59d75 HR |
657 | page = 1; |
658 | result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); | |
9927c688 JB |
659 | if (result) |
660 | goto recv_failed; | |
661 | ||
9927c688 JB |
662 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; |
663 | buf = kzalloc(len, GFP_KERNEL); | |
664 | if (!buf) | |
665 | goto err_free; | |
666 | ||
81b59d75 | 667 | result = ses_recv_diag(sdev, page, buf, len); |
9927c688 JB |
668 | if (result) |
669 | goto recv_failed; | |
670 | ||
8c3adc79 | 671 | types = 0; |
9927c688 | 672 | |
8c3adc79 JB |
673 | /* we always have one main enclosure and the rest are referred |
674 | * to as secondary subenclosures */ | |
675 | num_enclosures = buf[1] + 1; | |
9927c688 | 676 | |
8c3adc79 JB |
677 | /* begin at the enclosure descriptor */ |
678 | type_ptr = buf + 8; | |
679 | /* skip all the enclosure descriptors */ | |
680 | for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) { | |
681 | types += type_ptr[2]; | |
682 | type_ptr += type_ptr[3] + 4; | |
683 | } | |
684 | ||
685 | ses_dev->page1_types = type_ptr; | |
686 | ses_dev->page1_num_types = types; | |
687 | ||
688 | for (i = 0; i < types && type_ptr < buf + len; i++, type_ptr += 4) { | |
9927c688 JB |
689 | if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || |
690 | type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) | |
691 | components += type_ptr[1]; | |
692 | } | |
7c46c20a YL |
693 | ses_dev->page1 = buf; |
694 | ses_dev->page1_len = len; | |
695 | buf = NULL; | |
9927c688 | 696 | |
81b59d75 HR |
697 | page = 2; |
698 | result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); | |
9927c688 | 699 | if (result) |
dc56ce12 | 700 | goto page2_not_supported; |
9927c688 JB |
701 | |
702 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | |
703 | buf = kzalloc(len, GFP_KERNEL); | |
704 | if (!buf) | |
705 | goto err_free; | |
706 | ||
707 | /* make sure getting page 2 actually works */ | |
708 | result = ses_recv_diag(sdev, 2, buf, len); | |
709 | if (result) | |
710 | goto recv_failed; | |
711 | ses_dev->page2 = buf; | |
712 | ses_dev->page2_len = len; | |
7c46c20a | 713 | buf = NULL; |
9927c688 JB |
714 | |
715 | /* The additional information page --- allows us | |
716 | * to match up the devices */ | |
81b59d75 HR |
717 | page = 10; |
718 | result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE); | |
691b4773 YL |
719 | if (!result) { |
720 | ||
721 | len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; | |
722 | buf = kzalloc(len, GFP_KERNEL); | |
723 | if (!buf) | |
724 | goto err_free; | |
725 | ||
81b59d75 | 726 | result = ses_recv_diag(sdev, page, buf, len); |
691b4773 YL |
727 | if (result) |
728 | goto recv_failed; | |
729 | ses_dev->page10 = buf; | |
730 | ses_dev->page10_len = len; | |
731 | buf = NULL; | |
732 | } | |
dc56ce12 | 733 | page2_not_supported: |
6396bb22 | 734 | scomp = kcalloc(components, sizeof(struct ses_component), GFP_KERNEL); |
9927c688 | 735 | if (!scomp) |
7c46c20a | 736 | goto err_free; |
9927c688 | 737 | |
71610f55 | 738 | edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev), |
9927c688 JB |
739 | components, &ses_enclosure_callbacks); |
740 | if (IS_ERR(edev)) { | |
741 | err = PTR_ERR(edev); | |
742 | goto err_free; | |
743 | } | |
744 | ||
9b3a6549 JL |
745 | kfree(hdr_buf); |
746 | ||
9927c688 JB |
747 | edev->scratch = ses_dev; |
748 | for (i = 0; i < components; i++) | |
7c46c20a | 749 | edev->component[i].scratch = scomp + i; |
9927c688 | 750 | |
21fab1d0 | 751 | ses_enclosure_data_process(edev, sdev, 1); |
9927c688 JB |
752 | |
753 | /* see if there are any devices matching before | |
754 | * we found the enclosure */ | |
755 | shost_for_each_device(tmp_sdev, sdev->host) { | |
756 | if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev)) | |
757 | continue; | |
9c0a5002 | 758 | ses_match_to_enclosure(edev, tmp_sdev, 0); |
9927c688 JB |
759 | } |
760 | ||
761 | return 0; | |
762 | ||
763 | recv_failed: | |
764 | sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n", | |
81b59d75 | 765 | page); |
9927c688 JB |
766 | err = -ENODEV; |
767 | err_free: | |
768 | kfree(buf); | |
7c46c20a | 769 | kfree(scomp); |
9927c688 JB |
770 | kfree(ses_dev->page10); |
771 | kfree(ses_dev->page2); | |
772 | kfree(ses_dev->page1); | |
773 | err_init_free: | |
774 | kfree(ses_dev); | |
775 | kfree(hdr_buf); | |
776 | sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err); | |
777 | return err; | |
778 | } | |
779 | ||
780 | static int ses_remove(struct device *dev) | |
781 | { | |
782 | return 0; | |
783 | } | |
784 | ||
43d8eb9c JB |
785 | static void ses_intf_remove_component(struct scsi_device *sdev) |
786 | { | |
787 | struct enclosure_device *edev, *prev = NULL; | |
788 | ||
789 | while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) { | |
790 | prev = edev; | |
791 | if (!enclosure_remove_device(edev, &sdev->sdev_gendev)) | |
792 | break; | |
793 | } | |
794 | if (edev) | |
795 | put_device(&edev->edev); | |
796 | } | |
797 | ||
798 | static void ses_intf_remove_enclosure(struct scsi_device *sdev) | |
9927c688 | 799 | { |
9927c688 JB |
800 | struct enclosure_device *edev; |
801 | struct ses_device *ses_dev; | |
802 | ||
163f52b6 | 803 | /* exact match to this enclosure */ |
43d8eb9c | 804 | edev = enclosure_find(&sdev->sdev_gendev, NULL); |
9927c688 JB |
805 | if (!edev) |
806 | return; | |
807 | ||
808 | ses_dev = edev->scratch; | |
809 | edev->scratch = NULL; | |
810 | ||
7c46c20a | 811 | kfree(ses_dev->page10); |
9927c688 JB |
812 | kfree(ses_dev->page1); |
813 | kfree(ses_dev->page2); | |
814 | kfree(ses_dev); | |
815 | ||
816 | kfree(edev->component[0].scratch); | |
817 | ||
ee959b00 | 818 | put_device(&edev->edev); |
a5a039b0 | 819 | enclosure_unregister(edev); |
9927c688 JB |
820 | } |
821 | ||
43d8eb9c JB |
822 | static void ses_intf_remove(struct device *cdev, |
823 | struct class_interface *intf) | |
824 | { | |
825 | struct scsi_device *sdev = to_scsi_device(cdev->parent); | |
826 | ||
827 | if (!scsi_device_enclosure(sdev)) | |
828 | ses_intf_remove_component(sdev); | |
829 | else | |
830 | ses_intf_remove_enclosure(sdev); | |
831 | } | |
832 | ||
9927c688 | 833 | static struct class_interface ses_interface = { |
ee959b00 TJ |
834 | .add_dev = ses_intf_add, |
835 | .remove_dev = ses_intf_remove, | |
9927c688 JB |
836 | }; |
837 | ||
838 | static struct scsi_driver ses_template = { | |
9927c688 JB |
839 | .gendrv = { |
840 | .name = "ses", | |
3af6b352 | 841 | .owner = THIS_MODULE, |
9927c688 JB |
842 | .probe = ses_probe, |
843 | .remove = ses_remove, | |
844 | }, | |
845 | }; | |
846 | ||
847 | static int __init ses_init(void) | |
848 | { | |
849 | int err; | |
850 | ||
851 | err = scsi_register_interface(&ses_interface); | |
852 | if (err) | |
853 | return err; | |
854 | ||
855 | err = scsi_register_driver(&ses_template.gendrv); | |
856 | if (err) | |
857 | goto out_unreg; | |
858 | ||
859 | return 0; | |
860 | ||
861 | out_unreg: | |
862 | scsi_unregister_interface(&ses_interface); | |
863 | return err; | |
864 | } | |
865 | ||
866 | static void __exit ses_exit(void) | |
867 | { | |
868 | scsi_unregister_driver(&ses_template.gendrv); | |
869 | scsi_unregister_interface(&ses_interface); | |
870 | } | |
871 | ||
872 | module_init(ses_init); | |
873 | module_exit(ses_exit); | |
874 | ||
875 | MODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE); | |
876 | ||
877 | MODULE_AUTHOR("James Bottomley"); | |
878 | MODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver"); | |
879 | MODULE_LICENSE("GPL v2"); |