scsi-generic.c 17.1 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*
 * Generic SCSI Device support
 *
 * Copyright (c) 2007 Bull S.A.S.
 * Based on code by Paul Brook
 * Based on code by Fabrice Bellard
 *
 * Written by Laurent Vivier <Laurent.Vivier@bull.net>
 *
M
Matthew Fernandez 已提交
10
 * This code is licensed under the LGPL.
11 12 13
 *
 */

P
Peter Maydell 已提交
14
#include "qemu/osdep.h"
15
#include "qapi/error.h"
16
#include "qemu-common.h"
17
#include "qemu/error-report.h"
P
Paolo Bonzini 已提交
18
#include "hw/scsi/scsi.h"
19
#include "sysemu/block-backend.h"
20
#include "sysemu/blockdev.h"
21

22
#ifdef __linux__
23 24 25 26

//#define DEBUG_SCSI

#ifdef DEBUG_SCSI
27 28
#define DPRINTF(fmt, ...) \
do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
29
#else
30
#define DPRINTF(fmt, ...) do {} while(0)
31 32
#endif

33 34
#define BADF(fmt, ...) \
do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
35 36

#include <scsi/sg.h>
P
Paolo Bonzini 已提交
37
#include "block/scsi.h"
38 39 40 41 42

#ifndef MAX_UINT
#define MAX_UINT ((unsigned int)-1)
#endif

43 44
typedef struct SCSIGenericReq {
    SCSIRequest req;
45 46 47 48
    uint8_t *buf;
    int buflen;
    int len;
    sg_io_hdr_t io_header;
49
} SCSIGenericReq;
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
{
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);

    qemu_put_sbe32s(f, &r->buflen);
    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
        assert(!r->req.sg);
        qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
    }
}

static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
{
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);

    qemu_get_sbe32s(f, &r->buflen);
    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
        assert(!r->req.sg);
        qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
    }
}

P
Paolo Bonzini 已提交
73
static void scsi_free_request(SCSIRequest *req)
74
{
P
Paolo Bonzini 已提交
75 76
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);

77
    g_free(r->buf);
78 79 80
}

/* Helper function for command completion.  */
81
static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
82
{
83
    int status;
84

85 86
    assert(r->req.aiocb == NULL);

87
    if (r->req.io_canceled) {
88
        scsi_req_cancel_complete(&r->req);
89 90
        goto done;
    }
P
Paolo Bonzini 已提交
91
    if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
92
        r->req.sense_len = r->io_header.sb_len_wr;
P
Paolo Bonzini 已提交
93
    }
A
aurel32 已提交
94

95 96
    if (ret != 0) {
        switch (ret) {
P
Paolo Bonzini 已提交
97
        case -EDOM:
98
            status = TASK_SET_FULL;
P
Paolo Bonzini 已提交
99
            break;
100
        case -ENOMEM:
101
            status = CHECK_CONDITION;
102
            scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
103 104
            break;
        default:
105
            status = CHECK_CONDITION;
106
            scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
107 108 109
            break;
        }
    } else {
P
Paolo Bonzini 已提交
110 111 112 113
        if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
            r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
            r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
            (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
114
            status = BUSY;
115
            BADF("Driver Timeout\n");
P
Paolo Bonzini 已提交
116 117 118
        } else if (r->io_header.host_status) {
            status = CHECK_CONDITION;
            scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
119 120
        } else if (r->io_header.status) {
            status = r->io_header.status;
121
        } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
122 123 124 125
            status = CHECK_CONDITION;
        } else {
            status = GOOD;
        }
126
    }
A
aurel32 已提交
127
    DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
128
            r, r->req.tag, status);
G
Gerd Hoffmann 已提交
129

130
    scsi_req_complete(&r->req, status);
131
done:
132
    scsi_req_unref(&r->req);
133 134
}

