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