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