135 136 137
static void scsi_command_complete(void *opaque, int ret)
{
    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
138
    SCSIDevice *s = r->req.dev;
139 140 141

    assert(r->req.aiocb != NULL);
    r->req.aiocb = NULL;
142 143

    aio_context_acquire(blk_get_aio_context(s->conf.blk));
144
    scsi_command_complete_noio(r, ret);
145
    aio_context_release(blk_get_aio_context(s->conf.blk));
146 147
}

148
static int execute_command(BlockBackend *blk,
149
                           SCSIGenericReq *r, int direction,
150
                           BlockCompletionFunc *complete)
151 152 153 154 155
{
    r->io_header.interface_id = 'S';
    r->io_header.dxfer_direction = direction;
    r->io_header.dxferp = r->buf;
    r->io_header.dxfer_len = r->buflen;
156 157
    r->io_header.cmdp = r->req.cmd.buf;
    r->io_header.cmd_len = r->req.cmd.len;
158 159
    r->io_header.mx_sb_len = sizeof(r->req.sense);
    r->io_header.sbp = r->req.sense;
160 161 162 163
    r->io_header.timeout = MAX_UINT;
    r->io_header.usr_ptr = r;
    r->io_header.flags |= SG_FLAG_DIRECT_IO;

164
    r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
165 166 167
    if (r->req.aiocb == NULL) {
        return -EIO;
    }
168 169 170 171 172 173

    return 0;
}

static void scsi_read_complete(void * opaque, int ret)
{
174
    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
175
    SCSIDevice *s = r->req.dev;
176 177
    int len;

178
    assert(r->req.aiocb != NULL);
179
    r->req.aiocb = NULL;
180

181 182
    aio_context_acquire(blk_get_aio_context(s->conf.blk));

183
    if (ret || r->req.io_canceled) {
184
        scsi_command_complete_noio(r, ret);
185
        goto done;
186
    }
187

188
    len = r->io_header.dxfer_len - r->io_header.resid;
189
    DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
190 191

    r->len = -1;
192
    if (len == 0) {
193
        scsi_command_complete_noio(r, 0);
194
        goto done;
195
    }
196

197 198 199 200 201 202 203 204 205
    /* Snoop READ CAPACITY output to set the blocksize.  */
    if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
        (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
        s->blocksize = ldl_be_p(&r->buf[4]);
        s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
    } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
               (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
        s->blocksize = ldl_be_p(&r->buf[8]);
        s->max_lba = ldq_be_p(&r->buf[0]);
206
    }
207 208
    blk_set_guest_block_size(s->conf.blk, s->blocksize);

209 210 211 212 213 214 215 216 217 218 219 220 221 222
    /* Patch MODE SENSE device specific parameters if the BDS is opened
     * readonly.
     */
    if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
        blk_is_read_only(s->conf.blk) &&
        (r->req.cmd.buf[0] == MODE_SENSE ||
         r->req.cmd.buf[0] == MODE_SENSE_10) &&
        (r->req.cmd.buf[1] & 0x8) == 0) {
        if (r->req.cmd.buf[0] == MODE_SENSE) {
            r->buf[2] |= 0x80;
        } else  {
            r->buf[3] |= 0x80;
        }
    }
223 224 225
    if (s->type == TYPE_DISK &&
        r->req.cmd.buf[0] == INQUIRY &&
        r->req.cmd.buf[2] == 0xb0) {
226 227
        uint32_t max_transfer =
            blk_get_max_transfer(s->conf.blk) / s->blocksize;
228

229 230
        assert(max_transfer);
        stl_be_p(&r->buf[8], max_transfer);
231
        /* Also take care of the opt xfer len. */
232 233
        stl_be_p(&r->buf[12],
                 MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
234
    }
235 236
    scsi_req_data(&r->req, len);
    scsi_req_unref(&r->req);
237 238 239

done:
    aio_context_release(blk_get_aio_context(s->conf.blk));
240 241 242
}

/* Read more data from scsi device into buffer.  */
243
static void scsi_read_data(SCSIRequest *req)
244
{
245
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
246
    SCSIDevice *s = r->req.dev;
247 248
    int ret;

249
    DPRINTF("scsi_read_data tag=0x%x\n", req->tag);
250 251 252

    /* The request is used as the AIO opaque value, so add a ref.  */
    scsi_req_ref(&r->req);
253
    if (r->len == -1) {
254
        scsi_command_complete_noio(r, 0);
255 256 257
        return;
    }

258 259
    ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
                          scsi_read_complete);
