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#include <linux/slab.h>
  31
  32static LIST_HEAD(container_list);
  33static DEFINE_MUTEX(container_list_lock);
  34static 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 */
  54struct 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}
  81EXPORT_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 */
  95int 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}
 111EXPORT_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 */
 123struct enclosure_device *
 124enclosure_register(struct device *dev, const char *name, int components,
 125                   struct enclosure_component_callbacks *cb)
 126{
 127        struct enclosure_device *edev =
 128                kzalloc(struct_size(edev, component, components), GFP_KERNEL);
 129        int err, i;
 130
 131        BUG_ON(!cb);
 132
 133        if (!edev)
 134                return ERR_PTR(-ENOMEM);
 135
 136        edev->components = components;
 137
 138        edev->edev.class = &enclosure_class;
 139        edev->edev.parent = get_device(dev);
 140        edev->cb = cb;
 141        dev_set_name(&edev->edev, "%s", name);
 142        err = device_register(&edev->edev);
 143        if (err)
 144                goto err;
 145
 146        for (i = 0; i < components; i++) {
 147                edev->component[i].number = -1;
 148                edev->component[i].slot = -1;
 149                edev->component[i].power_status = -1;
 150        }
 151
 152        mutex_lock(&container_list_lock);
 153        list_add_tail(&edev->node, &container_list);
 154        mutex_unlock(&container_list_lock);
 155
 156        return edev;
 157
 158 err:
 159        put_device(edev->edev.parent);
 160        kfree(edev);
 161        return ERR_PTR(err);
 162}
 163EXPORT_SYMBOL_GPL(enclosure_register);
 164
 165static struct enclosure_component_callbacks enclosure_null_callbacks;
 166
 167/**
 168 * enclosure_unregister - remove an enclosure
 169 *
 170 * @edev:       the registered enclosure to remove;
 171 */
 172void enclosure_unregister(struct enclosure_device *edev)
 173{
 174        int i;
 175
 176        mutex_lock(&container_list_lock);
 177        list_del(&edev->node);
 178        mutex_unlock(&container_list_lock);
 179
 180        for (i = 0; i < edev->components; i++)
 181                if (edev->component[i].number != -1)
 182                        device_unregister(&edev->component[i].cdev);
 183
 184        /* prevent any callbacks into service user */
 185        edev->cb = &enclosure_null_callbacks;
 186        device_unregister(&edev->edev);
 187}
 188EXPORT_SYMBOL_GPL(enclosure_unregister);
 189
 190#define ENCLOSURE_NAME_SIZE     64
 191#define COMPONENT_NAME_SIZE     64
 192
 193static void enclosure_link_name(struct enclosure_component *cdev, char *name)
 194{
 195        strcpy(name, "enclosure_device:");
 196        strcat(name, dev_name(&cdev->cdev));
 197}
 198
 199static void enclosure_remove_links(struct enclosure_component *cdev)
 200{
 201        char name[ENCLOSURE_NAME_SIZE];
 202
 203        enclosure_link_name(cdev, name);
 204
 205        /*
 206         * In odd circumstances, like multipath devices, something else may
 207         * already have removed the links, so check for this condition first.
 208         */
 209        if (cdev->dev->kobj.sd)
 210                sysfs_remove_link(&cdev->dev->kobj, name);
 211
 212        if (cdev->cdev.kobj.sd)
 213                sysfs_remove_link(&cdev->cdev.kobj, "device");
 214}
 215
 216static int enclosure_add_links(struct enclosure_component *cdev)
 217{
 218        int error;
 219        char name[ENCLOSURE_NAME_SIZE];
 220
 221        error = sysfs_create_link(&cdev->cdev.kobj, &cdev->dev->kobj, "device");
 222        if (error)
 223                return error;
 224
 225        enclosure_link_name(cdev, name);
 226        error = sysfs_create_link(&cdev->dev->kobj, &cdev->cdev.kobj, name);
 227        if (error)
 228                sysfs_remove_link(&cdev->cdev.kobj, "device");
 229
 230        return error;
 231}
 232
 233static void enclosure_release(struct device *cdev)
 234{
 235        struct enclosure_device *edev = to_enclosure_device(cdev);
 236
 237        put_device(cdev->parent);
 238        kfree(edev);
 239}
 240
 241static void enclosure_component_release(struct device *dev)
 242{
 243        struct enclosure_component *cdev = to_enclosure_component(dev);
 244
 245        if (cdev->dev) {
 246                enclosure_remove_links(cdev);
 247                put_device(cdev->dev);
 248        }
 249        put_device(dev->parent);
 250}
 251
 252static struct enclosure_component *
 253enclosure_component_find_by_name(struct enclosure_device *edev,
 254                                const char *name)
 255{
 256        int i;
 257        const char *cname;
 258        struct enclosure_component *ecomp;
 259
 260        if (!edev || !name || !name[0])
 261                return NULL;
 262
 263        for (i = 0; i < edev->components; i++) {
 264                ecomp = &edev->component[i];
 265                cname = dev_name(&ecomp->cdev);
 266                if (ecomp->number != -1 &&
 267                    cname && cname[0] &&
 268                    !strcmp(cname, name))
 269                        return ecomp;
 270        }
 271
 272        return NULL;
 273}
 274
 275static const struct attribute_group *enclosure_component_groups[];
 276
 277/**
 278 * enclosure_component_alloc - prepare a new enclosure component
 279 * @edev:       the enclosure to add the component
 280 * @num:        the device number
 281 * @type:       the type of component being added
 282 * @name:       an optional name to appear in sysfs (leave NULL if none)
 283 *
 284 * The name is optional for enclosures that give their components a unique
 285 * name.  If not, leave the field NULL and a name will be assigned.
 286 *
 287 * Returns a pointer to the enclosure component or an error.
 288 */
 289struct enclosure_component *
 290enclosure_component_alloc(struct enclosure_device *edev,
 291                          unsigned int number,
 292                          enum enclosure_component_type type,
 293                          const char *name)
 294{
 295        struct enclosure_component *ecomp;
 296        struct device *cdev;
 297        int i;
 298        char newname[COMPONENT_NAME_SIZE];
 299
 300        if (number >= edev->components)
 301                return ERR_PTR(-EINVAL);
 302
 303        ecomp = &edev->component[number];
 304
 305        if (ecomp->number != -1)
 306                return ERR_PTR(-EINVAL);
 307
 308        ecomp->type = type;
 309        ecomp->number = number;
 310        cdev = &ecomp->cdev;
 311        cdev->parent = get_device(&edev->edev);
 312
 313        if (name && name[0]) {
 314                /* Some hardware (e.g. enclosure in RX300 S6) has components
 315                 * with non unique names. Registering duplicates in sysfs
 316                 * will lead to warnings during bootup. So make the names
 317                 * unique by appending consecutive numbers -1, -2, ... */
 318                i = 1;
 319                snprintf(newname, COMPONENT_NAME_SIZE,
 320                         "%s", name);
 321                while (enclosure_component_find_by_name(edev, newname))
 322                        snprintf(newname, COMPONENT_NAME_SIZE,
 323                                 "%s-%i", name, i++);
 324                dev_set_name(cdev, "%s", newname);
 325        } else
 326                dev_set_name(cdev, "%u", number);
 327
 328        cdev->release = enclosure_component_release;
 329        cdev->groups = enclosure_component_groups;
 330
 331        return ecomp;
 332}
 333EXPORT_SYMBOL_GPL(enclosure_component_alloc);
 334
 335/**
 336 * enclosure_component_register - publishes an initialized enclosure component
 337 * @ecomp:      component to add
 338 *
 339 * Returns 0 on successful registration, releases the component otherwise
 340 */
 341int enclosure_component_register(struct enclosure_component *ecomp)
 342{
 343        struct device *cdev;
 344        int err;
 345
 346        cdev = &ecomp->cdev;
 347        err = device_register(cdev);
 348        if (err) {
 349                ecomp->number = -1;
 350                put_device(cdev);
 351                return err;
 352        }
 353
 354        return 0;
 355}
 356EXPORT_SYMBOL_GPL(enclosure_component_register);
 357
 358/**
 359 * enclosure_add_device - add a device as being part of an enclosure
 360 * @edev:       the enclosure device being added to.
 361 * @num:        the number of the component
 362 * @dev:        the device being added
 363 *
 364 * Declares a real device to reside in slot (or identifier) @num of an
 365 * enclosure.  This will cause the relevant sysfs links to appear.
 366 * This function may also be used to change a device associated with
 367 * an enclosure without having to call enclosure_remove_device() in
 368 * between.
 369 *
 370 * Returns zero on success or an error.
 371 */
 372int enclosure_add_device(struct enclosure_device *edev, int component,
 373                         struct device *dev)
 374{
 375        struct enclosure_component *cdev;
 376        int err;
 377
 378        if (!edev || component >= edev->components)
 379                return -EINVAL;
 380
 381        cdev = &edev->component[component];
 382
 383        if (cdev->dev == dev)
 384                return -EEXIST;
 385
 386        if (cdev->dev) {
 387                enclosure_remove_links(cdev);
 388                put_device(cdev->dev);
 389        }
 390        cdev->dev = get_device(dev);
 391        err = enclosure_add_links(cdev);
 392        if (err) {
 393                put_device(cdev->dev);
 394                cdev->dev = NULL;
 395        }
 396        return err;
 397}
 398EXPORT_SYMBOL_GPL(enclosure_add_device);
 399
 400/**
 401 * enclosure_remove_device - remove a device from an enclosure
 402 * @edev:       the enclosure device
 403 * @num:        the number of the component to remove
 404 *
 405 * Returns zero on success or an error.
 406 *
 407 */
 408int enclosure_remove_device(struct enclosure_device *edev, struct device *dev)
 409{
 410        struct enclosure_component *cdev;
 411        int i;
 412
 413        if (!edev || !dev)
 414                return -EINVAL;
 415
 416        for (i = 0; i < edev->components; i++) {
 417                cdev = &edev->component[i];
 418                if (cdev->dev == dev) {
 419                        enclosure_remove_links(cdev);
 420                        device_del(&cdev->cdev);
 421                        put_device(dev);
 422                        cdev->dev = NULL;
 423                        return device_add(&cdev->cdev);
 424                }
 425        }
 426        return -ENODEV;
 427}
 428EXPORT_SYMBOL_GPL(enclosure_remove_device);
 429
 430/*
 431 * sysfs pieces below
 432 */
 433
 434static ssize_t components_show(struct device *cdev,
 435                               struct device_attribute *attr, char *buf)
 436{
 437        struct enclosure_device *edev = to_enclosure_device(cdev);
 438
 439        return snprintf(buf, 40, "%d\n", edev->components);
 440}
 441static DEVICE_ATTR_RO(components);
 442
 443static ssize_t id_show(struct device *cdev,
 444                                 struct device_attribute *attr,
 445                                 char *buf)
 446{
 447        struct enclosure_device *edev = to_enclosure_device(cdev);
 448
 449        if (edev->cb->show_id)
 450                return edev->cb->show_id(edev, buf);
 451        return -EINVAL;
 452}
 453static DEVICE_ATTR_RO(id);
 454
 455static struct attribute *enclosure_class_attrs[] = {
 456        &dev_attr_components.attr,
 457        &dev_attr_id.attr,
 458        NULL,
 459};
 460ATTRIBUTE_GROUPS(enclosure_class);
 461
 462static struct class enclosure_class = {
 463        .name                   = "enclosure",
 464        .owner                  = THIS_MODULE,
 465        .dev_release            = enclosure_release,
 466        .dev_groups             = enclosure_class_groups,
 467};
 468
 469static const char *const enclosure_status[] = {
 470        [ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported",
 471        [ENCLOSURE_STATUS_OK] = "OK",
 472        [ENCLOSURE_STATUS_CRITICAL] = "critical",
 473        [ENCLOSURE_STATUS_NON_CRITICAL] = "non-critical",
 474        [ENCLOSURE_STATUS_UNRECOVERABLE] = "unrecoverable",
 475        [ENCLOSURE_STATUS_NOT_INSTALLED] = "not installed",
 476        [ENCLOSURE_STATUS_UNKNOWN] = "unknown",
 477        [ENCLOSURE_STATUS_UNAVAILABLE] = "unavailable",
 478        [ENCLOSURE_STATUS_MAX] = NULL,
 479};
 480
 481static const char *const enclosure_type[] = {
 482        [ENCLOSURE_COMPONENT_DEVICE] = "device",
 483        [ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device",
 484};
 485
 486static ssize_t get_component_fault(struct device *cdev,
 487                                   struct device_attribute *attr, char *buf)
 488{
 489        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 490        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 491
 492        if (edev->cb->get_fault)
 493                edev->cb->get_fault(edev, ecomp);
 494        return snprintf(buf, 40, "%d\n", ecomp->fault);
 495}
 496
 497static ssize_t set_component_fault(struct device *cdev,
 498                                   struct device_attribute *attr,
 499                                   const char *buf, size_t count)
 500{
 501        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 502        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 503        int val = simple_strtoul(buf, NULL, 0);
 504
 505        if (edev->cb->set_fault)
 506                edev->cb->set_fault(edev, ecomp, val);
 507        return count;
 508}
 509
 510static ssize_t get_component_status(struct device *cdev,
 511                                    struct device_attribute *attr,char *buf)
 512{
 513        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 514        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 515
 516        if (edev->cb->get_status)
 517                edev->cb->get_status(edev, ecomp);
 518        return snprintf(buf, 40, "%s\n", enclosure_status[ecomp->status]);
 519}
 520
 521static ssize_t set_component_status(struct device *cdev,
 522                                    struct device_attribute *attr,
 523                                    const char *buf, size_t count)
 524{
 525        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 526        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 527        int i;
 528
 529        for (i = 0; enclosure_status[i]; i++) {
 530                if (strncmp(buf, enclosure_status[i],
 531                            strlen(enclosure_status[i])) == 0 &&
 532                    (buf[strlen(enclosure_status[i])] == '\n' ||
 533                     buf[strlen(enclosure_status[i])] == '\0'))
 534                        break;
 535        }
 536
 537        if (enclosure_status[i] && edev->cb->set_status) {
 538                edev->cb->set_status(edev, ecomp, i);
 539                return count;
 540        } else
 541                return -EINVAL;
 542}
 543
 544static ssize_t get_component_active(struct device *cdev,
 545                                    struct device_attribute *attr, char *buf)
 546{
 547        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 548        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 549
 550        if (edev->cb->get_active)
 551                edev->cb->get_active(edev, ecomp);
 552        return snprintf(buf, 40, "%d\n", ecomp->active);
 553}
 554
 555static ssize_t set_component_active(struct device *cdev,
 556                                    struct device_attribute *attr,
 557                                    const char *buf, size_t count)
 558{
 559        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 560        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 561        int val = simple_strtoul(buf, NULL, 0);
 562
 563        if (edev->cb->set_active)
 564                edev->cb->set_active(edev, ecomp, val);
 565        return count;
 566}
 567
 568static ssize_t get_component_locate(struct device *cdev,
 569                                    struct device_attribute *attr, char *buf)
 570{
 571        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 572        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 573
 574        if (edev->cb->get_locate)
 575                edev->cb->get_locate(edev, ecomp);
 576        return snprintf(buf, 40, "%d\n", ecomp->locate);
 577}
 578
 579static ssize_t set_component_locate(struct device *cdev,
 580                                    struct device_attribute *attr,
 581                                    const char *buf, size_t count)
 582{
 583        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 584        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 585        int val = simple_strtoul(buf, NULL, 0);
 586
 587        if (edev->cb->set_locate)
 588                edev->cb->set_locate(edev, ecomp, val);
 589        return count;
 590}
 591
 592static ssize_t get_component_power_status(struct device *cdev,
 593                                          struct device_attribute *attr,
 594                                          char *buf)
 595{
 596        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 597        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 598
 599        if (edev->cb->get_power_status)
 600                edev->cb->get_power_status(edev, ecomp);
 601
 602        /* If still uninitialized, the callback failed or does not exist. */
 603        if (ecomp->power_status == -1)
 604                return (edev->cb->get_power_status) ? -EIO : -ENOTTY;
 605
 606        return snprintf(buf, 40, "%s\n", ecomp->power_status ? "on" : "off");
 607}
 608
 609static ssize_t set_component_power_status(struct device *cdev,
 610                                          struct device_attribute *attr,
 611                                          const char *buf, size_t count)
 612{
 613        struct enclosure_device *edev = to_enclosure_device(cdev->parent);
 614        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 615        int val;
 616
 617        if (strncmp(buf, "on", 2) == 0 &&
 618            (buf[2] == '\n' || buf[2] == '\0'))
 619                val = 1;
 620        else if (strncmp(buf, "off", 3) == 0 &&
 621            (buf[3] == '\n' || buf[3] == '\0'))
 622                val = 0;
 623        else
 624                return -EINVAL;
 625
 626        if (edev->cb->set_power_status)
 627                edev->cb->set_power_status(edev, ecomp, val);
 628        return count;
 629}
 630
 631static ssize_t get_component_type(struct device *cdev,
 632                                  struct device_attribute *attr, char *buf)
 633{
 634        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 635
 636        return snprintf(buf, 40, "%s\n", enclosure_type[ecomp->type]);
 637}
 638
 639static ssize_t get_component_slot(struct device *cdev,
 640                                  struct device_attribute *attr, char *buf)
 641{
 642        struct enclosure_component *ecomp = to_enclosure_component(cdev);
 643        int slot;
 644
 645        /* if the enclosure does not override then use 'number' as a stand-in */
 646        if (ecomp->slot >= 0)
 647                slot = ecomp->slot;
 648        else
 649                slot = ecomp->number;
 650
 651        return snprintf(buf, 40, "%d\n", slot);
 652}
 653
 654static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault,
 655                    set_component_fault);
 656static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, get_component_status,
 657                   set_component_status);
 658static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active,
 659                   set_component_active);
 660static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate,
 661                   set_component_locate);
 662static DEVICE_ATTR(power_status, S_IRUGO | S_IWUSR, get_component_power_status,
 663                   set_component_power_status);
 664static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL);
 665static DEVICE_ATTR(slot, S_IRUGO, get_component_slot, NULL);
 666
 667static struct attribute *enclosure_component_attrs[] = {
 668        &dev_attr_fault.attr,
 669        &dev_attr_status.attr,
 670        &dev_attr_active.attr,
 671        &dev_attr_locate.attr,
 672        &dev_attr_power_status.attr,
 673        &dev_attr_type.attr,
 674        &dev_attr_slot.attr,
 675        NULL
 676};
 677ATTRIBUTE_GROUPS(enclosure_component);
 678
 679static int __init enclosure_init(void)
 680{
 681        return class_register(&enclosure_class);
 682}
 683
 684static void __exit enclosure_exit(void)
 685{
 686        class_unregister(&enclosure_class);
 687}
 688
 689module_init(enclosure_init);
 690module_exit(enclosure_exit);
 691
 692MODULE_AUTHOR("James Bottomley");
 693MODULE_DESCRIPTION("Enclosure Services");
 694MODULE_LICENSE("GPL v2");
 695