diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/scsi_sysfs.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/scsi_sysfs.c')
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c new file mode 100644 index 000000000000..134d3a3e4222 --- /dev/null +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -0,0 +1,816 @@ | |||
1 | /* | ||
2 | * scsi_sysfs.c | ||
3 | * | ||
4 | * SCSI sysfs interface routines. | ||
5 | * | ||
6 | * Created to pull SCSI mid layer sysfs routines into one file. | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/blkdev.h> | ||
13 | #include <linux/device.h> | ||
14 | |||
15 | #include <scsi/scsi.h> | ||
16 | #include <scsi/scsi_device.h> | ||
17 | #include <scsi/scsi_host.h> | ||
18 | #include <scsi/scsi_tcq.h> | ||
19 | #include <scsi/scsi_transport.h> | ||
20 | |||
21 | #include "scsi_priv.h" | ||
22 | #include "scsi_logging.h" | ||
23 | |||
24 | static struct { | ||
25 | enum scsi_device_state value; | ||
26 | char *name; | ||
27 | } sdev_states[] = { | ||
28 | { SDEV_CREATED, "created" }, | ||
29 | { SDEV_RUNNING, "running" }, | ||
30 | { SDEV_CANCEL, "cancel" }, | ||
31 | { SDEV_DEL, "deleted" }, | ||
32 | { SDEV_QUIESCE, "quiesce" }, | ||
33 | { SDEV_OFFLINE, "offline" }, | ||
34 | { SDEV_BLOCK, "blocked" }, | ||
35 | }; | ||
36 | |||
37 | const char *scsi_device_state_name(enum scsi_device_state state) | ||
38 | { | ||
39 | int i; | ||
40 | char *name = NULL; | ||
41 | |||
42 | for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) { | ||
43 | if (sdev_states[i].value == state) { | ||
44 | name = sdev_states[i].name; | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | return name; | ||
49 | } | ||
50 | |||
51 | static int check_set(unsigned int *val, char *src) | ||
52 | { | ||
53 | char *last; | ||
54 | |||
55 | if (strncmp(src, "-", 20) == 0) { | ||
56 | *val = SCAN_WILD_CARD; | ||
57 | } else { | ||
58 | /* | ||
59 | * Doesn't check for int overflow | ||
60 | */ | ||
61 | *val = simple_strtoul(src, &last, 0); | ||
62 | if (*last != '\0') | ||
63 | return 1; | ||
64 | } | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int scsi_scan(struct Scsi_Host *shost, const char *str) | ||
69 | { | ||
70 | char s1[15], s2[15], s3[15], junk; | ||
71 | unsigned int channel, id, lun; | ||
72 | int res; | ||
73 | |||
74 | res = sscanf(str, "%10s %10s %10s %c", s1, s2, s3, &junk); | ||
75 | if (res != 3) | ||
76 | return -EINVAL; | ||
77 | if (check_set(&channel, s1)) | ||
78 | return -EINVAL; | ||
79 | if (check_set(&id, s2)) | ||
80 | return -EINVAL; | ||
81 | if (check_set(&lun, s3)) | ||
82 | return -EINVAL; | ||
83 | res = scsi_scan_host_selected(shost, channel, id, lun, 1); | ||
84 | return res; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * shost_show_function: macro to create an attr function that can be used to | ||
89 | * show a non-bit field. | ||
90 | */ | ||
91 | #define shost_show_function(name, field, format_string) \ | ||
92 | static ssize_t \ | ||
93 | show_##name (struct class_device *class_dev, char *buf) \ | ||
94 | { \ | ||
95 | struct Scsi_Host *shost = class_to_shost(class_dev); \ | ||
96 | return snprintf (buf, 20, format_string, shost->field); \ | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * shost_rd_attr: macro to create a function and attribute variable for a | ||
101 | * read only field. | ||
102 | */ | ||
103 | #define shost_rd_attr2(name, field, format_string) \ | ||
104 | shost_show_function(name, field, format_string) \ | ||
105 | static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); | ||
106 | |||
107 | #define shost_rd_attr(field, format_string) \ | ||
108 | shost_rd_attr2(field, field, format_string) | ||
109 | |||
110 | /* | ||
111 | * Create the actual show/store functions and data structures. | ||
112 | */ | ||
113 | |||
114 | static ssize_t store_scan(struct class_device *class_dev, const char *buf, | ||
115 | size_t count) | ||
116 | { | ||
117 | struct Scsi_Host *shost = class_to_shost(class_dev); | ||
118 | int res; | ||
119 | |||
120 | res = scsi_scan(shost, buf); | ||
121 | if (res == 0) | ||
122 | res = count; | ||
123 | return res; | ||
124 | }; | ||
125 | static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); | ||
126 | |||
127 | shost_rd_attr(unique_id, "%u\n"); | ||
128 | shost_rd_attr(host_busy, "%hu\n"); | ||
129 | shost_rd_attr(cmd_per_lun, "%hd\n"); | ||
130 | shost_rd_attr(sg_tablesize, "%hu\n"); | ||
131 | shost_rd_attr(unchecked_isa_dma, "%d\n"); | ||
132 | shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); | ||
133 | |||
134 | static struct class_device_attribute *scsi_sysfs_shost_attrs[] = { | ||
135 | &class_device_attr_unique_id, | ||
136 | &class_device_attr_host_busy, | ||
137 | &class_device_attr_cmd_per_lun, | ||
138 | &class_device_attr_sg_tablesize, | ||
139 | &class_device_attr_unchecked_isa_dma, | ||
140 | &class_device_attr_proc_name, | ||
141 | &class_device_attr_scan, | ||
142 | NULL | ||
143 | }; | ||
144 | |||
145 | static void scsi_device_cls_release(struct class_device *class_dev) | ||
146 | { | ||
147 | struct scsi_device *sdev; | ||
148 | |||
149 | sdev = class_to_sdev(class_dev); | ||
150 | put_device(&sdev->sdev_gendev); | ||
151 | } | ||
152 | |||
153 | void scsi_device_dev_release(struct device *dev) | ||
154 | { | ||
155 | struct scsi_device *sdev; | ||
156 | struct device *parent; | ||
157 | struct scsi_target *starget; | ||
158 | unsigned long flags; | ||
159 | |||
160 | parent = dev->parent; | ||
161 | sdev = to_scsi_device(dev); | ||
162 | starget = to_scsi_target(parent); | ||
163 | |||
164 | spin_lock_irqsave(sdev->host->host_lock, flags); | ||
165 | starget->reap_ref++; | ||
166 | list_del(&sdev->siblings); | ||
167 | list_del(&sdev->same_target_siblings); | ||
168 | list_del(&sdev->starved_entry); | ||
169 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
170 | |||
171 | if (sdev->request_queue) { | ||
172 | sdev->request_queue->queuedata = NULL; | ||
173 | scsi_free_queue(sdev->request_queue); | ||
174 | } | ||
175 | |||
176 | scsi_target_reap(scsi_target(sdev)); | ||
177 | |||
178 | kfree(sdev->inquiry); | ||
179 | kfree(sdev); | ||
180 | |||
181 | if (parent) | ||
182 | put_device(parent); | ||
183 | } | ||
184 | |||
185 | struct class sdev_class = { | ||
186 | .name = "scsi_device", | ||
187 | .release = scsi_device_cls_release, | ||
188 | }; | ||
189 | |||
190 | /* all probing is done in the individual ->probe routines */ | ||
191 | static int scsi_bus_match(struct device *dev, struct device_driver *gendrv) | ||
192 | { | ||
193 | struct scsi_device *sdp = to_scsi_device(dev); | ||
194 | if (sdp->no_uld_attach) | ||
195 | return 0; | ||
196 | return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; | ||
197 | } | ||
198 | |||
199 | struct bus_type scsi_bus_type = { | ||
200 | .name = "scsi", | ||
201 | .match = scsi_bus_match, | ||
202 | }; | ||
203 | |||
204 | int scsi_sysfs_register(void) | ||
205 | { | ||
206 | int error; | ||
207 | |||
208 | error = bus_register(&scsi_bus_type); | ||
209 | if (!error) { | ||
210 | error = class_register(&sdev_class); | ||
211 | if (error) | ||
212 | bus_unregister(&scsi_bus_type); | ||
213 | } | ||
214 | |||
215 | return error; | ||
216 | } | ||
217 | |||
218 | void scsi_sysfs_unregister(void) | ||
219 | { | ||
220 | class_unregister(&sdev_class); | ||
221 | bus_unregister(&scsi_bus_type); | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * sdev_show_function: macro to create an attr function that can be used to | ||
226 | * show a non-bit field. | ||
227 | */ | ||
228 | #define sdev_show_function(field, format_string) \ | ||
229 | static ssize_t \ | ||
230 | sdev_show_##field (struct device *dev, char *buf) \ | ||
231 | { \ | ||
232 | struct scsi_device *sdev; \ | ||
233 | sdev = to_scsi_device(dev); \ | ||
234 | return snprintf (buf, 20, format_string, sdev->field); \ | ||
235 | } \ | ||
236 | |||
237 | /* | ||
238 | * sdev_rd_attr: macro to create a function and attribute variable for a | ||
239 | * read only field. | ||
240 | */ | ||
241 | #define sdev_rd_attr(field, format_string) \ | ||
242 | sdev_show_function(field, format_string) \ | ||
243 | static DEVICE_ATTR(field, S_IRUGO, sdev_show_##field, NULL); | ||
244 | |||
245 | |||
246 | /* | ||
247 | * sdev_rd_attr: create a function and attribute variable for a | ||
248 | * read/write field. | ||
249 | */ | ||
250 | #define sdev_rw_attr(field, format_string) \ | ||
251 | sdev_show_function(field, format_string) \ | ||
252 | \ | ||
253 | static ssize_t \ | ||
254 | sdev_store_##field (struct device *dev, const char *buf, size_t count) \ | ||
255 | { \ | ||
256 | struct scsi_device *sdev; \ | ||
257 | sdev = to_scsi_device(dev); \ | ||
258 | snscanf (buf, 20, format_string, &sdev->field); \ | ||
259 | return count; \ | ||
260 | } \ | ||
261 | static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field); | ||
262 | |||
263 | /* Currently we don't export bit fields, but we might in future, | ||
264 | * so leave this code in */ | ||
265 | #if 0 | ||
266 | /* | ||
267 | * sdev_rd_attr: create a function and attribute variable for a | ||
268 | * read/write bit field. | ||
269 | */ | ||
270 | #define sdev_rw_attr_bit(field) \ | ||
271 | sdev_show_function(field, "%d\n") \ | ||
272 | \ | ||
273 | static ssize_t \ | ||
274 | sdev_store_##field (struct device *dev, const char *buf, size_t count) \ | ||
275 | { \ | ||
276 | int ret; \ | ||
277 | struct scsi_device *sdev; \ | ||
278 | ret = scsi_sdev_check_buf_bit(buf); \ | ||
279 | if (ret >= 0) { \ | ||
280 | sdev = to_scsi_device(dev); \ | ||
281 | sdev->field = ret; \ | ||
282 | ret = count; \ | ||
283 | } \ | ||
284 | return ret; \ | ||
285 | } \ | ||
286 | static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field); | ||
287 | |||
288 | /* | ||
289 | * scsi_sdev_check_buf_bit: return 0 if buf is "0", return 1 if buf is "1", | ||
290 | * else return -EINVAL. | ||
291 | */ | ||
292 | static int scsi_sdev_check_buf_bit(const char *buf) | ||
293 | { | ||
294 | if ((buf[1] == '\0') || ((buf[1] == '\n') && (buf[2] == '\0'))) { | ||
295 | if (buf[0] == '1') | ||
296 | return 1; | ||
297 | else if (buf[0] == '0') | ||
298 | return 0; | ||
299 | else | ||
300 | return -EINVAL; | ||
301 | } else | ||
302 | return -EINVAL; | ||
303 | } | ||
304 | #endif | ||
305 | /* | ||
306 | * Create the actual show/store functions and data structures. | ||
307 | */ | ||
308 | sdev_rd_attr (device_blocked, "%d\n"); | ||
309 | sdev_rd_attr (queue_depth, "%d\n"); | ||
310 | sdev_rd_attr (type, "%d\n"); | ||
311 | sdev_rd_attr (scsi_level, "%d\n"); | ||
312 | sdev_rd_attr (vendor, "%.8s\n"); | ||
313 | sdev_rd_attr (model, "%.16s\n"); | ||
314 | sdev_rd_attr (rev, "%.4s\n"); | ||
315 | |||
316 | static ssize_t | ||
317 | sdev_show_timeout (struct device *dev, char *buf) | ||
318 | { | ||
319 | struct scsi_device *sdev; | ||
320 | sdev = to_scsi_device(dev); | ||
321 | return snprintf (buf, 20, "%d\n", sdev->timeout / HZ); | ||
322 | } | ||
323 | |||
324 | static ssize_t | ||
325 | sdev_store_timeout (struct device *dev, const char *buf, size_t count) | ||
326 | { | ||
327 | struct scsi_device *sdev; | ||
328 | int timeout; | ||
329 | sdev = to_scsi_device(dev); | ||
330 | sscanf (buf, "%d\n", &timeout); | ||
331 | sdev->timeout = timeout * HZ; | ||
332 | return count; | ||
333 | } | ||
334 | static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout); | ||
335 | |||
336 | static ssize_t | ||
337 | store_rescan_field (struct device *dev, const char *buf, size_t count) | ||
338 | { | ||
339 | scsi_rescan_device(dev); | ||
340 | return count; | ||
341 | } | ||
342 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); | ||
343 | |||
344 | static ssize_t sdev_store_delete(struct device *dev, const char *buf, | ||
345 | size_t count) | ||
346 | { | ||
347 | scsi_remove_device(to_scsi_device(dev)); | ||
348 | return count; | ||
349 | }; | ||
350 | static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); | ||
351 | |||
352 | static ssize_t | ||
353 | store_state_field(struct device *dev, const char *buf, size_t count) | ||
354 | { | ||
355 | int i; | ||
356 | struct scsi_device *sdev = to_scsi_device(dev); | ||
357 | enum scsi_device_state state = 0; | ||
358 | |||
359 | for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) { | ||
360 | const int len = strlen(sdev_states[i].name); | ||
361 | if (strncmp(sdev_states[i].name, buf, len) == 0 && | ||
362 | buf[len] == '\n') { | ||
363 | state = sdev_states[i].value; | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | if (!state) | ||
368 | return -EINVAL; | ||
369 | |||
370 | if (scsi_device_set_state(sdev, state)) | ||
371 | return -EINVAL; | ||
372 | return count; | ||
373 | } | ||
374 | |||
375 | static ssize_t | ||
376 | show_state_field(struct device *dev, char *buf) | ||
377 | { | ||
378 | struct scsi_device *sdev = to_scsi_device(dev); | ||
379 | const char *name = scsi_device_state_name(sdev->sdev_state); | ||
380 | |||
381 | if (!name) | ||
382 | return -EINVAL; | ||
383 | |||
384 | return snprintf(buf, 20, "%s\n", name); | ||
385 | } | ||
386 | |||
387 | static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field); | ||
388 | |||
389 | static ssize_t | ||
390 | show_queue_type_field(struct device *dev, char *buf) | ||
391 | { | ||
392 | struct scsi_device *sdev = to_scsi_device(dev); | ||
393 | const char *name = "none"; | ||
394 | |||
395 | if (sdev->ordered_tags) | ||
396 | name = "ordered"; | ||
397 | else if (sdev->simple_tags) | ||
398 | name = "simple"; | ||
399 | |||
400 | return snprintf(buf, 20, "%s\n", name); | ||
401 | } | ||
402 | |||
403 | static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL); | ||
404 | |||
405 | static ssize_t | ||
406 | show_iostat_counterbits(struct device *dev, char *buf) | ||
407 | { | ||
408 | return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8); | ||
409 | } | ||
410 | |||
411 | static DEVICE_ATTR(iocounterbits, S_IRUGO, show_iostat_counterbits, NULL); | ||
412 | |||
413 | #define show_sdev_iostat(field) \ | ||
414 | static ssize_t \ | ||
415 | show_iostat_##field(struct device *dev, char *buf) \ | ||
416 | { \ | ||
417 | struct scsi_device *sdev = to_scsi_device(dev); \ | ||
418 | unsigned long long count = atomic_read(&sdev->field); \ | ||
419 | return snprintf(buf, 20, "0x%llx\n", count); \ | ||
420 | } \ | ||
421 | static DEVICE_ATTR(field, S_IRUGO, show_iostat_##field, NULL) | ||
422 | |||
423 | show_sdev_iostat(iorequest_cnt); | ||
424 | show_sdev_iostat(iodone_cnt); | ||
425 | show_sdev_iostat(ioerr_cnt); | ||
426 | |||
427 | |||
428 | /* Default template for device attributes. May NOT be modified */ | ||
429 | static struct device_attribute *scsi_sysfs_sdev_attrs[] = { | ||
430 | &dev_attr_device_blocked, | ||
431 | &dev_attr_queue_depth, | ||
432 | &dev_attr_queue_type, | ||
433 | &dev_attr_type, | ||
434 | &dev_attr_scsi_level, | ||
435 | &dev_attr_vendor, | ||
436 | &dev_attr_model, | ||
437 | &dev_attr_rev, | ||
438 | &dev_attr_rescan, | ||
439 | &dev_attr_delete, | ||
440 | &dev_attr_state, | ||
441 | &dev_attr_timeout, | ||
442 | &dev_attr_iocounterbits, | ||
443 | &dev_attr_iorequest_cnt, | ||
444 | &dev_attr_iodone_cnt, | ||
445 | &dev_attr_ioerr_cnt, | ||
446 | NULL | ||
447 | }; | ||
448 | |||
449 | static ssize_t sdev_store_queue_depth_rw(struct device *dev, const char *buf, | ||
450 | size_t count) | ||
451 | { | ||
452 | int depth, retval; | ||
453 | struct scsi_device *sdev = to_scsi_device(dev); | ||
454 | struct scsi_host_template *sht = sdev->host->hostt; | ||
455 | |||
456 | if (!sht->change_queue_depth) | ||
457 | return -EINVAL; | ||
458 | |||
459 | depth = simple_strtoul(buf, NULL, 0); | ||
460 | |||
461 | if (depth < 1) | ||
462 | return -EINVAL; | ||
463 | |||
464 | retval = sht->change_queue_depth(sdev, depth); | ||
465 | if (retval < 0) | ||
466 | return retval; | ||
467 | |||
468 | return count; | ||
469 | } | ||
470 | |||
471 | static struct device_attribute sdev_attr_queue_depth_rw = | ||
472 | __ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth, | ||
473 | sdev_store_queue_depth_rw); | ||
474 | |||
475 | static ssize_t sdev_store_queue_type_rw(struct device *dev, const char *buf, | ||
476 | size_t count) | ||
477 | { | ||
478 | struct scsi_device *sdev = to_scsi_device(dev); | ||
479 | struct scsi_host_template *sht = sdev->host->hostt; | ||
480 | int tag_type = 0, retval; | ||
481 | int prev_tag_type = scsi_get_tag_type(sdev); | ||
482 | |||
483 | if (!sdev->tagged_supported || !sht->change_queue_type) | ||
484 | return -EINVAL; | ||
485 | |||
486 | if (strncmp(buf, "ordered", 7) == 0) | ||
487 | tag_type = MSG_ORDERED_TAG; | ||
488 | else if (strncmp(buf, "simple", 6) == 0) | ||
489 | tag_type = MSG_SIMPLE_TAG; | ||
490 | else if (strncmp(buf, "none", 4) != 0) | ||
491 | return -EINVAL; | ||
492 | |||
493 | if (tag_type == prev_tag_type) | ||
494 | return count; | ||
495 | |||
496 | retval = sht->change_queue_type(sdev, tag_type); | ||
497 | if (retval < 0) | ||
498 | return retval; | ||
499 | |||
500 | return count; | ||
501 | } | ||
502 | |||
503 | static struct device_attribute sdev_attr_queue_type_rw = | ||
504 | __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, | ||
505 | sdev_store_queue_type_rw); | ||
506 | |||
507 | static struct device_attribute *attr_changed_internally( | ||
508 | struct Scsi_Host *shost, | ||
509 | struct device_attribute * attr) | ||
510 | { | ||
511 | if (!strcmp("queue_depth", attr->attr.name) | ||
512 | && shost->hostt->change_queue_depth) | ||
513 | return &sdev_attr_queue_depth_rw; | ||
514 | else if (!strcmp("queue_type", attr->attr.name) | ||
515 | && shost->hostt->change_queue_type) | ||
516 | return &sdev_attr_queue_type_rw; | ||
517 | return attr; | ||
518 | } | ||
519 | |||
520 | |||
521 | static struct device_attribute *attr_overridden( | ||
522 | struct device_attribute **attrs, | ||
523 | struct device_attribute *attr) | ||
524 | { | ||
525 | int i; | ||
526 | |||
527 | if (!attrs) | ||
528 | return NULL; | ||
529 | for (i = 0; attrs[i]; i++) | ||
530 | if (!strcmp(attrs[i]->attr.name, attr->attr.name)) | ||
531 | return attrs[i]; | ||
532 | return NULL; | ||
533 | } | ||
534 | |||
535 | static int attr_add(struct device *dev, struct device_attribute *attr) | ||
536 | { | ||
537 | struct device_attribute *base_attr; | ||
538 | |||
539 | /* | ||
540 | * Spare the caller from having to copy things it's not interested in. | ||
541 | */ | ||
542 | base_attr = attr_overridden(scsi_sysfs_sdev_attrs, attr); | ||
543 | if (base_attr) { | ||
544 | /* extend permissions */ | ||
545 | attr->attr.mode |= base_attr->attr.mode; | ||
546 | |||
547 | /* override null show/store with default */ | ||
548 | if (!attr->show) | ||
549 | attr->show = base_attr->show; | ||
550 | if (!attr->store) | ||
551 | attr->store = base_attr->store; | ||
552 | } | ||
553 | |||
554 | return device_create_file(dev, attr); | ||
555 | } | ||
556 | |||
557 | /** | ||
558 | * scsi_sysfs_add_sdev - add scsi device to sysfs | ||
559 | * @sdev: scsi_device to add | ||
560 | * | ||
561 | * Return value: | ||
562 | * 0 on Success / non-zero on Failure | ||
563 | **/ | ||
564 | int scsi_sysfs_add_sdev(struct scsi_device *sdev) | ||
565 | { | ||
566 | int error, i; | ||
567 | |||
568 | if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) | ||
569 | return error; | ||
570 | |||
571 | error = device_add(&sdev->sdev_gendev); | ||
572 | if (error) { | ||
573 | put_device(sdev->sdev_gendev.parent); | ||
574 | printk(KERN_INFO "error 1\n"); | ||
575 | return error; | ||
576 | } | ||
577 | error = class_device_add(&sdev->sdev_classdev); | ||
578 | if (error) { | ||
579 | printk(KERN_INFO "error 2\n"); | ||
580 | goto clean_device; | ||
581 | } | ||
582 | |||
583 | /* take a reference for the sdev_classdev; this is | ||
584 | * released by the sdev_class .release */ | ||
585 | get_device(&sdev->sdev_gendev); | ||
586 | if (sdev->host->hostt->sdev_attrs) { | ||
587 | for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { | ||
588 | error = attr_add(&sdev->sdev_gendev, | ||
589 | sdev->host->hostt->sdev_attrs[i]); | ||
590 | if (error) { | ||
591 | scsi_remove_device(sdev); | ||
592 | goto out; | ||
593 | } | ||
594 | } | ||
595 | } | ||
596 | |||
597 | for (i = 0; scsi_sysfs_sdev_attrs[i]; i++) { | ||
598 | if (!attr_overridden(sdev->host->hostt->sdev_attrs, | ||
599 | scsi_sysfs_sdev_attrs[i])) { | ||
600 | struct device_attribute * attr = | ||
601 | attr_changed_internally(sdev->host, | ||
602 | scsi_sysfs_sdev_attrs[i]); | ||
603 | error = device_create_file(&sdev->sdev_gendev, attr); | ||
604 | if (error) { | ||
605 | scsi_remove_device(sdev); | ||
606 | goto out; | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | |||
611 | transport_add_device(&sdev->sdev_gendev); | ||
612 | out: | ||
613 | return error; | ||
614 | |||
615 | clean_device: | ||
616 | scsi_device_set_state(sdev, SDEV_CANCEL); | ||
617 | |||
618 | device_del(&sdev->sdev_gendev); | ||
619 | transport_destroy_device(&sdev->sdev_gendev); | ||
620 | put_device(&sdev->sdev_gendev); | ||
621 | |||
622 | return error; | ||
623 | } | ||
624 | |||
625 | /** | ||
626 | * scsi_remove_device - unregister a device from the scsi bus | ||
627 | * @sdev: scsi_device to unregister | ||
628 | **/ | ||
629 | void scsi_remove_device(struct scsi_device *sdev) | ||
630 | { | ||
631 | struct Scsi_Host *shost = sdev->host; | ||
632 | |||
633 | down(&shost->scan_mutex); | ||
634 | if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) | ||
635 | goto out; | ||
636 | |||
637 | class_device_unregister(&sdev->sdev_classdev); | ||
638 | device_del(&sdev->sdev_gendev); | ||
639 | scsi_device_set_state(sdev, SDEV_DEL); | ||
640 | if (sdev->host->hostt->slave_destroy) | ||
641 | sdev->host->hostt->slave_destroy(sdev); | ||
642 | transport_unregister_device(&sdev->sdev_gendev); | ||
643 | put_device(&sdev->sdev_gendev); | ||
644 | out: | ||
645 | up(&shost->scan_mutex); | ||
646 | } | ||
647 | EXPORT_SYMBOL(scsi_remove_device); | ||
648 | |||
649 | void __scsi_remove_target(struct scsi_target *starget) | ||
650 | { | ||
651 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
652 | unsigned long flags; | ||
653 | struct scsi_device *sdev, *tmp; | ||
654 | |||
655 | spin_lock_irqsave(shost->host_lock, flags); | ||
656 | starget->reap_ref++; | ||
657 | list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) { | ||
658 | if (sdev->channel != starget->channel || | ||
659 | sdev->id != starget->id) | ||
660 | continue; | ||
661 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
662 | scsi_remove_device(sdev); | ||
663 | spin_lock_irqsave(shost->host_lock, flags); | ||
664 | } | ||
665 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
666 | scsi_target_reap(starget); | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * scsi_remove_target - try to remove a target and all its devices | ||
671 | * @dev: generic starget or parent of generic stargets to be removed | ||
672 | * | ||
673 | * Note: This is slightly racy. It is possible that if the user | ||
674 | * requests the addition of another device then the target won't be | ||
675 | * removed. | ||
676 | */ | ||
677 | void scsi_remove_target(struct device *dev) | ||
678 | { | ||
679 | struct device *rdev, *idev, *next; | ||
680 | |||
681 | if (scsi_is_target_device(dev)) { | ||
682 | __scsi_remove_target(to_scsi_target(dev)); | ||
683 | return; | ||
684 | } | ||
685 | |||
686 | rdev = get_device(dev); | ||
687 | list_for_each_entry_safe(idev, next, &dev->children, node) { | ||
688 | if (scsi_is_target_device(idev)) | ||
689 | __scsi_remove_target(to_scsi_target(idev)); | ||
690 | } | ||
691 | put_device(rdev); | ||
692 | } | ||
693 | EXPORT_SYMBOL(scsi_remove_target); | ||
694 | |||
695 | int scsi_register_driver(struct device_driver *drv) | ||
696 | { | ||
697 | drv->bus = &scsi_bus_type; | ||
698 | |||
699 | return driver_register(drv); | ||
700 | } | ||
701 | EXPORT_SYMBOL(scsi_register_driver); | ||
702 | |||
703 | int scsi_register_interface(struct class_interface *intf) | ||
704 | { | ||
705 | intf->class = &sdev_class; | ||
706 | |||
707 | return class_interface_register(intf); | ||
708 | } | ||
709 | EXPORT_SYMBOL(scsi_register_interface); | ||
710 | |||
711 | |||
712 | static struct class_device_attribute *class_attr_overridden( | ||
713 | struct class_device_attribute **attrs, | ||
714 | struct class_device_attribute *attr) | ||
715 | { | ||
716 | int i; | ||
717 | |||
718 | if (!attrs) | ||
719 | return NULL; | ||
720 | for (i = 0; attrs[i]; i++) | ||
721 | if (!strcmp(attrs[i]->attr.name, attr->attr.name)) | ||
722 | return attrs[i]; | ||
723 | return NULL; | ||
724 | } | ||
725 | |||
726 | static int class_attr_add(struct class_device *classdev, | ||
727 | struct class_device_attribute *attr) | ||
728 | { | ||
729 | struct class_device_attribute *base_attr; | ||
730 | |||
731 | /* | ||
732 | * Spare the caller from having to copy things it's not interested in. | ||
733 | */ | ||
734 | base_attr = class_attr_overridden(scsi_sysfs_shost_attrs, attr); | ||
735 | if (base_attr) { | ||
736 | /* extend permissions */ | ||
737 | attr->attr.mode |= base_attr->attr.mode; | ||
738 | |||
739 | /* override null show/store with default */ | ||
740 | if (!attr->show) | ||
741 | attr->show = base_attr->show; | ||
742 | if (!attr->store) | ||
743 | attr->store = base_attr->store; | ||
744 | } | ||
745 | |||
746 | return class_device_create_file(classdev, attr); | ||
747 | } | ||
748 | |||
749 | /** | ||
750 | * scsi_sysfs_add_host - add scsi host to subsystem | ||
751 | * @shost: scsi host struct to add to subsystem | ||
752 | * @dev: parent struct device pointer | ||
753 | **/ | ||
754 | int scsi_sysfs_add_host(struct Scsi_Host *shost) | ||
755 | { | ||
756 | int error, i; | ||
757 | |||
758 | if (shost->hostt->shost_attrs) { | ||
759 | for (i = 0; shost->hostt->shost_attrs[i]; i++) { | ||
760 | error = class_attr_add(&shost->shost_classdev, | ||
761 | shost->hostt->shost_attrs[i]); | ||
762 | if (error) | ||
763 | return error; | ||
764 | } | ||
765 | } | ||
766 | |||
767 | for (i = 0; scsi_sysfs_shost_attrs[i]; i++) { | ||
768 | if (!class_attr_overridden(shost->hostt->shost_attrs, | ||
769 | scsi_sysfs_shost_attrs[i])) { | ||
770 | error = class_device_create_file(&shost->shost_classdev, | ||
771 | scsi_sysfs_shost_attrs[i]); | ||
772 | if (error) | ||
773 | return error; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | transport_register_device(&shost->shost_gendev); | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | void scsi_sysfs_device_initialize(struct scsi_device *sdev) | ||
782 | { | ||
783 | unsigned long flags; | ||
784 | struct Scsi_Host *shost = sdev->host; | ||
785 | struct scsi_target *starget = sdev->sdev_target; | ||
786 | |||
787 | device_initialize(&sdev->sdev_gendev); | ||
788 | sdev->sdev_gendev.bus = &scsi_bus_type; | ||
789 | sdev->sdev_gendev.release = scsi_device_dev_release; | ||
790 | sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", | ||
791 | sdev->host->host_no, sdev->channel, sdev->id, | ||
792 | sdev->lun); | ||
793 | |||
794 | class_device_initialize(&sdev->sdev_classdev); | ||
795 | sdev->sdev_classdev.dev = &sdev->sdev_gendev; | ||
796 | sdev->sdev_classdev.class = &sdev_class; | ||
797 | snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, | ||
798 | "%d:%d:%d:%d", sdev->host->host_no, | ||
799 | sdev->channel, sdev->id, sdev->lun); | ||
800 | sdev->scsi_level = SCSI_2; | ||
801 | transport_setup_device(&sdev->sdev_gendev); | ||
802 | spin_lock_irqsave(shost->host_lock, flags); | ||
803 | list_add_tail(&sdev->same_target_siblings, &starget->devices); | ||
804 | list_add_tail(&sdev->siblings, &shost->__devices); | ||
805 | spin_unlock_irqrestore(shost->host_lock, flags); | ||
806 | } | ||
807 | |||
808 | int scsi_is_sdev_device(const struct device *dev) | ||
809 | { | ||
810 | return dev->release == scsi_device_dev_release; | ||
811 | } | ||
812 | EXPORT_SYMBOL(scsi_is_sdev_device); | ||
813 | |||
814 | /* A blank transport template that is used in drivers that don't | ||
815 | * yet implement Transport Attributes */ | ||
816 | struct scsi_transport_template blank_transport_template = { { { {NULL, }, }, }, }; | ||