]> git.proxmox.com Git - qemu.git/blame - hw/scsi-generic.c
Merge remote branch 'qemu-kvm/uq/master' into staging
[qemu.git] / hw / scsi-generic.c
CommitLineData
2cc977e2
TS
1/*
2 * Generic SCSI Device support
3 *
4 * Copyright (c) 2007 Bull S.A.S.
5 * Based on code by Paul Brook
6 * Based on code by Fabrice Bellard
7 *
8 * Written by Laurent Vivier <Laurent.Vivier@bull.net>
9 *
10 * This code is licenced under the LGPL.
11 *
12 */
13
14#include "qemu-common.h"
2f792016 15#include "qemu-error.h"
43b443b6 16#include "scsi.h"
2cc977e2 17
d52affa7 18#ifdef __linux__
2cc977e2
TS
19
20//#define DEBUG_SCSI
21
22#ifdef DEBUG_SCSI
001faf32
BS
23#define DPRINTF(fmt, ...) \
24do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
2cc977e2 25#else
001faf32 26#define DPRINTF(fmt, ...) do {} while(0)
2cc977e2
TS
27#endif
28
001faf32
BS
29#define BADF(fmt, ...) \
30do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
2cc977e2
TS
31
32#include <stdio.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h>
36#include <scsi/sg.h>
0d65e1f8 37#include "scsi-defs.h"
2cc977e2 38
a9dd6843 39#define SCSI_SENSE_BUF_SIZE 96
2cc977e2
TS
40
41#define SG_ERR_DRIVER_TIMEOUT 0x06
42#define SG_ERR_DRIVER_SENSE 0x08
43
44#ifndef MAX_UINT
45#define MAX_UINT ((unsigned int)-1)
46#endif
47
d52affa7
GH
48typedef struct SCSIGenericState SCSIGenericState;
49
4c41d2ef
GH
50typedef struct SCSIGenericReq {
51 SCSIRequest req;
2cc977e2
TS
52 uint8_t *buf;
53 int buflen;
54 int len;
55 sg_io_hdr_t io_header;
4c41d2ef 56} SCSIGenericReq;
2cc977e2 57
d52affa7 58struct SCSIGenericState
2cc977e2 59{
d52affa7 60 SCSIDevice qdev;
428c149b 61 BlockDriverState *bs;
2cc977e2 62 int lun;
2cc977e2
TS
63 int driver_status;
64 uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
89c0f643 65 uint8_t senselen;
2cc977e2
TS
66};
67
89b08ae1 68static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
2cc977e2 69{
89b08ae1 70 SCSIRequest *req;
2cc977e2 71
89b08ae1
GH
72 req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
73 return DO_UPCAST(SCSIGenericReq, req, req);
2cc977e2
TS
74}
75
4c41d2ef 76static void scsi_remove_request(SCSIGenericReq *r)
2cc977e2 77{
9af99d98 78 qemu_free(r->buf);
89b08ae1 79 scsi_req_free(&r->req);
2cc977e2
TS
80}
81
4c41d2ef 82static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag)
2cc977e2 83{
89b08ae1 84 return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag));
2cc977e2
TS
85}
86
87/* Helper function for command completion. */
88static void scsi_command_complete(void *opaque, int ret)
89{
4c41d2ef
GH
90 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
91 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
2cc977e2
TS
92
93 s->driver_status = r->io_header.driver_status;
89c0f643
AJ
94 if (s->driver_status & SG_ERR_DRIVER_SENSE)
95 s->senselen = r->io_header.sb_len_wr;
96
2cc977e2 97 if (ret != 0)
ed3a34a3 98 r->req.status = BUSY << 1;
2cc977e2
TS
99 else {
100 if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
ed3a34a3 101 r->req.status = BUSY << 1;
2cc977e2 102 BADF("Driver Timeout\n");
89c0f643 103 } else if (r->io_header.status)
ed3a34a3 104 r->req.status = r->io_header.status;
89c0f643 105 else if (s->driver_status & SG_ERR_DRIVER_SENSE)
ed3a34a3 106 r->req.status = CHECK_CONDITION << 1;
2cc977e2 107 else
ed3a34a3 108 r->req.status = GOOD << 1;
2cc977e2 109 }
89c0f643 110 DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
ed3a34a3
GH
111 r, r->req.tag, r->req.status);
112
113 scsi_req_complete(&r->req);
89b08ae1 114 scsi_remove_request(r);
2cc977e2
TS
115}
116
117/* Cancel a pending data transfer. */
118static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
119{
120 DPRINTF("scsi_cancel_io 0x%x\n", tag);
d52affa7 121 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
4c41d2ef 122 SCSIGenericReq *r;
2cc977e2
TS
123 DPRINTF("Cancel tag=0x%x\n", tag);
124 r = scsi_find_request(s, tag);
125 if (r) {
4c41d2ef
GH
126 if (r->req.aiocb)
127 bdrv_aio_cancel(r->req.aiocb);
128 r->req.aiocb = NULL;
2cc977e2
TS
129 scsi_remove_request(r);
130 }
131}
132
133static int execute_command(BlockDriverState *bdrv,
4c41d2ef 134 SCSIGenericReq *r, int direction,
2cc977e2
TS
135 BlockDriverCompletionFunc *complete)
136{
4c41d2ef
GH
137 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
138
2cc977e2
TS
139 r->io_header.interface_id = 'S';
140 r->io_header.dxfer_direction = direction;
141 r->io_header.dxferp = r->buf;
142 r->io_header.dxfer_len = r->buflen;
29362ebe
GH
143 r->io_header.cmdp = r->req.cmd.buf;
144 r->io_header.cmd_len = r->req.cmd.len;
4c41d2ef
GH
145 r->io_header.mx_sb_len = sizeof(s->sensebuf);
146 r->io_header.sbp = s->sensebuf;
2cc977e2
TS
147 r->io_header.timeout = MAX_UINT;
148 r->io_header.usr_ptr = r;
149 r->io_header.flags |= SG_FLAG_DIRECT_IO;
150
4c41d2ef
GH
151 r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
152 if (r->req.aiocb == NULL) {
2cc977e2
TS
153 BADF("execute_command: read failed !\n");
154 return -1;
155 }
156
157 return 0;
158}
159
160static void scsi_read_complete(void * opaque, int ret)
161{
4c41d2ef 162 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
2cc977e2
TS
163 int len;
164
165 if (ret) {
166 DPRINTF("IO error\n");
167 scsi_command_complete(r, ret);
168 return;
169 }
170 len = r->io_header.dxfer_len - r->io_header.resid;
4c41d2ef 171 DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
2cc977e2
TS
172
173 r->len = -1;
4c41d2ef 174 r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
89c0f643
AJ
175 if (len == 0)
176 scsi_command_complete(r, 0);
2cc977e2
TS
177}
178
179/* Read more data from scsi device into buffer. */
180static void scsi_read_data(SCSIDevice *d, uint32_t tag)
181{
d52affa7 182 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
4c41d2ef 183 SCSIGenericReq *r;
2cc977e2
TS
184 int ret;
185
186 DPRINTF("scsi_read_data 0x%x\n", tag);
187 r = scsi_find_request(s, tag);
188 if (!r) {
189 BADF("Bad read tag 0x%x\n", tag);
190 /* ??? This is the wrong error. */
191 scsi_command_complete(r, -EINVAL);
192 return;
193 }
194
195 if (r->len == -1) {
196 scsi_command_complete(r, 0);
197 return;
198 }
199
29362ebe 200 if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
2cc977e2 201 {
89c0f643
AJ
202 s->senselen = MIN(r->len, s->senselen);
203 memcpy(r->buf, s->sensebuf, s->senselen);
2cc977e2 204 r->io_header.driver_status = 0;
89c0f643
AJ
205 r->io_header.status = 0;
206 r->io_header.dxfer_len = s->senselen;
2cc977e2 207 r->len = -1;
4c41d2ef 208 DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen);
a9dd6843
AL
209 DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
210 r->buf[0], r->buf[1], r->buf[2], r->buf[3],
211 r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
4c41d2ef 212 r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen);
2cc977e2
TS
213 return;
214 }
215
428c149b 216 ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
2cc977e2
TS
217 if (ret == -1) {
218 scsi_command_complete(r, -EINVAL);
219 return;
220 }
221}
222
223static void scsi_write_complete(void * opaque, int ret)
224{
4c41d2ef
GH
225 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
226 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
2cc977e2
TS
227
228 DPRINTF("scsi_write_complete() ret = %d\n", ret);
229 if (ret) {
230 DPRINTF("IO error\n");
231 scsi_command_complete(r, ret);
232 return;
233 }
234
29362ebe 235 if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
91376656 236 s->qdev.type == TYPE_TAPE) {
b07995e3 237 s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
4c41d2ef 238 DPRINTF("block size %d\n", s->blocksize);
89c0f643
AJ
239 }
240
2cc977e2
TS
241 scsi_command_complete(r, ret);
242}
243
244/* Write data to a scsi device. Returns nonzero on failure.
245 The transfer may complete asynchronously. */
246static int scsi_write_data(SCSIDevice *d, uint32_t tag)
247{
d52affa7 248 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
4c41d2ef 249 SCSIGenericReq *r;
2cc977e2
TS
250 int ret;
251
252 DPRINTF("scsi_write_data 0x%x\n", tag);
253 r = scsi_find_request(s, tag);
254 if (!r) {
255 BADF("Bad write tag 0x%x\n", tag);
256 /* ??? This is the wrong error. */
257 scsi_command_complete(r, -EINVAL);
258 return 0;
259 }
260
261 if (r->len == 0) {
262 r->len = r->buflen;
4c41d2ef 263 r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len);
2cc977e2
TS
264 return 0;
265 }
266
428c149b 267 ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
2cc977e2
TS
268 if (ret == -1) {
269 scsi_command_complete(r, -EINVAL);
270 return 1;
271 }
272
273 return 0;
274}
275
276/* Return a pointer to the data buffer. */
277static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
278{
d52affa7 279 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
4c41d2ef 280 SCSIGenericReq *r;
2cc977e2
TS
281 r = scsi_find_request(s, tag);
282 if (!r) {
283 BADF("Bad buffer tag 0x%x\n", tag);
284 return NULL;
285 }
286 return r->buf;
287}
288
2ec749cb 289static void scsi_req_fixup(SCSIRequest *req)
2cc977e2 290{
2ec749cb 291 switch(req->cmd.buf[0]) {
2cc977e2 292 case WRITE_10:
2ec749cb 293 req->cmd.buf[1] &= ~0x08; /* disable FUA */
2cc977e2
TS
294 break;
295 case READ_10:
2ec749cb 296 req->cmd.buf[1] &= ~0x08; /* disable FUA */
a9dd6843
AL
297 break;
298 case REWIND:
299 case START_STOP:
2ec749cb
GH
300 if (req->dev->type == TYPE_TAPE) {
301 /* force IMMED, otherwise qemu waits end of command */
302 req->cmd.buf[1] = 0x01;
303 }
a9dd6843 304 break;
a9dd6843 305 }
a9dd6843
AL
306}
307
2cc977e2
TS
308/* Execute a scsi command. Returns the length of the data expected by the
309 command. This will be Positive for data transfers from the device
310 (eg. disk reads), negative for transfers to the device (eg. disk writes),
311 and zero if the command does not transfer any data. */
312
313static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
314 uint8_t *cmd, int lun)
315{
d52affa7 316 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
4c41d2ef 317 SCSIGenericReq *r;
d52affa7 318 SCSIBus *bus;
2cc977e2
TS
319 int ret;
320
89c0f643
AJ
321 if (cmd[0] != REQUEST_SENSE &&
322 (lun != s->lun || (cmd[1] >> 5) != s->lun)) {
323 DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
324
325 s->sensebuf[0] = 0x70;
326 s->sensebuf[1] = 0x00;
327 s->sensebuf[2] = ILLEGAL_REQUEST;
328 s->sensebuf[3] = 0x00;
329 s->sensebuf[4] = 0x00;
330 s->sensebuf[5] = 0x00;
331 s->sensebuf[6] = 0x00;
332 s->senselen = 7;
333 s->driver_status = SG_ERR_DRIVER_SENSE;
d52affa7
GH
334 bus = scsi_bus_from_device(d);
335 bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1);
89c0f643
AJ
336 return 0;
337 }
338
2cc977e2
TS
339 r = scsi_find_request(s, tag);
340 if (r) {
341 BADF("Tag 0x%x already in use %p\n", tag, r);
342 scsi_cancel_io(d, tag);
343 }
89b08ae1 344 r = scsi_new_request(d, tag, lun);
2cc977e2 345
2ec749cb
GH
346 if (-1 == scsi_req_parse(&r->req, cmd)) {
347 BADF("Unsupported command length, command %x\n", cmd[0]);
348 scsi_remove_request(r);
349 return 0;
350 }
351 scsi_req_fixup(&r->req);
352
353 DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
354 cmd[0], r->req.cmd.xfer);
2cc977e2 355
2ec749cb 356 if (r->req.cmd.xfer == 0) {
2cc977e2 357 if (r->buf != NULL)
e3c916e6 358 qemu_free(r->buf);
2cc977e2
TS
359 r->buflen = 0;
360 r->buf = NULL;
428c149b 361 ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
2cc977e2
TS
362 if (ret == -1) {
363 scsi_command_complete(r, -EINVAL);
364 return 0;
365 }
366 return 0;
367 }
368
2ec749cb 369 if (r->buflen != r->req.cmd.xfer) {
2cc977e2 370 if (r->buf != NULL)
e3c916e6 371 qemu_free(r->buf);
2ec749cb
GH
372 r->buf = qemu_malloc(r->req.cmd.xfer);
373 r->buflen = r->req.cmd.xfer;
2cc977e2
TS
374 }
375
376 memset(r->buf, 0, r->buflen);
2ec749cb 377 r->len = r->req.cmd.xfer;
97a06435 378 if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
2cc977e2 379 r->len = 0;
2ec749cb 380 return -r->req.cmd.xfer;
2cc977e2
TS
381 }
382
2ec749cb 383 return r->req.cmd.xfer;
2cc977e2
TS
384}
385
386static int get_blocksize(BlockDriverState *bdrv)
387{
388 uint8_t cmd[10];
389 uint8_t buf[8];
390 uint8_t sensebuf[8];
391 sg_io_hdr_t io_header;
392 int ret;
393
4f26a486
AL
394 memset(cmd, 0, sizeof(cmd));
395 memset(buf, 0, sizeof(buf));
2cc977e2
TS
396 cmd[0] = READ_CAPACITY;
397
398 memset(&io_header, 0, sizeof(io_header));
399 io_header.interface_id = 'S';
400 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
401 io_header.dxfer_len = sizeof(buf);
402 io_header.dxferp = buf;
403 io_header.cmdp = cmd;
404 io_header.cmd_len = sizeof(cmd);
405 io_header.mx_sb_len = sizeof(sensebuf);
406 io_header.sbp = sensebuf;
407 io_header.timeout = 6000; /* XXX */
408
221f715d 409 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 410 if (ret < 0)
2cc977e2
TS
411 return -1;
412
413 return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
414}
415
89c0f643
AJ
416static int get_stream_blocksize(BlockDriverState *bdrv)
417{
418 uint8_t cmd[6];
419 uint8_t buf[12];
420 uint8_t sensebuf[8];
421 sg_io_hdr_t io_header;
422 int ret;
423
424 memset(cmd, 0, sizeof(cmd));
425 memset(buf, 0, sizeof(buf));
426 cmd[0] = MODE_SENSE;
427 cmd[4] = sizeof(buf);
428
429 memset(&io_header, 0, sizeof(io_header));
430 io_header.interface_id = 'S';
431 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
432 io_header.dxfer_len = sizeof(buf);
433 io_header.dxferp = buf;
434 io_header.cmdp = cmd;
435 io_header.cmd_len = sizeof(cmd);
436 io_header.mx_sb_len = sizeof(sensebuf);
437 io_header.sbp = sensebuf;
438 io_header.timeout = 6000; /* XXX */
439
221f715d 440 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 441 if (ret < 0)
89c0f643
AJ
442 return -1;
443
444 return (buf[9] << 16) | (buf[10] << 8) | buf[11];
445}
446
2cc977e2
TS
447static void scsi_destroy(SCSIDevice *d)
448{
d52affa7 449 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
9af99d98 450 SCSIGenericReq *r;
2cc977e2 451
9af99d98
GH
452 while (!QTAILQ_EMPTY(&s->qdev.requests)) {
453 r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
454 scsi_remove_request(r);
2cc977e2 455 }
f8b6cc00 456 blockdev_mark_auto_del(s->qdev.conf.bs);
2cc977e2
TS
457}
458
d52affa7 459static int scsi_generic_initfn(SCSIDevice *dev)
2cc977e2 460{
d52affa7 461 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
2cc977e2 462 int sg_version;
2cc977e2
TS
463 struct sg_scsi_id scsiid;
464
f8b6cc00 465 if (!s->qdev.conf.bs) {
1ecda02b 466 error_report("scsi-generic: drive property not set");
d52affa7
GH
467 return -1;
468 }
f8b6cc00 469 s->bs = s->qdev.conf.bs;
2cc977e2 470
d52affa7 471 /* check we are really using a /dev/sg* file */
428c149b 472 if (!bdrv_is_sg(s->bs)) {
1ecda02b 473 error_report("scsi-generic: not /dev/sg*");
d52affa7
GH
474 return -1;
475 }
2cc977e2 476
620f862e
MA
477 if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
478 error_report("Device doesn't support drive option werror");
479 return -1;
480 }
481 if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
482 error_report("Device doesn't support drive option rerror");
483 return -1;
484 }
485
2cc977e2 486 /* check we are using a driver managing SG_IO (version 3 and after */
428c149b 487 if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
d52affa7 488 sg_version < 30000) {
1ecda02b 489 error_report("scsi-generic: scsi generic interface too old");
d52affa7
GH
490 return -1;
491 }
2cc977e2
TS
492
493 /* get LUN of the /dev/sg? */
428c149b 494 if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) {
1ecda02b 495 error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed");
d52affa7
GH
496 return -1;
497 }
2cc977e2
TS
498
499 /* define device state */
2cc977e2 500 s->lun = scsiid.lun;
89c0f643 501 DPRINTF("LUN %d\n", s->lun);
91376656
GH
502 s->qdev.type = scsiid.scsi_type;
503 DPRINTF("device type %d\n", s->qdev.type);
504 if (s->qdev.type == TYPE_TAPE) {
428c149b 505 s->qdev.blocksize = get_stream_blocksize(s->bs);
b07995e3
GH
506 if (s->qdev.blocksize == -1)
507 s->qdev.blocksize = 0;
89c0f643 508 } else {
428c149b 509 s->qdev.blocksize = get_blocksize(s->bs);
89c0f643 510 /* removable media returns 0 if not present */
b07995e3 511 if (s->qdev.blocksize <= 0) {
91376656 512 if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM)
b07995e3 513 s->qdev.blocksize = 2048;
89c0f643 514 else
b07995e3 515 s->qdev.blocksize = 512;
89c0f643
AJ
516 }
517 }
b07995e3 518 DPRINTF("block size %d\n", s->qdev.blocksize);
2cc977e2
TS
519 s->driver_status = 0;
520 memset(s->sensebuf, 0, sizeof(s->sensebuf));
7d0d6950 521 bdrv_set_removable(s->bs, 0);
d52affa7
GH
522 return 0;
523}
2cc977e2 524
d52affa7
GH
525static SCSIDeviceInfo scsi_generic_info = {
526 .qdev.name = "scsi-generic",
527 .qdev.desc = "pass through generic scsi device (/dev/sg*)",
528 .qdev.size = sizeof(SCSIGenericState),
529 .init = scsi_generic_initfn,
530 .destroy = scsi_destroy,
531 .send_command = scsi_send_command,
532 .read_data = scsi_read_data,
533 .write_data = scsi_write_data,
534 .cancel_io = scsi_cancel_io,
535 .get_buf = scsi_get_buf,
536 .qdev.props = (Property[]) {
428c149b 537 DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
d52affa7
GH
538 DEFINE_PROP_END_OF_LIST(),
539 },
540};
2cc977e2 541
d52affa7
GH
542static void scsi_generic_register_devices(void)
543{
544 scsi_qdev_register(&scsi_generic_info);
2cc977e2 545}
d52affa7
GH
546device_init(scsi_generic_register_devices)
547
2cc977e2 548#endif /* __linux__ */