Root/
1 | /* |
2 | * 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 | #include <linux/device.h> |
24 | #include <linux/enclosure.h> |
25 | #include <linux/err.h> |
26 | #include <linux/list.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/module.h> |
29 | #include <linux/mutex.h> |
30 | #include <linux/slab.h> |
31 | |
32 | static LIST_HEAD(container_list); |
33 | static DEFINE_MUTEX(container_list_lock); |
34 | static struct class enclosure_class; |
35 | |
36 | /** |
37 | * enclosure_find - find an enclosure given a parent device |
38 | * @dev: the parent to match against |
39 | * @start: Optional enclosure device to start from (NULL if none) |
40 | * |
41 | * Looks through the list of registered enclosures to find all those |
42 | * with @dev as a parent. Returns NULL if no enclosure is |
43 | * found. @start can be used as a starting point to obtain multiple |
44 | * enclosures per parent (should begin with NULL and then be set to |
45 | * each returned enclosure device). Obtains a reference to the |
46 | * enclosure class device which must be released with device_put(). |
47 | * If @start is not NULL, a reference must be taken on it which is |
48 | * released before returning (this allows a loop through all |
49 | * enclosures to exit with only the reference on the enclosure of |
50 | * interest held). Note that the @dev may correspond to the actual |
51 | * device housing the enclosure, in which case no iteration via @start |
52 | * is required. |
53 | */ |
54 | struct enclosure_device *enclosure_find(struct device *dev, |
55 | struct enclosure_device *start) |
56 | { |
57 | struct enclosure_device *edev; |
58 | |
59 | mutex_lock(&container_list_lock); |
60 | edev = list_prepare_entry(start, &container_list, node); |
61 | if (start) |
62 | put_device(&start->edev); |
63 | |
64 | list_for_each_entry_continue(edev, &container_list, node) { |
65 | struct device *parent = edev->edev.parent; |
66 | /* parent might not be immediate, so iterate up to |
67 | * the root of the tree if necessary */ |
68 | while (parent) { |
69 | if (parent == dev) { |
70 | get_device(&edev->edev); |
71 | mutex_unlock(&container_list_lock); |
72 | return edev; |
73 | } |
74 | parent = parent->parent; |
75 | } |
76 | } |
77 | mutex_unlock(&container_list_lock); |
78 | |
79 | return NULL; |
80 | } |
81 | EXPORT_SYMBOL_GPL(enclosure_find); |
82 | |
83 | /** |
84 | * enclosure_for_each_device - calls a function for each enclosure |
85 | * @fn: the function to call |
86 | * @data: the data to pass to each call |
87 | * |
88 | * Loops over all the enclosures calling the function. |
89 | * |
90 | * Note, this function uses a mutex which will be held across calls to |
91 | * @fn, so it must have non atomic context, and @fn may (although it |
92 | * should not) sleep or otherwise cause the mutex to be held for |
93 | * indefinite periods |
94 | */ |
95 | int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), |
96 | void *data) |
97 | { |
98 | int error = 0; |
99 | struct enclosure_device *edev; |
100 | |
101 | mutex_lock(&container_list_lock); |
102 | list_for_each_entry(edev, &container_list, node) { |
103 | error = fn(edev, data); |
104 | if (error) |
105 | break; |
106 | } |
107 | mutex_unlock(&container_list_lock); |
108 | |
109 | return error; |
110 | } |
111 | EXPORT_SYMBOL_GPL(enclosure_for_each_device); |
112 | |
113 | /** |
114 | * enclosure_register - register device as an enclosure |
115 | * |
116 | * @dev: device containing the enclosure |
117 | * @components: number of components in the enclosure |
118 | * |
119 | * This sets up the device for being an enclosure. Note that @dev does |
120 | * not have to be a dedicated enclosure device. It may be some other type |
121 | * of device that additionally responds to enclosure services |
122 | */ |
123 | struct enclosure_device * |
124 | enclosure_register(struct device *dev, const char *name, int components, |
125 | struct enclosure_component_callbacks *cb) |
126 | { |
127 | struct enclosure_device *edev = |
128 | kzalloc(sizeof(struct enclosure_device) + |
129 | sizeof(struct enclosure_component)*components, |
130 | GFP_KERNEL); |
131 | int err, i; |
132 | |
133 | BUG_ON(!cb); |
134 | |
135 | if (!edev) |
136 | return ERR_PTR(-ENOMEM); |
137 | |
138 | edev->components = components; |
139 | |
140 | edev->edev.class = &enclosure_class; |
141 | edev->edev.parent = get_device(dev); |
142 | edev->cb = cb; |
143 | dev_set_name(&edev->edev, "%s", name); |
144 | err = device_register(&edev->edev); |
145 | if (err) |
146 | goto err; |
147 | |
148 | for (i = 0; i < components; i++) |
149 | edev->component[i].number = -1; |
150 | |
151 | mutex_lock(&container_list_lock); |
152 | list_add_tail(&edev->node, &container_list); |
153 | mutex_unlock(&container_list_lock); |
154 | |
155 | return edev; |
156 | |
157 | err: |
158 | put_device(edev->edev.parent); |
159 | kfree(edev); |
160 | return ERR_PTR(err); |
161 | } |
162 | EXPORT_SYMBOL_GPL(enclosure_register); |
163 | |
164 | static struct enclosure_component_callbacks enclosure_null_callbacks; |
165 | |
166 | /** |
167 | * enclosure_unregister - remove an enclosure |
168 | * |
169 | * @edev: the registered enclosure to remove; |
170 | */ |
171 | void enclosure_unregister(struct enclosure_device *edev) |
172 | { |
173 | int i; |
174 | |
175 | mutex_lock(&container_list_lock); |
176 | list_del(&edev->node); |
177 | mutex_unlock(&container_list_lock); |
178 | |
179 | for (i = 0; i < edev->components; i++) |
180 | if (edev->component[i].number != -1) |
181 | device_unregister(&edev->component[i].cdev); |
182 | |
183 | /* prevent any callbacks into service user */ |
184 | edev->cb = &enclosure_null_callbacks; |
185 | device_unregister(&edev->edev); |
186 | } |
187 | EXPORT_SYMBOL_GPL(enclosure_unregister); |
188 | |
189 | #define ENCLOSURE_NAME_SIZE 64 |
190 | |
191 | static void enclosure_link_name(struct enclosure_component *cdev, char *name) |
192 | { |
193 | strcpy(name, "enclosure_device:"); |
194 | strcat(name, dev_name(&cdev->cdev)); |
195 | } |
196 | |
197 | static void enclosure_remove_links(struct enclosure_component *cdev) |
198 | { |
199 | char name[ENCLOSURE_NAME_SIZE]; |
200 | |
201 | enclosure_link_name(cdev, name); |
202 | sysfs_remove_link(&cdev->dev->kobj, name); |
203 | sysfs_remove_link(&cdev->cdev.kobj, "device"); |
204 | } |
205 | |
206 | static int enclosure_add_links(struct enclosure_component *cdev) |
207 | { |
208 | int error; |
209 | char name[ENCLOSURE_NAME_SIZE]; |
210 | |
211 | error = sysfs_create_link(&cdev->cdev.kobj, &cdev->dev->kobj, "device"); |
212 | if (error) |
213 | return error; |
214 | |
215 | enclosure_link_name(cdev, name); |
216 | error = sysfs_create_link(&cdev->dev->kobj, &cdev->cdev.kobj, name); |
217 | if (error) |
218 | sysfs_remove_link(&cdev->cdev.kobj, "device"); |
219 | |
220 | return error; |
221 | } |
222 | |
223 | static void enclosure_release(struct device *cdev) |
224 | { |
225 | struct enclosure_device *edev = to_enclosure_device(cdev); |
226 | |
227 | put_device(cdev->parent); |
228 | kfree(edev); |
229 | } |
230 | |
231 | static void enclosure_component_release(struct device *dev) |
232 | { |
233 | struct enclosure_component *cdev = to_enclosure_component(dev); |
234 | |
235 | if (cdev->dev) { |
236 | enclosure_remove_links(cdev); |
237 | put_device(cdev->dev); |
238 | } |
239 | put_device(dev->parent); |
240 | } |
241 | |
242 | static const struct attribute_group *enclosure_groups[]; |
243 | |
244 | /** |
245 | * enclosure_component_register - add a particular component to an enclosure |
246 | * @edev: the enclosure to add the component |
247 | * @num: the device number |
248 | * @type: the type of component being added |
249 | * @name: an optional name to appear in sysfs (leave NULL if none) |
250 | * |
251 | * Registers the component. The name is optional for enclosures that |
252 | * give their components a unique name. If not, leave the field NULL |
253 | * and a name will be assigned. |
254 | * |
255 | * Returns a pointer to the enclosure component or an error. |
256 | */ |
257 | struct enclosure_component * |
258 | enclosure_component_register(struct enclosure_device *edev, |
259 | unsigned int number, |
260 | enum enclosure_component_type type, |
261 | const char *name) |
262 | { |
263 | struct enclosure_component *ecomp; |
264 | struct device *cdev; |
265 | int err; |
266 | |
267 | if (number >= edev->components) |
268 | return ERR_PTR(-EINVAL); |
269 | |
270 | ecomp = &edev->component[number]; |
271 | |
272 | if (ecomp->number != -1) |
273 | return ERR_PTR(-EINVAL); |
274 | |
275 | ecomp->type = type; |
276 | ecomp->number = number; |
277 | cdev = &ecomp->cdev; |
278 | cdev->parent = get_device(&edev->edev); |
279 | if (name && name[0]) |
280 | dev_set_name(cdev, "%s", name); |
281 | else |
282 | dev_set_name(cdev, "%u", number); |
283 | |
284 | cdev->release = enclosure_component_release; |
285 | cdev->groups = enclosure_groups; |
286 | |
287 | err = device_register(cdev); |
288 | if (err) { |
289 | ecomp->number = -1; |
290 | put_device(cdev); |
291 | return ERR_PTR(err); |
292 | } |
293 | |
294 | return ecomp; |
295 | } |
296 | EXPORT_SYMBOL_GPL(enclosure_component_register); |
297 | |
298 | /** |
299 | * enclosure_add_device - add a device as being part of an enclosure |
300 | * @edev: the enclosure device being added to. |
301 | * @num: the number of the component |
302 | * @dev: the device being added |
303 | * |
304 | * Declares a real device to reside in slot (or identifier) @num of an |
305 | * enclosure. This will cause the relevant sysfs links to appear. |
306 | * This function may also be used to change a device associated with |
307 | * an enclosure without having to call enclosure_remove_device() in |
308 | * between. |
309 | * |
310 | * Returns zero on success or an error. |
311 | */ |
312 | int enclosure_add_device(struct enclosure_device *edev, int component, |
313 | struct device *dev) |
314 | { |
315 | struct enclosure_component *cdev; |
316 | |
317 | if (!edev || component >= edev->components) |
318 | return -EINVAL; |
319 | |
320 | cdev = &edev->component[component]; |
321 | |
322 | if (cdev->dev == dev) |
323 | return -EEXIST; |
324 | |
325 | if (cdev->dev) |
326 | enclosure_remove_links(cdev); |
327 | |
328 | put_device(cdev->dev); |
329 | cdev->dev = get_device(dev); |
330 | return enclosure_add_links(cdev); |
331 | } |
332 | EXPORT_SYMBOL_GPL(enclosure_add_device); |
333 | |
334 | /** |
335 | * enclosure_remove_device - remove a device from an enclosure |
336 | * @edev: the enclosure device |
337 | * @num: the number of the component to remove |
338 | * |
339 | * Returns zero on success or an error. |
340 | * |
341 | */ |
342 | int enclosure_remove_device(struct enclosure_device *edev, struct device *dev) |
343 | { |
344 | struct enclosure_component *cdev; |
345 | int i; |
346 | |
347 | if (!edev || !dev) |
348 | return -EINVAL; |
349 | |
350 | for (i = 0; i < edev->components; i++) { |
351 | cdev = &edev->component[i]; |
352 | if (cdev->dev == dev) { |
353 | enclosure_remove_links(cdev); |
354 | device_del(&cdev->cdev); |
355 | put_device(dev); |
356 | cdev->dev = NULL; |
357 | return device_add(&cdev->cdev); |
358 | } |
359 | } |
360 | return -ENODEV; |
361 | } |
362 | EXPORT_SYMBOL_GPL(enclosure_remove_device); |
363 | |
364 | /* |
365 | * sysfs pieces below |
366 | */ |
367 | |
368 | static ssize_t enclosure_show_components(struct device *cdev, |
369 | struct device_attribute *attr, |
370 | char *buf) |
371 | { |
372 | struct enclosure_device *edev = to_enclosure_device(cdev); |
373 | |
374 | return snprintf(buf, 40, "%d\n", edev->components); |
375 | } |
376 | |
377 | static struct device_attribute enclosure_attrs[] = { |
378 | __ATTR(components, S_IRUGO, enclosure_show_components, NULL), |
379 | __ATTR_NULL |
380 | }; |
381 | |
382 | static struct class enclosure_class = { |
383 | .name = "enclosure", |
384 | .owner = THIS_MODULE, |
385 | .dev_release = enclosure_release, |
386 | .dev_attrs = enclosure_attrs, |
387 | }; |
388 | |
389 | static const char *const enclosure_status [] = { |
390 | [ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported", |
391 | [ENCLOSURE_STATUS_OK] = "OK", |
392 | [ENCLOSURE_STATUS_CRITICAL] = "critical", |
393 | [ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical", |
394 | [ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable", |
395 | [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed", |
396 | [ENCLOSURE_STATUS_UNKNOWN] = "unknown", |
397 | [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable", |
398 | [ENCLOSURE_STATUS_MAX] = NULL, |
399 | }; |
400 | |
401 | static const char *const enclosure_type [] = { |
402 | [ENCLOSURE_COMPONENT_DEVICE] = "device", |
403 | [ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device", |
404 | }; |
405 | |
406 | static ssize_t get_component_fault(struct device *cdev, |
407 | struct device_attribute *attr, char *buf) |
408 | { |
409 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
410 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
411 | |
412 | if (edev->cb->get_fault) |
413 | edev->cb->get_fault(edev, ecomp); |
414 | return snprintf(buf, 40, "%d\n", ecomp->fault); |
415 | } |
416 | |
417 | static ssize_t set_component_fault(struct device *cdev, |
418 | struct device_attribute *attr, |
419 | const char *buf, size_t count) |
420 | { |
421 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
422 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
423 | int val = simple_strtoul(buf, NULL, 0); |
424 | |
425 | if (edev->cb->set_fault) |
426 | edev->cb->set_fault(edev, ecomp, val); |
427 | return count; |
428 | } |
429 | |
430 | static ssize_t get_component_status(struct device *cdev, |
431 | struct device_attribute *attr,char *buf) |
432 | { |
433 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
434 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
435 | |
436 | if (edev->cb->get_status) |
437 | edev->cb->get_status(edev, ecomp); |
438 | return snprintf(buf, 40, "%s\n", enclosure_status[ecomp->status]); |
439 | } |
440 | |
441 | static ssize_t set_component_status(struct device *cdev, |
442 | struct device_attribute *attr, |
443 | const char *buf, size_t count) |
444 | { |
445 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
446 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
447 | int i; |
448 | |
449 | for (i = 0; enclosure_status[i]; i++) { |
450 | if (strncmp(buf, enclosure_status[i], |
451 | strlen(enclosure_status[i])) == 0 && |
452 | (buf[strlen(enclosure_status[i])] == '\n' || |
453 | buf[strlen(enclosure_status[i])] == '\0')) |
454 | break; |
455 | } |
456 | |
457 | if (enclosure_status[i] && edev->cb->set_status) { |
458 | edev->cb->set_status(edev, ecomp, i); |
459 | return count; |
460 | } else |
461 | return -EINVAL; |
462 | } |
463 | |
464 | static ssize_t get_component_active(struct device *cdev, |
465 | struct device_attribute *attr, char *buf) |
466 | { |
467 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
468 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
469 | |
470 | if (edev->cb->get_active) |
471 | edev->cb->get_active(edev, ecomp); |
472 | return snprintf(buf, 40, "%d\n", ecomp->active); |
473 | } |
474 | |
475 | static ssize_t set_component_active(struct device *cdev, |
476 | struct device_attribute *attr, |
477 | const char *buf, size_t count) |
478 | { |
479 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
480 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
481 | int val = simple_strtoul(buf, NULL, 0); |
482 | |
483 | if (edev->cb->set_active) |
484 | edev->cb->set_active(edev, ecomp, val); |
485 | return count; |
486 | } |
487 | |
488 | static ssize_t get_component_locate(struct device *cdev, |
489 | struct device_attribute *attr, char *buf) |
490 | { |
491 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
492 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
493 | |
494 | if (edev->cb->get_locate) |
495 | edev->cb->get_locate(edev, ecomp); |
496 | return snprintf(buf, 40, "%d\n", ecomp->locate); |
497 | } |
498 | |
499 | static ssize_t set_component_locate(struct device *cdev, |
500 | struct device_attribute *attr, |
501 | const char *buf, size_t count) |
502 | { |
503 | struct enclosure_device *edev = to_enclosure_device(cdev->parent); |
504 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
505 | int val = simple_strtoul(buf, NULL, 0); |
506 | |
507 | if (edev->cb->set_locate) |
508 | edev->cb->set_locate(edev, ecomp, val); |
509 | return count; |
510 | } |
511 | |
512 | static ssize_t get_component_type(struct device *cdev, |
513 | struct device_attribute *attr, char *buf) |
514 | { |
515 | struct enclosure_component *ecomp = to_enclosure_component(cdev); |
516 | |
517 | return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]); |
518 | } |
519 | |
520 | |
521 | static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, |
522 | set_component_fault); |
523 | static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, get_component_status, |
524 | set_component_status); |
525 | static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, |
526 | set_component_active); |
527 | static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, |
528 | set_component_locate); |
529 | static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); |
530 | |
531 | static struct attribute *enclosure_component_attrs[] = { |
532 | &dev_attr_fault.attr, |
533 | &dev_attr_status.attr, |
534 | &dev_attr_active.attr, |
535 | &dev_attr_locate.attr, |
536 | &dev_attr_type.attr, |
537 | NULL |
538 | }; |
539 | |
540 | static struct attribute_group enclosure_group = { |
541 | .attrs = enclosure_component_attrs, |
542 | }; |
543 | |
544 | static const struct attribute_group *enclosure_groups[] = { |
545 | &enclosure_group, |
546 | NULL |
547 | }; |
548 | |
549 | static int __init enclosure_init(void) |
550 | { |
551 | int err; |
552 | |
553 | err = class_register(&enclosure_class); |
554 | if (err) |
555 | return err; |
556 | |
557 | return 0; |
558 | } |
559 | |
560 | static void __exit enclosure_exit(void) |
561 | { |
562 | class_unregister(&enclosure_class); |
563 | } |
564 | |
565 | module_init(enclosure_init); |
566 | module_exit(enclosure_exit); |
567 | |
568 | MODULE_AUTHOR("James Bottomley"); |
569 | MODULE_DESCRIPTION("Enclosure Services"); |
570 | MODULE_LICENSE("GPL v2"); |
571 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9