[SCSI] enclosure: add support for enclosure services

The enclosure misc device is really just a library providing sysfs
support for physical enclosure devices and their components.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

+623
+9
drivers/misc/Kconfig
··· 285 285 286 286 If unsure, say N. 287 287 288 + config ENCLOSURE_SERVICES 289 + tristate "Enclosure Services" 290 + default n 291 + help 292 + Provides support for intelligent enclosures (bays which 293 + contain storage devices). You also need either a host 294 + driver (SCSI/ATA) which supports enclosures 295 + or a SCSI enclosure device (SES) to use these services. 296 + 288 297 endif # MISC_DEVICES
+1
drivers/misc/Makefile
··· 20 20 obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o 21 21 obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o 22 22 obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o 23 + obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+484
drivers/misc/enclosure.c
··· 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 + 31 + static LIST_HEAD(container_list); 32 + static DEFINE_MUTEX(container_list_lock); 33 + static struct class enclosure_class; 34 + static struct class enclosure_component_class; 35 + 36 + /** 37 + * enclosure_find - find an enclosure given a device 38 + * @dev: the device to find for 39 + * 40 + * Looks through the list of registered enclosures to see 41 + * if it can find a match for a device. Returns NULL if no 42 + * enclosure is found. Obtains a reference to the enclosure class 43 + * device which must be released with class_device_put(). 44 + */ 45 + struct enclosure_device *enclosure_find(struct device *dev) 46 + { 47 + struct enclosure_device *edev = NULL; 48 + 49 + mutex_lock(&container_list_lock); 50 + list_for_each_entry(edev, &container_list, node) { 51 + if (edev->cdev.dev == dev) { 52 + class_device_get(&edev->cdev); 53 + mutex_unlock(&container_list_lock); 54 + return edev; 55 + } 56 + } 57 + mutex_unlock(&container_list_lock); 58 + 59 + return NULL; 60 + } 61 + EXPORT_SYMBOL_GPL(enclosure_find); 62 + 63 + /** 64 + * enclosure_for_each_device - calls a function for each enclosure 65 + * @fn: the function to call 66 + * @data: the data to pass to each call 67 + * 68 + * Loops over all the enclosures calling the function. 69 + * 70 + * Note, this function uses a mutex which will be held across calls to 71 + * @fn, so it must have non atomic context, and @fn may (although it 72 + * should not) sleep or otherwise cause the mutex to be held for 73 + * indefinite periods 74 + */ 75 + int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), 76 + void *data) 77 + { 78 + int error = 0; 79 + struct enclosure_device *edev; 80 + 81 + mutex_lock(&container_list_lock); 82 + list_for_each_entry(edev, &container_list, node) { 83 + error = fn(edev, data); 84 + if (error) 85 + break; 86 + } 87 + mutex_unlock(&container_list_lock); 88 + 89 + return error; 90 + } 91 + EXPORT_SYMBOL_GPL(enclosure_for_each_device); 92 + 93 + /** 94 + * enclosure_register - register device as an enclosure 95 + * 96 + * @dev: device containing the enclosure 97 + * @components: number of components in the enclosure 98 + * 99 + * This sets up the device for being an enclosure. Note that @dev does 100 + * not have to be a dedicated enclosure device. It may be some other type 101 + * of device that additionally responds to enclosure services 102 + */ 103 + struct enclosure_device * 104 + enclosure_register(struct device *dev, const char *name, int components, 105 + struct enclosure_component_callbacks *cb) 106 + { 107 + struct enclosure_device *edev = 108 + kzalloc(sizeof(struct enclosure_device) + 109 + sizeof(struct enclosure_component)*components, 110 + GFP_KERNEL); 111 + int err, i; 112 + 113 + BUG_ON(!cb); 114 + 115 + if (!edev) 116 + return ERR_PTR(-ENOMEM); 117 + 118 + edev->components = components; 119 + 120 + edev->cdev.class = &enclosure_class; 121 + edev->cdev.dev = get_device(dev); 122 + edev->cb = cb; 123 + snprintf(edev->cdev.class_id, BUS_ID_SIZE, "%s", name); 124 + err = class_device_register(&edev->cdev); 125 + if (err) 126 + goto err; 127 + 128 + for (i = 0; i < components; i++) 129 + edev->component[i].number = -1; 130 + 131 + mutex_lock(&container_list_lock); 132 + list_add_tail(&edev->node, &container_list); 133 + mutex_unlock(&container_list_lock); 134 + 135 + return edev; 136 + 137 + err: 138 + put_device(edev->cdev.dev); 139 + kfree(edev); 140 + return ERR_PTR(err); 141 + } 142 + EXPORT_SYMBOL_GPL(enclosure_register); 143 + 144 + static struct enclosure_component_callbacks enclosure_null_callbacks; 145 + 146 + /** 147 + * enclosure_unregister - remove an enclosure 148 + * 149 + * @edev: the registered enclosure to remove; 150 + */ 151 + void enclosure_unregister(struct enclosure_device *edev) 152 + { 153 + int i; 154 + 155 + mutex_lock(&container_list_lock); 156 + list_del(&edev->node); 157 + mutex_unlock(&container_list_lock); 158 + 159 + for (i = 0; i < edev->components; i++) 160 + if (edev->component[i].number != -1) 161 + class_device_unregister(&edev->component[i].cdev); 162 + 163 + /* prevent any callbacks into service user */ 164 + edev->cb = &enclosure_null_callbacks; 165 + class_device_unregister(&edev->cdev); 166 + } 167 + EXPORT_SYMBOL_GPL(enclosure_unregister); 168 + 169 + static void enclosure_release(struct class_device *cdev) 170 + { 171 + struct enclosure_device *edev = to_enclosure_device(cdev); 172 + 173 + put_device(cdev->dev); 174 + kfree(edev); 175 + } 176 + 177 + static void enclosure_component_release(struct class_device *cdev) 178 + { 179 + if (cdev->dev) 180 + put_device(cdev->dev); 181 + class_device_put(cdev->parent); 182 + } 183 + 184 + /** 185 + * enclosure_component_register - add a particular component to an enclosure 186 + * @edev: the enclosure to add the component 187 + * @num: the device number 188 + * @type: the type of component being added 189 + * @name: an optional name to appear in sysfs (leave NULL if none) 190 + * 191 + * Registers the component. The name is optional for enclosures that 192 + * give their components a unique name. If not, leave the field NULL 193 + * and a name will be assigned. 194 + * 195 + * Returns a pointer to the enclosure component or an error. 196 + */ 197 + struct enclosure_component * 198 + enclosure_component_register(struct enclosure_device *edev, 199 + unsigned int number, 200 + enum enclosure_component_type type, 201 + const char *name) 202 + { 203 + struct enclosure_component *ecomp; 204 + struct class_device *cdev; 205 + int err; 206 + 207 + if (number >= edev->components) 208 + return ERR_PTR(-EINVAL); 209 + 210 + ecomp = &edev->component[number]; 211 + 212 + if (ecomp->number != -1) 213 + return ERR_PTR(-EINVAL); 214 + 215 + ecomp->type = type; 216 + ecomp->number = number; 217 + cdev = &ecomp->cdev; 218 + cdev->parent = class_device_get(&edev->cdev); 219 + cdev->class = &enclosure_component_class; 220 + if (name) 221 + snprintf(cdev->class_id, BUS_ID_SIZE, "%s", name); 222 + else 223 + snprintf(cdev->class_id, BUS_ID_SIZE, "%u", number); 224 + 225 + err = class_device_register(cdev); 226 + if (err) 227 + ERR_PTR(err); 228 + 229 + return ecomp; 230 + } 231 + EXPORT_SYMBOL_GPL(enclosure_component_register); 232 + 233 + /** 234 + * enclosure_add_device - add a device as being part of an enclosure 235 + * @edev: the enclosure device being added to. 236 + * @num: the number of the component 237 + * @dev: the device being added 238 + * 239 + * Declares a real device to reside in slot (or identifier) @num of an 240 + * enclosure. This will cause the relevant sysfs links to appear. 241 + * This function may also be used to change a device associated with 242 + * an enclosure without having to call enclosure_remove_device() in 243 + * between. 244 + * 245 + * Returns zero on success or an error. 246 + */ 247 + int enclosure_add_device(struct enclosure_device *edev, int component, 248 + struct device *dev) 249 + { 250 + struct class_device *cdev; 251 + 252 + if (!edev || component >= edev->components) 253 + return -EINVAL; 254 + 255 + cdev = &edev->component[component].cdev; 256 + 257 + class_device_del(cdev); 258 + if (cdev->dev) 259 + put_device(cdev->dev); 260 + cdev->dev = get_device(dev); 261 + return class_device_add(cdev); 262 + } 263 + EXPORT_SYMBOL_GPL(enclosure_add_device); 264 + 265 + /** 266 + * enclosure_remove_device - remove a device from an enclosure 267 + * @edev: the enclosure device 268 + * @num: the number of the component to remove 269 + * 270 + * Returns zero on success or an error. 271 + * 272 + */ 273 + int enclosure_remove_device(struct enclosure_device *edev, int component) 274 + { 275 + struct class_device *cdev; 276 + 277 + if (!edev || component >= edev->components) 278 + return -EINVAL; 279 + 280 + cdev = &edev->component[component].cdev; 281 + 282 + class_device_del(cdev); 283 + if (cdev->dev) 284 + put_device(cdev->dev); 285 + cdev->dev = NULL; 286 + return class_device_add(cdev); 287 + } 288 + EXPORT_SYMBOL_GPL(enclosure_remove_device); 289 + 290 + /* 291 + * sysfs pieces below 292 + */ 293 + 294 + static ssize_t enclosure_show_components(struct class_device *cdev, char *buf) 295 + { 296 + struct enclosure_device *edev = to_enclosure_device(cdev); 297 + 298 + return snprintf(buf, 40, "%d\n", edev->components); 299 + } 300 + 301 + static struct class_device_attribute enclosure_attrs[] = { 302 + __ATTR(components, S_IRUGO, enclosure_show_components, NULL), 303 + __ATTR_NULL 304 + }; 305 + 306 + static struct class enclosure_class = { 307 + .name = "enclosure", 308 + .owner = THIS_MODULE, 309 + .release = enclosure_release, 310 + .class_dev_attrs = enclosure_attrs, 311 + }; 312 + 313 + static const char *const enclosure_status [] = { 314 + [ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported", 315 + [ENCLOSURE_STATUS_OK] = "OK", 316 + [ENCLOSURE_STATUS_CRITICAL] = "critical", 317 + [ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical", 318 + [ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable", 319 + [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed", 320 + [ENCLOSURE_STATUS_UNKNOWN] = "unknown", 321 + [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable", 322 + }; 323 + 324 + static const char *const enclosure_type [] = { 325 + [ENCLOSURE_COMPONENT_DEVICE] = "device", 326 + [ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device", 327 + }; 328 + 329 + static ssize_t get_component_fault(struct class_device *cdev, char *buf) 330 + { 331 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 332 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 333 + 334 + if (edev->cb->get_fault) 335 + edev->cb->get_fault(edev, ecomp); 336 + return snprintf(buf, 40, "%d\n", ecomp->fault); 337 + } 338 + 339 + static ssize_t set_component_fault(struct class_device *cdev, const char *buf, 340 + size_t count) 341 + { 342 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 343 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 344 + int val = simple_strtoul(buf, NULL, 0); 345 + 346 + if (edev->cb->set_fault) 347 + edev->cb->set_fault(edev, ecomp, val); 348 + return count; 349 + } 350 + 351 + static ssize_t get_component_status(struct class_device *cdev, char *buf) 352 + { 353 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 354 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 355 + 356 + if (edev->cb->get_status) 357 + edev->cb->get_status(edev, ecomp); 358 + return snprintf(buf, 40, "%s\n", enclosure_status[ecomp->status]); 359 + } 360 + 361 + static ssize_t set_component_status(struct class_device *cdev, const char *buf, 362 + size_t count) 363 + { 364 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 365 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 366 + int i; 367 + 368 + for (i = 0; enclosure_status[i]; i++) { 369 + if (strncmp(buf, enclosure_status[i], 370 + strlen(enclosure_status[i])) == 0 && 371 + (buf[strlen(enclosure_status[i])] == '\n' || 372 + buf[strlen(enclosure_status[i])] == '\0')) 373 + break; 374 + } 375 + 376 + if (enclosure_status[i] && edev->cb->set_status) { 377 + edev->cb->set_status(edev, ecomp, i); 378 + return count; 379 + } else 380 + return -EINVAL; 381 + } 382 + 383 + static ssize_t get_component_active(struct class_device *cdev, char *buf) 384 + { 385 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 386 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 387 + 388 + if (edev->cb->get_active) 389 + edev->cb->get_active(edev, ecomp); 390 + return snprintf(buf, 40, "%d\n", ecomp->active); 391 + } 392 + 393 + static ssize_t set_component_active(struct class_device *cdev, const char *buf, 394 + size_t count) 395 + { 396 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 397 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 398 + int val = simple_strtoul(buf, NULL, 0); 399 + 400 + if (edev->cb->set_active) 401 + edev->cb->set_active(edev, ecomp, val); 402 + return count; 403 + } 404 + 405 + static ssize_t get_component_locate(struct class_device *cdev, char *buf) 406 + { 407 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 408 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 409 + 410 + if (edev->cb->get_locate) 411 + edev->cb->get_locate(edev, ecomp); 412 + return snprintf(buf, 40, "%d\n", ecomp->locate); 413 + } 414 + 415 + static ssize_t set_component_locate(struct class_device *cdev, const char *buf, 416 + size_t count) 417 + { 418 + struct enclosure_device *edev = to_enclosure_device(cdev->parent); 419 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 420 + int val = simple_strtoul(buf, NULL, 0); 421 + 422 + if (edev->cb->set_locate) 423 + edev->cb->set_locate(edev, ecomp, val); 424 + return count; 425 + } 426 + 427 + static ssize_t get_component_type(struct class_device *cdev, char *buf) 428 + { 429 + struct enclosure_component *ecomp = to_enclosure_component(cdev); 430 + 431 + return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]); 432 + } 433 + 434 + 435 + static struct class_device_attribute enclosure_component_attrs[] = { 436 + __ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, 437 + set_component_fault), 438 + __ATTR(status, S_IRUGO | S_IWUSR, get_component_status, 439 + set_component_status), 440 + __ATTR(active, S_IRUGO | S_IWUSR, get_component_active, 441 + set_component_active), 442 + __ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, 443 + set_component_locate), 444 + __ATTR(type, S_IRUGO, get_component_type, NULL), 445 + __ATTR_NULL 446 + }; 447 + 448 + static struct class enclosure_component_class = { 449 + .name = "enclosure_component", 450 + .owner = THIS_MODULE, 451 + .class_dev_attrs = enclosure_component_attrs, 452 + .release = enclosure_component_release, 453 + }; 454 + 455 + static int __init enclosure_init(void) 456 + { 457 + int err; 458 + 459 + err = class_register(&enclosure_class); 460 + if (err) 461 + return err; 462 + err = class_register(&enclosure_component_class); 463 + if (err) 464 + goto err_out; 465 + 466 + return 0; 467 + err_out: 468 + class_unregister(&enclosure_class); 469 + 470 + return err; 471 + } 472 + 473 + static void __exit enclosure_exit(void) 474 + { 475 + class_unregister(&enclosure_component_class); 476 + class_unregister(&enclosure_class); 477 + } 478 + 479 + module_init(enclosure_init); 480 + module_exit(enclosure_exit); 481 + 482 + MODULE_AUTHOR("James Bottomley"); 483 + MODULE_DESCRIPTION("Enclosure Services"); 484 + MODULE_LICENSE("GPL v2");
+129
include/linux/enclosure.h
··· 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 + #ifndef _LINUX_ENCLOSURE_H_ 24 + #define _LINUX_ENCLOSURE_H_ 25 + 26 + #include <linux/device.h> 27 + #include <linux/list.h> 28 + 29 + /* A few generic types ... taken from ses-2 */ 30 + enum enclosure_component_type { 31 + ENCLOSURE_COMPONENT_DEVICE = 0x01, 32 + ENCLOSURE_COMPONENT_ARRAY_DEVICE = 0x17, 33 + }; 34 + 35 + /* ses-2 common element status */ 36 + enum enclosure_status { 37 + ENCLOSURE_STATUS_UNSUPPORTED = 0, 38 + ENCLOSURE_STATUS_OK, 39 + ENCLOSURE_STATUS_CRITICAL, 40 + ENCLOSURE_STATUS_NON_CRITICAL, 41 + ENCLOSURE_STATUS_UNRECOVERABLE, 42 + ENCLOSURE_STATUS_NOT_INSTALLED, 43 + ENCLOSURE_STATUS_UNKNOWN, 44 + ENCLOSURE_STATUS_UNAVAILABLE, 45 + }; 46 + 47 + /* SFF-8485 activity light settings */ 48 + enum enclosure_component_setting { 49 + ENCLOSURE_SETTING_DISABLED = 0, 50 + ENCLOSURE_SETTING_ENABLED = 1, 51 + ENCLOSURE_SETTING_BLINK_A_ON_OFF = 2, 52 + ENCLOSURE_SETTING_BLINK_A_OFF_ON = 3, 53 + ENCLOSURE_SETTING_BLINK_B_ON_OFF = 6, 54 + ENCLOSURE_SETTING_BLINK_B_OFF_ON = 7, 55 + }; 56 + 57 + struct enclosure_device; 58 + struct enclosure_component; 59 + struct enclosure_component_callbacks { 60 + void (*get_status)(struct enclosure_device *, 61 + struct enclosure_component *); 62 + int (*set_status)(struct enclosure_device *, 63 + struct enclosure_component *, 64 + enum enclosure_status); 65 + void (*get_fault)(struct enclosure_device *, 66 + struct enclosure_component *); 67 + int (*set_fault)(struct enclosure_device *, 68 + struct enclosure_component *, 69 + enum enclosure_component_setting); 70 + void (*get_active)(struct enclosure_device *, 71 + struct enclosure_component *); 72 + int (*set_active)(struct enclosure_device *, 73 + struct enclosure_component *, 74 + enum enclosure_component_setting); 75 + void (*get_locate)(struct enclosure_device *, 76 + struct enclosure_component *); 77 + int (*set_locate)(struct enclosure_device *, 78 + struct enclosure_component *, 79 + enum enclosure_component_setting); 80 + }; 81 + 82 + 83 + struct enclosure_component { 84 + void *scratch; 85 + struct class_device cdev; 86 + enum enclosure_component_type type; 87 + int number; 88 + int fault; 89 + int active; 90 + int locate; 91 + enum enclosure_status status; 92 + }; 93 + 94 + struct enclosure_device { 95 + void *scratch; 96 + struct list_head node; 97 + struct class_device cdev; 98 + struct enclosure_component_callbacks *cb; 99 + int components; 100 + struct enclosure_component component[0]; 101 + }; 102 + 103 + static inline struct enclosure_device * 104 + to_enclosure_device(struct class_device *dev) 105 + { 106 + return container_of(dev, struct enclosure_device, cdev); 107 + } 108 + 109 + static inline struct enclosure_component * 110 + to_enclosure_component(struct class_device *dev) 111 + { 112 + return container_of(dev, struct enclosure_component, cdev); 113 + } 114 + 115 + struct enclosure_device * 116 + enclosure_register(struct device *, const char *, int, 117 + struct enclosure_component_callbacks *); 118 + void enclosure_unregister(struct enclosure_device *); 119 + struct enclosure_component * 120 + enclosure_component_register(struct enclosure_device *, unsigned int, 121 + enum enclosure_component_type, const char *); 122 + int enclosure_add_device(struct enclosure_device *enclosure, int component, 123 + struct device *dev); 124 + int enclosure_remove_device(struct enclosure_device *enclosure, int component); 125 + struct enclosure_device *enclosure_find(struct device *dev); 126 + int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *), 127 + void *data); 128 + 129 + #endif /* _LINUX_ENCLOSURE_H_ */