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