linux/drivers/scsi/ses.c
<<
>>
Prefs
   1/*
   2 * SCSI 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
  24#include <linux/module.h>
  25#include <linux/kernel.h>
  26#include <linux/enclosure.h>
  27
  28#include <scsi/scsi.h>
  29#include <scsi/scsi_cmnd.h>
  30#include <scsi/scsi_dbg.h>
  31#include <scsi/scsi_device.h>
  32#include <scsi/scsi_driver.h>
  33#include <scsi/scsi_host.h>
  34
  35struct ses_device {
  36        unsigned char *page1;
  37        unsigned char *page2;
  38        unsigned char *page10;
  39        short page1_len;
  40        short page2_len;
  41        short page10_len;
  42};
  43
  44struct ses_component {
  45        u64 addr;
  46        unsigned char *desc;
  47};
  48
  49static int ses_probe(struct device *dev)
  50{
  51        struct scsi_device *sdev = to_scsi_device(dev);
  52        int err = -ENODEV;
  53
  54        if (sdev->type != TYPE_ENCLOSURE)
  55                goto out;
  56
  57        err = 0;
  58        sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n");
  59
  60 out:
  61        return err;
  62}
  63
  64#define SES_TIMEOUT (30 * HZ)
  65#define SES_RETRIES 3
  66
  67static int ses_recv_diag(struct scsi_device *sdev, int page_code,
  68                         void *buf, int bufflen)
  69{
  70        unsigned char cmd[] = {
  71                RECEIVE_DIAGNOSTIC,
  72                1,              /* Set PCV bit */
  73                page_code,
  74                bufflen >> 8,
  75                bufflen & 0xff,
  76                0
  77        };
  78
  79        return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
  80                                NULL, SES_TIMEOUT, SES_RETRIES);
  81}
  82
  83static int ses_send_diag(struct scsi_device *sdev, int page_code,
  84                         void *buf, int bufflen)
  85{
  86        u32 result;
  87
  88        unsigned char cmd[] = {
  89                SEND_DIAGNOSTIC,
  90                0x10,           /* Set PF bit */
  91                0,
  92                bufflen >> 8,
  93                bufflen & 0xff,
  94                0
  95        };
  96
  97        result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen,
  98                                  NULL, SES_TIMEOUT, SES_RETRIES);
  99        if (result)
 100                sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
 101                            result);
 102        return result;
 103}
 104
 105static int ses_set_page2_descriptor(struct enclosure_device *edev,
 106                                      struct enclosure_component *ecomp,
 107                                      unsigned char *desc)
 108{
 109        int i, j, count = 0, descriptor = ecomp->number;
 110        struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
 111        struct ses_device *ses_dev = edev->scratch;
 112        unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
 113        unsigned char *desc_ptr = ses_dev->page2 + 8;
 114
 115        /* Clear everything */
 116        memset(desc_ptr, 0, ses_dev->page2_len - 8);
 117        for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) {
 118                for (j = 0; j < type_ptr[1]; j++) {
 119                        desc_ptr += 4;
 120                        if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
 121                            type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
 122                                continue;
 123                        if (count++ == descriptor) {
 124                                memcpy(desc_ptr, desc, 4);
 125                                /* set select */
 126                                desc_ptr[0] |= 0x80;
 127                                /* clear reserved, just in case */
 128                                desc_ptr[0] &= 0xf0;
 129                        }
 130                }
 131        }
 132
 133        return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
 134}
 135
 136static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev,
 137                                      struct enclosure_component *ecomp)
 138{
 139        int i, j, count = 0, descriptor = ecomp->number;
 140        struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
 141        struct ses_device *ses_dev = edev->scratch;
 142        unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
 143        unsigned char *desc_ptr = ses_dev->page2 + 8;
 144
 145        ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
 146
 147        for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) {
 148                for (j = 0; j < type_ptr[1]; j++) {
 149                        desc_ptr += 4;
 150                        if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
 151                            type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
 152                                continue;
 153                        if (count++ == descriptor)
 154                                return desc_ptr;
 155                }
 156        }
 157        return NULL;
 158}
 159
 160static void ses_get_fault(struct enclosure_device *edev,
 161                          struct enclosure_component *ecomp)
 162{
 163        unsigned char *desc;
 164
 165        desc = ses_get_page2_descriptor(edev, ecomp);
 166        if (desc)
 167                ecomp->fault = (desc[3] & 0x60) >> 4;
 168}
 169
 170static int ses_set_fault(struct enclosure_device *edev,
 171                          struct enclosure_component *ecomp,
 172                         enum enclosure_component_setting val)
 173{
 174        unsigned char desc[4] = {0 };
 175
 176        switch (val) {
 177        case ENCLOSURE_SETTING_DISABLED:
 178                /* zero is disabled */
 179                break;
 180        case ENCLOSURE_SETTING_ENABLED:
 181                desc[2] = 0x02;
 182                break;
 183        default:
 184                /* SES doesn't do the SGPIO blink settings */
 185                return -EINVAL;
 186        }
 187
 188        return ses_set_page2_descriptor(edev, ecomp, desc);
 189}
 190
 191static void ses_get_status(struct enclosure_device *edev,
 192                           struct enclosure_component *ecomp)
 193{
 194        unsigned char *desc;
 195
 196        desc = ses_get_page2_descriptor(edev, ecomp);
 197        if (desc)
 198                ecomp->status = (desc[0] & 0x0f);
 199}
 200
 201static void ses_get_locate(struct enclosure_device *edev,
 202                           struct enclosure_component *ecomp)
 203{
 204        unsigned char *desc;
 205
 206        desc = ses_get_page2_descriptor(edev, ecomp);
 207        if (desc)
 208                ecomp->locate = (desc[2] & 0x02) ? 1 : 0;
 209}
 210
 211static int ses_set_locate(struct enclosure_device *edev,
 212                          struct enclosure_component *ecomp,
 213                          enum enclosure_component_setting val)
 214{
 215        unsigned char desc[4] = {0 };
 216
 217        switch (val) {
 218        case ENCLOSURE_SETTING_DISABLED:
 219                /* zero is disabled */
 220                break;
 221        case ENCLOSURE_SETTING_ENABLED:
 222                desc[2] = 0x02;
 223                break;
 224        default:
 225                /* SES doesn't do the SGPIO blink settings */
 226                return -EINVAL;
 227        }
 228        return ses_set_page2_descriptor(edev, ecomp, desc);
 229}
 230
 231static int ses_set_active(struct enclosure_device *edev,
 232                          struct enclosure_component *ecomp,
 233                          enum enclosure_component_setting val)
 234{
 235        unsigned char desc[4] = {0 };
 236
 237        switch (val) {
 238        case ENCLOSURE_SETTING_DISABLED:
 239                /* zero is disabled */
 240                ecomp->active = 0;
 241                break;
 242        case ENCLOSURE_SETTING_ENABLED:
 243                desc[2] = 0x80;
 244                ecomp->active = 1;
 245                break;
 246        default:
 247                /* SES doesn't do the SGPIO blink settings */
 248                return -EINVAL;
 249        }
 250        return ses_set_page2_descriptor(edev, ecomp, desc);
 251}
 252
 253static struct enclosure_component_callbacks ses_enclosure_callbacks = {
 254        .get_fault              = ses_get_fault,
 255        .set_fault              = ses_set_fault,
 256        .get_status             = ses_get_status,
 257        .get_locate             = ses_get_locate,
 258        .set_locate             = ses_set_locate,
 259        .set_active             = ses_set_active,
 260};
 261
 262struct ses_host_edev {
 263        struct Scsi_Host *shost;
 264        struct enclosure_device *edev;
 265};
 266
 267int ses_match_host(struct enclosure_device *edev, void *data)
 268{
 269        struct ses_host_edev *sed = data;
 270        struct scsi_device *sdev;
 271
 272        if (!scsi_is_sdev_device(edev->edev.parent))
 273                return 0;
 274
 275        sdev = to_scsi_device(edev->edev.parent);
 276
 277        if (sdev->host != sed->shost)
 278                return 0;
 279
 280        sed->edev = edev;
 281        return 1;
 282}
 283
 284static void ses_process_descriptor(struct enclosure_component *ecomp,
 285                                   unsigned char *desc)
 286{
 287        int eip = desc[0] & 0x10;
 288        int invalid = desc[0] & 0x80;
 289        enum scsi_protocol proto = desc[0] & 0x0f;
 290        u64 addr = 0;
 291        struct ses_component *scomp = ecomp->scratch;
 292        unsigned char *d;
 293
 294        scomp->desc = desc;
 295
 296        if (invalid)
 297                return;
 298
 299        switch (proto) {
 300        case SCSI_PROTOCOL_SAS:
 301                if (eip)
 302                        d = desc + 8;
 303                else
 304                        d = desc + 4;
 305                /* only take the phy0 addr */
 306                addr = (u64)d[12] << 56 |
 307                        (u64)d[13] << 48 |
 308                        (u64)d[14] << 40 |
 309                        (u64)d[15] << 32 |
 310                        (u64)d[16] << 24 |
 311                        (u64)d[17] << 16 |
 312                        (u64)d[18] << 8 |
 313                        (u64)d[19];
 314                break;
 315        default:
 316                /* FIXME: Need to add more protocols than just SAS */
 317                break;
 318        }
 319        scomp->addr = addr;
 320}
 321
 322struct efd {
 323        u64 addr;
 324        struct device *dev;
 325};
 326
 327static int ses_enclosure_find_by_addr(struct enclosure_device *edev,
 328                                      void *data)
 329{
 330        struct efd *efd = data;
 331        int i;
 332        struct ses_component *scomp;
 333
 334        if (!edev->component[0].scratch)
 335                return 0;
 336
 337        for (i = 0; i < edev->components; i++) {
 338                scomp = edev->component[i].scratch;
 339                if (scomp->addr != efd->addr)
 340                        continue;
 341
 342                enclosure_add_device(edev, i, efd->dev);
 343                return 1;
 344        }
 345        return 0;
 346}
 347
 348#define VPD_INQUIRY_SIZE 36
 349
 350static void ses_match_to_enclosure(struct enclosure_device *edev,
 351                                   struct scsi_device *sdev)
 352{
 353        unsigned char *buf = kmalloc(VPD_INQUIRY_SIZE, GFP_KERNEL);
 354        unsigned char *desc;
 355        u16 vpd_len;
 356        struct efd efd = {
 357                .addr = 0,
 358        };
 359        unsigned char cmd[] = {
 360                INQUIRY,
 361                1,
 362                0x83,
 363                VPD_INQUIRY_SIZE >> 8,
 364                VPD_INQUIRY_SIZE & 0xff,
 365                0
 366        };
 367
 368        if (!buf)
 369                return;
 370
 371        if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
 372                             VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES))
 373                goto free;
 374
 375        vpd_len = (buf[2] << 8) + buf[3];
 376        kfree(buf);
 377        buf = kmalloc(vpd_len, GFP_KERNEL);
 378        if (!buf)
 379                return;
 380        cmd[3] = vpd_len >> 8;
 381        cmd[4] = vpd_len & 0xff;
 382        if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
 383                             vpd_len, NULL, SES_TIMEOUT, SES_RETRIES))
 384                goto free;
 385
 386        desc = buf + 4;
 387        while (desc < buf + vpd_len) {
 388                enum scsi_protocol proto = desc[0] >> 4;
 389                u8 code_set = desc[0] & 0x0f;
 390                u8 piv = desc[1] & 0x80;
 391                u8 assoc = (desc[1] & 0x30) >> 4;
 392                u8 type = desc[1] & 0x0f;
 393                u8 len = desc[3];
 394
 395                if (piv && code_set == 1 && assoc == 1 && code_set == 1
 396                    && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8)
 397                        efd.addr = (u64)desc[4] << 56 |
 398                                (u64)desc[5] << 48 |
 399                                (u64)desc[6] << 40 |
 400                                (u64)desc[7] << 32 |
 401                                (u64)desc[8] << 24 |
 402                                (u64)desc[9] << 16 |
 403                                (u64)desc[10] << 8 |
 404                                (u64)desc[11];
 405
 406                desc += len + 4;
 407        }
 408        if (!efd.addr)
 409                goto free;
 410
 411        efd.dev = &sdev->sdev_gendev;
 412
 413        enclosure_for_each_device(ses_enclosure_find_by_addr, &efd);
 414 free:
 415        kfree(buf);
 416}
 417
 418#define INIT_ALLOC_SIZE 32
 419
 420static int ses_intf_add(struct device *cdev,
 421                        struct class_interface *intf)
 422{
 423        struct scsi_device *sdev = to_scsi_device(cdev->parent);
 424        struct scsi_device *tmp_sdev;
 425        unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL,
 426                *addl_desc_ptr = NULL;
 427        struct ses_device *ses_dev;
 428        u32 result;
 429        int i, j, types, len, page7_len = 0, components = 0;
 430        int err = -ENOMEM;
 431        struct enclosure_device *edev;
 432        struct ses_component *scomp = NULL;
 433
 434        if (!scsi_device_enclosure(sdev)) {
 435                /* not an enclosure, but might be in one */
 436                edev = enclosure_find(&sdev->host->shost_gendev);
 437                if (edev) {
 438                        ses_match_to_enclosure(edev, sdev);
 439                        put_device(&edev->edev);
 440                }
 441                return -ENODEV;
 442        }
 443
 444        /* TYPE_ENCLOSURE prints a message in probe */
 445        if (sdev->type != TYPE_ENCLOSURE)
 446                sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n");
 447
 448        ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL);
 449        hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
 450        if (!hdr_buf || !ses_dev)
 451                goto err_init_free;
 452
 453        result = ses_recv_diag(sdev, 1, hdr_buf, INIT_ALLOC_SIZE);
 454        if (result)
 455                goto recv_failed;
 456
 457        if (hdr_buf[1] != 0) {
 458                /* FIXME: need subenclosure support; I've just never
 459                 * seen a device with subenclosures and it makes the
 460                 * traversal routines more complex */
 461                sdev_printk(KERN_ERR, sdev,
 462                        "FIXME driver has no support for subenclosures (%d)\n",
 463                        hdr_buf[1]);
 464                goto err_free;
 465        }
 466
 467        len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
 468        buf = kzalloc(len, GFP_KERNEL);
 469        if (!buf)
 470                goto err_free;
 471
 472        result = ses_recv_diag(sdev, 1, buf, len);
 473        if (result)
 474                goto recv_failed;
 475
 476        types = buf[10];
 477
 478        type_ptr = buf + 12 + buf[11];
 479
 480        for (i = 0; i < types; i++, type_ptr += 4) {
 481                if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
 482                    type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE)
 483                        components += type_ptr[1];
 484        }
 485        ses_dev->page1 = buf;
 486        ses_dev->page1_len = len;
 487        buf = NULL;
 488
 489        result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE);
 490        if (result)
 491                goto recv_failed;
 492
 493        len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
 494        buf = kzalloc(len, GFP_KERNEL);
 495        if (!buf)
 496                goto err_free;
 497
 498        /* make sure getting page 2 actually works */
 499        result = ses_recv_diag(sdev, 2, buf, len);
 500        if (result)
 501                goto recv_failed;
 502        ses_dev->page2 = buf;
 503        ses_dev->page2_len = len;
 504        buf = NULL;
 505
 506        /* The additional information page --- allows us
 507         * to match up the devices */
 508        result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE);
 509        if (!result) {
 510
 511                len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
 512                buf = kzalloc(len, GFP_KERNEL);
 513                if (!buf)
 514                        goto err_free;
 515
 516                result = ses_recv_diag(sdev, 10, buf, len);
 517                if (result)
 518                        goto recv_failed;
 519                ses_dev->page10 = buf;
 520                ses_dev->page10_len = len;
 521                buf = NULL;
 522        }
 523
 524        scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL);
 525        if (!scomp)
 526                goto err_free;
 527
 528        edev = enclosure_register(cdev->parent, sdev->sdev_gendev.bus_id,
 529                                  components, &ses_enclosure_callbacks);
 530        if (IS_ERR(edev)) {
 531                err = PTR_ERR(edev);
 532                goto err_free;
 533        }
 534
 535        edev->scratch = ses_dev;
 536        for (i = 0; i < components; i++)
 537                edev->component[i].scratch = scomp + i;
 538
 539        /* Page 7 for the descriptors is optional */
 540        result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
 541        if (result)
 542                goto simple_populate;
 543
 544        page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
 545        /* add 1 for trailing '\0' we'll use */
 546        buf = kzalloc(len + 1, GFP_KERNEL);
 547        if (!buf)
 548                goto simple_populate;
 549        result = ses_recv_diag(sdev, 7, buf, len);
 550        if (result) {
 551 simple_populate:
 552                kfree(buf);
 553                buf = NULL;
 554                desc_ptr = NULL;
 555                addl_desc_ptr = NULL;
 556        } else {
 557                desc_ptr = buf + 8;
 558                len = (desc_ptr[2] << 8) + desc_ptr[3];
 559                /* skip past overall descriptor */
 560                desc_ptr += len + 4;
 561                if (ses_dev->page10)
 562                        addl_desc_ptr = ses_dev->page10 + 8;
 563        }
 564        type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
 565        components = 0;
 566        for (i = 0; i < types; i++, type_ptr += 4) {
 567                for (j = 0; j < type_ptr[1]; j++) {
 568                        char *name = NULL;
 569                        struct enclosure_component *ecomp;
 570
 571                        if (desc_ptr) {
 572                                if (desc_ptr >= buf + page7_len) {
 573                                        desc_ptr = NULL;
 574                                } else {
 575                                        len = (desc_ptr[2] << 8) + desc_ptr[3];
 576                                        desc_ptr += 4;
 577                                        /* Add trailing zero - pushes into
 578                                         * reserved space */
 579                                        desc_ptr[len] = '\0';
 580                                        name = desc_ptr;
 581                                }
 582                        }
 583                        if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
 584                            type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
 585
 586                                ecomp = enclosure_component_register(edev,
 587                                                             components++,
 588                                                             type_ptr[0],
 589                                                             name);
 590
 591                                if (!IS_ERR(ecomp) && addl_desc_ptr)
 592                                        ses_process_descriptor(ecomp,
 593                                                               addl_desc_ptr);
 594                        }
 595                        if (desc_ptr)
 596                                desc_ptr += len;
 597
 598                        if (addl_desc_ptr)
 599                                addl_desc_ptr += addl_desc_ptr[1] + 2;
 600
 601                }
 602        }
 603        kfree(buf);
 604        kfree(hdr_buf);
 605
 606        /* see if there are any devices matching before
 607         * we found the enclosure */
 608        shost_for_each_device(tmp_sdev, sdev->host) {
 609                if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
 610                        continue;
 611                ses_match_to_enclosure(edev, tmp_sdev);
 612        }
 613
 614        return 0;
 615
 616 recv_failed:
 617        sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n",
 618                    result);
 619        err = -ENODEV;
 620 err_free:
 621        kfree(buf);
 622        kfree(scomp);
 623        kfree(ses_dev->page10);
 624        kfree(ses_dev->page2);
 625        kfree(ses_dev->page1);
 626 err_init_free:
 627        kfree(ses_dev);
 628        kfree(hdr_buf);
 629        sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err);
 630        return err;
 631}
 632
 633static int ses_remove(struct device *dev)
 634{
 635        return 0;
 636}
 637
 638static void ses_intf_remove(struct device *cdev,
 639                            struct class_interface *intf)
 640{
 641        struct scsi_device *sdev = to_scsi_device(cdev->parent);
 642        struct enclosure_device *edev;
 643        struct ses_device *ses_dev;
 644
 645        if (!scsi_device_enclosure(sdev))
 646                return;
 647
 648        edev = enclosure_find(cdev->parent);
 649        if (!edev)
 650                return;
 651
 652        ses_dev = edev->scratch;
 653        edev->scratch = NULL;
 654
 655        kfree(ses_dev->page10);
 656        kfree(ses_dev->page1);
 657        kfree(ses_dev->page2);
 658        kfree(ses_dev);
 659
 660        kfree(edev->component[0].scratch);
 661
 662        put_device(&edev->edev);
 663        enclosure_unregister(edev);
 664}
 665
 666static struct class_interface ses_interface = {
 667        .add_dev        = ses_intf_add,
 668        .remove_dev     = ses_intf_remove,
 669};
 670
 671static struct scsi_driver ses_template = {
 672        .owner                  = THIS_MODULE,
 673        .gendrv = {
 674                .name           = "ses",
 675                .probe          = ses_probe,
 676                .remove         = ses_remove,
 677        },
 678};
 679
 680static int __init ses_init(void)
 681{
 682        int err;
 683
 684        err = scsi_register_interface(&ses_interface);
 685        if (err)
 686                return err;
 687
 688        err = scsi_register_driver(&ses_template.gendrv);
 689        if (err)
 690                goto out_unreg;
 691
 692        return 0;
 693
 694 out_unreg:
 695        scsi_unregister_interface(&ses_interface);
 696        return err;
 697}
 698
 699static void __exit ses_exit(void)
 700{
 701        scsi_unregister_driver(&ses_template.gendrv);
 702        scsi_unregister_interface(&ses_interface);
 703}
 704
 705module_init(ses_init);
 706module_exit(ses_exit);
 707
 708MODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE);
 709
 710MODULE_AUTHOR("James Bottomley");
 711MODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver");
 712MODULE_LICENSE("GPL v2");
 713
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.