260
    if (ret < 0) {
261
        scsi_command_complete_noio(r, ret);
262 263 264 265 266
    }
}

static void scsi_write_complete(void * opaque, int ret)
{
267
    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
268
    SCSIDevice *s = r->req.dev;
269 270

    DPRINTF("scsi_write_complete() ret = %d\n", ret);
271 272

    assert(r->req.aiocb != NULL);
273
    r->req.aiocb = NULL;
274

275 276
    aio_context_acquire(blk_get_aio_context(s->conf.blk));

277
    if (ret || r->req.io_canceled) {
278
        scsi_command_complete_noio(r, ret);
279
        goto done;
280 281
    }

282
    if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
283 284 285
        s->type == TYPE_TAPE) {
        s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
        DPRINTF("block size %d\n", s->blocksize);
A
aurel32 已提交
286 287
    }

288
    scsi_command_complete_noio(r, ret);
289 290 291

done:
    aio_context_release(blk_get_aio_context(s->conf.blk));
292 293 294 295
}

/* Write data to a scsi device.  Returns nonzero on failure.
   The transfer may complete asynchronously.  */
296
static void scsi_write_data(SCSIRequest *req)
297
{
298
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
299
    SCSIDevice *s = r->req.dev;
300 301
    int ret;

302
    DPRINTF("scsi_write_data tag=0x%x\n", req->tag);
303 304
    if (r->len == 0) {
        r->len = r->buflen;
P
Paolo Bonzini 已提交
305
        scsi_req_data(&r->req, r->len);
306
        return;
307 308
    }

309 310
    /* The request is used as the AIO opaque value, so add a ref.  */
    scsi_req_ref(&r->req);
311
    ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
312
    if (ret < 0) {
313
        scsi_command_complete_noio(r, ret);
314 315 316 317
    }
}

/* Return a pointer to the data buffer.  */
318
static uint8_t *scsi_get_buf(SCSIRequest *req)
319
{
320 321
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);

322 323 324 325 326 327 328 329
    return r->buf;
}

/* Execute a scsi command.  Returns the length of the data expected by the
   command.  This will be Positive for data transfers from the device
   (eg. disk reads), negative for transfers to the device (eg. disk writes),
   and zero if the command does not transfer any data.  */

330
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
331
{
332
    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
333
    SCSIDevice *s = r->req.dev;
334 335
    int ret;

336
#ifdef DEBUG_SCSI
337
    DPRINTF("Command: data=0x%02x", cmd[0]);
338 339 340 341 342 343 344 345
    {
        int i;
        for (i = 1; i < r->req.cmd.len; i++) {
            printf(" 0x%02x", cmd[i]);
        }
        printf("\n");
    }
#endif
346

347
    if (r->req.cmd.xfer == 0) {
348
        g_free(r->buf);
349 350
        r->buflen = 0;
        r->buf = NULL;
351 352
        /* The request is used as the AIO opaque value, so add a ref.  */
        scsi_req_ref(&r->req);
353 354
        ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
                              scsi_command_complete);
355
        if (ret < 0) {
356
            scsi_command_complete_noio(r, ret);
357
            return 0;
358 359 360 361
        }
        return 0;
    }

362
    if (r->buflen != r->req.cmd.xfer) {
363
        g_free(r->buf);
364
        r->buf = g_malloc(r->req.cmd.xfer);
365
        r->buflen = r->req.cmd.xfer;
366 367 368
    }

    memset(r->buf, 0, r->buflen);
369
    r->len = r->req.cmd.xfer;
G
Gerd Hoffmann 已提交
370
    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
371
        r->len = 0;
372
        return -r->req.cmd.xfer;
P
Paolo Bonzini 已提交
373
    } else {
374
        return r->req.cmd.xfer;
375 376 377
    }
}

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
{
    int i;

    if ((p[1] & 0xF) == 3) {
        /* NAA designator type */
        if (p[3] != 8) {
            return -EINVAL;
        }
        *p_wwn = ldq_be_p(p + 4);
        return 0;
    }

    if ((p[1] & 0xF) == 8) {
        /* SCSI name string designator type */
        if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
            return -EINVAL;
        }
        if (p[3] > 20 && p[24] != ',') {
            return -EINVAL;
        }
        *p_wwn = 0;
        for (i = 8; i < 24; i++) {
401
            char c = qemu_toupper(p[i]);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
            c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
            *p_wwn = (*p_wwn << 4) | c;
        }
        return 0;
    }

    return -EINVAL;
}

void scsi_generic_read_device_identification(SCSIDevice *s)
{
    uint8_t cmd[6];
    uint8_t buf[250];
    uint8_t sensebuf[8];
    sg_io_hdr_t io_header;
    int ret;
    int i, len;

    memset(cmd, 0, sizeof(cmd));
    memset(buf, 0, sizeof(buf));
    cmd[0] = INQUIRY;
    cmd[1] = 1;
    cmd[2] = 0x83;
    cmd[4] = sizeof(buf);

    memset(&io_header, 0, sizeof(io_header));
    io_header.interface_id = 'S';
    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
    io_header.dxfer_len = sizeof(buf);
    io_header.dxferp = buf;
    io_header.cmdp = cmd;
    io_header.cmd_len = sizeof(cmd);
    io_header.mx_sb_len = sizeof(sensebuf);
    io_header.sbp = sensebuf;
    io_header.timeout = 6000; /* XXX */

    ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
    if (ret < 0 || io_header.driver_status || io_header.host_status) {
        return;
    }

    len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
    for (i = 0; i + 3 <= len; ) {
        const uint8_t *p = &buf[i + 4];
        uint64_t wwn;

        if (i + (p[3] + 4) > len) {
            break;
        }

        if ((p[1] & 0x10) == 0) {
            /* Associated with the logical unit */
            if (read_naa_id(p, &wwn) == 0) {
                s->wwn = wwn;
            }
        } else if ((p[1] & 0x10) == 0x10) {
            /* Associated with the target port */
            if (read_naa_id(p, &wwn) == 0) {
                s->port_wwn = wwn;
            }
        }

        i += p[3] + 4;
    }
}

468
static int get_stream_blocksize(BlockBackend *blk)
A
aurel32 已提交
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
{
    uint8_t cmd[6];
    uint8_t buf[12];
    uint8_t sensebuf[8];
    sg_io_hdr_t io_header;
    int ret;

    memset(cmd, 0, sizeof(cmd));
    memset(buf, 0, sizeof(buf));
    cmd[0] = MODE_SENSE;
    cmd[4] = sizeof(buf);

    memset(&io_header, 0, sizeof(io_header));
    io_header.interface_id = 'S';
    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
    io_header.dxfer_len = sizeof(buf);
    io_header.dxferp = buf;
    io_header.cmdp = cmd;
    io_header.cmd_len = sizeof(cmd);
    io_header.mx_sb_len = sizeof(sensebuf);
    io_header.sbp = sensebuf;
    io_header.timeout = 6000; /* XXX */

492
    ret = blk_ioctl(blk, SG_IO, &io_header);
493
    if (ret < 0 || io_header.driver_status || io_header.host_status) {
A
aurel32 已提交
494
        return -1;
495
    }
A
aurel32 已提交
496 497 498
    return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}

499 500
static void scsi_generic_reset(DeviceState *dev)
{
501
    SCSIDevice *s = SCSI_DEVICE(dev);
502

503
    scsi_device_purge_requests(s, SENSE_CODE(RESET));
504 505
}

506
static void scsi_generic_realize(SCSIDevice *s, Error **errp)
507
{
508
    int rc;
509 510 511
    int sg_version;
    struct sg_scsi_id scsiid;

512
    if (!s->conf.blk) {
513 514
        error_setg(errp, "drive property not set");
        return;
515
    }
516

517
    if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
518 519
        error_setg(errp, "Device doesn't support drive option werror");
        return;
520
    }
521
    if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
522 523
        error_setg(errp, "Device doesn't support drive option rerror");
        return;
524 525
    }

526
    /* check we are using a driver managing SG_IO (version 3 and after */
527
    rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
528
    if (rc < 0) {
529 530 531 532
        error_setg(errp, "cannot get SG_IO version number: %s.  "
                         "Is this a SCSI device?",
                         strerror(-rc));
        return;
533 534
    }
    if (sg_version < 30000) {
535 536
        error_setg(errp, "scsi generic interface too old");
        return;
537
    }
538 539

    /* get LUN of the /dev/sg? */
540
    if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
541 542
        error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
        return;
543
    }
544 545

    /* define device state */
546 547
    s->type = scsiid.scsi_type;
    DPRINTF("device type %d\n", s->type);
P
Paolo Bonzini 已提交
548

549 550
    switch (s->type) {
    case TYPE_TAPE:
551
        s->blocksize = get_stream_blocksize(s->conf.blk);
552 553 554
        if (s->blocksize == -1) {
            s->blocksize = 0;
        }
555 556 557 558 559 560 561 562 563 564 565 566 567
        break;

        /* Make a guess for block devices, we'll fix it when the guest sends.
         * READ CAPACITY.  If they don't, they likely would assume these sizes
         * anyway. (TODO: they could also send MODE SENSE).
         */
    case TYPE_ROM:
    case TYPE_WORM:
        s->blocksize = 2048;
        break;
    default:
        s->blocksize = 512;
        break;
A
aurel32 已提交
568
    }
569 570

    DPRINTF("block size %d\n", s->blocksize);
571 572

    scsi_generic_read_device_identification(s);
573
}
574

P
Paolo Bonzini 已提交
575
const SCSIReqOps scsi_generic_req_ops = {
P
Paolo Bonzini 已提交
576
    .size         = sizeof(SCSIGenericReq),
577 578 579 580 581
    .free_req     = scsi_free_request,
    .send_command = scsi_send_command,
    .read_data    = scsi_read_data,
    .write_data   = scsi_write_data,
    .get_buf      = scsi_get_buf,
582 583
    .load_request = scsi_generic_load_request,
    .save_request = scsi_generic_save_request,
P
Paolo Bonzini 已提交
584 585 586
};

static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
P
Paolo Bonzini 已提交
587
                                     uint8_t *buf, void *hba_private)
P
Paolo Bonzini 已提交
588
{
589
    return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
P
Paolo Bonzini 已提交
590 591
}

592
static Property scsi_generic_properties[] = {
593
    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
594 595 596
    DEFINE_PROP_END_OF_LIST(),
};

597 598 599 600 601 602
static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
                                  uint8_t *buf, void *hba_private)
{
    return scsi_bus_parse_cdb(dev, cmd, buf, hba_private);
}

603 604
static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
{
605
    DeviceClass *dc = DEVICE_CLASS(klass);
606 607
    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);

608
    sc->realize      = scsi_generic_realize;
609
    sc->alloc_req    = scsi_new_request;
610
    sc->parse_cdb    = scsi_generic_parse_cdb;
611 612 613 614
    dc->fw_name = "disk";
    dc->desc = "pass through generic scsi device (/dev/sg*)";
    dc->reset = scsi_generic_reset;
    dc->props = scsi_generic_properties;
615
    dc->vmsd  = &vmstate_scsi_device;
616 617
}

618
static const TypeInfo scsi_generic_info = {
619 620 621 622
    .name          = "scsi-generic",
    .parent        = TYPE_SCSI_DEVICE,
    .instance_size = sizeof(SCSIDevice),
    .class_init    = scsi_generic_class_initfn,
623
};
624

A
Andreas Färber 已提交
625
static void scsi_generic_register_types(void)
626
{
627
    type_register_static(&scsi_generic_info);
628
}
A
Andreas Färber 已提交
629 630

type_init(scsi_generic_register_types)
631

632
#endif /* __linux__ */