]> git.proxmox.com Git - qemu.git/blame - hw/scsi-generic.c
scsi: Update sense code handling
[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
a1f0cce2
HR
69static void scsi_set_sense(SCSIGenericState *s, SCSISense sense)
70{
71 s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0);
72 s->driver_status = SG_ERR_DRIVER_SENSE;
73}
74
75static void scsi_clear_sense(SCSIGenericState *s)
76{
77 memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE);
78 s->senselen = 0;
79 s->driver_status = 0;
80}
81
5c6c0e51 82static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
2cc977e2 83{
89b08ae1 84 SCSIRequest *req;
2cc977e2 85
89b08ae1 86 req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
5c6c0e51 87 return req;
2cc977e2
TS
88}
89
ad2d30f7 90static void scsi_free_request(SCSIRequest *req)
2cc977e2 91{
ad2d30f7
PB
92 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
93
9af99d98 94 qemu_free(r->buf);
2cc977e2
TS
95}
96
2cc977e2
TS
97/* Helper function for command completion. */
98static void scsi_command_complete(void *opaque, int ret)
99{
4c41d2ef
GH
100 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
101 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
2cc977e2 102
d33e0ce2 103 r->req.aiocb = NULL;
2cc977e2 104 s->driver_status = r->io_header.driver_status;
89c0f643
AJ
105 if (s->driver_status & SG_ERR_DRIVER_SENSE)
106 s->senselen = r->io_header.sb_len_wr;
107
a1f0cce2
HR
108 if (ret != 0) {
109 switch (ret) {
110 case -EINVAL:
111 r->req.status = CHECK_CONDITION;
112 scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
113 break;
114 case -ENOMEM:
115 r->req.status = CHECK_CONDITION;
116 scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
117 break;
118 default:
119 r->req.status = CHECK_CONDITION;
120 scsi_set_sense(s, SENSE_CODE(IO_ERROR));
121 break;
122 }
123 } else {
2cc977e2 124 if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
f0171327 125 r->req.status = BUSY;
2cc977e2 126 BADF("Driver Timeout\n");
89c0f643 127 } else if (r->io_header.status)
ed3a34a3 128 r->req.status = r->io_header.status;
89c0f643 129 else if (s->driver_status & SG_ERR_DRIVER_SENSE)
f0171327 130 r->req.status = CHECK_CONDITION;
2cc977e2 131 else
f0171327 132 r->req.status = GOOD;
2cc977e2 133 }
89c0f643 134 DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
ed3a34a3
GH
135 r, r->req.tag, r->req.status);
136
137 scsi_req_complete(&r->req);
2cc977e2
TS
138}
139
140/* Cancel a pending data transfer. */
5c6c0e51 141static void scsi_cancel_io(SCSIRequest *req)
2cc977e2 142{
5c6c0e51
HR
143 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
144
145 DPRINTF("Cancel tag=0x%x\n", req->tag);
146 if (r->req.aiocb) {
147 bdrv_aio_cancel(r->req.aiocb);
2cc977e2 148 }
5c6c0e51 149 r->req.aiocb = NULL;
2cc977e2
TS
150}
151
152static int execute_command(BlockDriverState *bdrv,
4c41d2ef 153 SCSIGenericReq *r, int direction,
2cc977e2
TS
154 BlockDriverCompletionFunc *complete)
155{
4c41d2ef
GH
156 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
157
2cc977e2
TS
158 r->io_header.interface_id = 'S';
159 r->io_header.dxfer_direction = direction;
160 r->io_header.dxferp = r->buf;
161 r->io_header.dxfer_len = r->buflen;
29362ebe
GH
162 r->io_header.cmdp = r->req.cmd.buf;
163 r->io_header.cmd_len = r->req.cmd.len;
4c41d2ef
GH
164 r->io_header.mx_sb_len = sizeof(s->sensebuf);
165 r->io_header.sbp = s->sensebuf;
2cc977e2
TS
166 r->io_header.timeout = MAX_UINT;
167 r->io_header.usr_ptr = r;
168 r->io_header.flags |= SG_FLAG_DIRECT_IO;
169
4c41d2ef
GH
170 r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
171 if (r->req.aiocb == NULL) {
2cc977e2 172 BADF("execute_command: read failed !\n");
a1f0cce2 173 return -ENOMEM;
2cc977e2
TS
174 }
175
176 return 0;
177}
178
179static void scsi_read_complete(void * opaque, int ret)
180{
4c41d2ef 181 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
2cc977e2
TS
182 int len;
183
d33e0ce2 184 r->req.aiocb = NULL;
2cc977e2 185 if (ret) {
aa2b1e89 186 DPRINTF("IO error ret %d\n", ret);
2cc977e2
TS
187 scsi_command_complete(r, ret);
188 return;
189 }
190 len = r->io_header.dxfer_len - r->io_header.resid;
4c41d2ef 191 DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
2cc977e2
TS
192
193 r->len = -1;
40f16dd1 194 if (len == 0) {
89c0f643 195 scsi_command_complete(r, 0);
40f16dd1 196 } else {
ab9adc88 197 scsi_req_data(&r->req, len);
40f16dd1 198 }
2cc977e2
TS
199}
200
201/* Read more data from scsi device into buffer. */
5c6c0e51 202static void scsi_read_data(SCSIRequest *req)
2cc977e2 203{
5c6c0e51
HR
204 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
205 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
2cc977e2
TS
206 int ret;
207
5c6c0e51 208 DPRINTF("scsi_read_data 0x%x\n", req->tag);
2cc977e2
TS
209 if (r->len == -1) {
210 scsi_command_complete(r, 0);
211 return;
212 }
213
29362ebe 214 if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
2cc977e2 215 {
89c0f643
AJ
216 s->senselen = MIN(r->len, s->senselen);
217 memcpy(r->buf, s->sensebuf, s->senselen);
2cc977e2 218 r->io_header.driver_status = 0;
89c0f643
AJ
219 r->io_header.status = 0;
220 r->io_header.dxfer_len = s->senselen;
2cc977e2 221 r->len = -1;
4c41d2ef 222 DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen);
a9dd6843
AL
223 DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
224 r->buf[0], r->buf[1], r->buf[2], r->buf[3],
225 r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
ab9adc88 226 scsi_req_data(&r->req, s->senselen);
a1f0cce2
HR
227 /* Clear sensebuf after REQUEST_SENSE */
228 scsi_clear_sense(s);
2cc977e2
TS
229 return;
230 }
231
428c149b 232 ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
a1f0cce2
HR
233 if (ret < 0) {
234 scsi_command_complete(r, ret);
2cc977e2
TS
235 return;
236 }
237}
238
239static void scsi_write_complete(void * opaque, int ret)
240{
4c41d2ef
GH
241 SCSIGenericReq *r = (SCSIGenericReq *)opaque;
242 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
2cc977e2
TS
243
244 DPRINTF("scsi_write_complete() ret = %d\n", ret);
d33e0ce2 245 r->req.aiocb = NULL;
2cc977e2
TS
246 if (ret) {
247 DPRINTF("IO error\n");
248 scsi_command_complete(r, ret);
249 return;
250 }
251
29362ebe 252 if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
91376656 253 s->qdev.type == TYPE_TAPE) {
b07995e3 254 s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
aa2b1e89 255 DPRINTF("block size %d\n", s->qdev.blocksize);
89c0f643
AJ
256 }
257
2cc977e2
TS
258 scsi_command_complete(r, ret);
259}
260
261/* Write data to a scsi device. Returns nonzero on failure.
262 The transfer may complete asynchronously. */
5c6c0e51 263static int scsi_write_data(SCSIRequest *req)
2cc977e2 264{
5c6c0e51
HR
265 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
266 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
2cc977e2
TS
267 int ret;
268
5c6c0e51 269 DPRINTF("scsi_write_data 0x%x\n", req->tag);
2cc977e2
TS
270 if (r->len == 0) {
271 r->len = r->buflen;
ab9adc88 272 scsi_req_data(&r->req, r->len);
2cc977e2
TS
273 return 0;
274 }
275
428c149b 276 ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
a1f0cce2
HR
277 if (ret < 0) {
278 scsi_command_complete(r, ret);
2cc977e2
TS
279 return 1;
280 }
281
282 return 0;
283}
284
285/* Return a pointer to the data buffer. */
5c6c0e51 286static uint8_t *scsi_get_buf(SCSIRequest *req)
2cc977e2 287{
5c6c0e51
HR
288 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
289
2cc977e2
TS
290 return r->buf;
291}
292
2ec749cb 293static void scsi_req_fixup(SCSIRequest *req)
2cc977e2 294{
2ec749cb 295 switch(req->cmd.buf[0]) {
2cc977e2 296 case WRITE_10:
2ec749cb 297 req->cmd.buf[1] &= ~0x08; /* disable FUA */
2cc977e2
TS
298 break;
299 case READ_10:
2ec749cb 300 req->cmd.buf[1] &= ~0x08; /* disable FUA */
a9dd6843
AL
301 break;
302 case REWIND:
303 case START_STOP:
2ec749cb
GH
304 if (req->dev->type == TYPE_TAPE) {
305 /* force IMMED, otherwise qemu waits end of command */
306 req->cmd.buf[1] = 0x01;
307 }
a9dd6843 308 break;
a9dd6843 309 }
a9dd6843
AL
310}
311
2cc977e2
TS
312/* Execute a scsi command. Returns the length of the data expected by the
313 command. This will be Positive for data transfers from the device
314 (eg. disk reads), negative for transfers to the device (eg. disk writes),
315 and zero if the command does not transfer any data. */
316
5c6c0e51 317static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
2cc977e2 318{
5c6c0e51
HR
319 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
320 SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
2cc977e2
TS
321 int ret;
322
5c6c0e51 323 scsi_req_enqueue(req);
89c0f643 324 if (cmd[0] != REQUEST_SENSE &&
5c6c0e51
HR
325 (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
326 DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
a1f0cce2 327 scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
2b8b3bb9
PB
328 r->req.status = CHECK_CONDITION;
329 scsi_req_complete(&r->req);
89c0f643
AJ
330 return 0;
331 }
332
2ec749cb
GH
333 if (-1 == scsi_req_parse(&r->req, cmd)) {
334 BADF("Unsupported command length, command %x\n", cmd[0]);
a1f0cce2 335 scsi_command_complete(r, -EINVAL);
2ec749cb
GH
336 return 0;
337 }
338 scsi_req_fixup(&r->req);
339
aa2b1e89
BK
340 DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
341 r->req.cmd.xfer, cmd[0]);
342
343#ifdef DEBUG_SCSI
344 {
345 int i;
346 for (i = 1; i < r->req.cmd.len; i++) {
347 printf(" 0x%02x", cmd[i]);
348 }
349 printf("\n");
350 }
351#endif
2cc977e2 352
2ec749cb 353 if (r->req.cmd.xfer == 0) {
2cc977e2 354 if (r->buf != NULL)
e3c916e6 355 qemu_free(r->buf);
2cc977e2
TS
356 r->buflen = 0;
357 r->buf = NULL;
428c149b 358 ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
a1f0cce2
HR
359 if (ret < 0) {
360 scsi_command_complete(r, ret);
361 return 0;
2cc977e2
TS
362 }
363 return 0;
364 }
365
2ec749cb 366 if (r->buflen != r->req.cmd.xfer) {
2cc977e2 367 if (r->buf != NULL)
e3c916e6 368 qemu_free(r->buf);
2ec749cb
GH
369 r->buf = qemu_malloc(r->req.cmd.xfer);
370 r->buflen = r->req.cmd.xfer;
2cc977e2
TS
371 }
372
373 memset(r->buf, 0, r->buflen);
2ec749cb 374 r->len = r->req.cmd.xfer;
97a06435 375 if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
2cc977e2 376 r->len = 0;
5c6c0e51 377 return -r->req.cmd.xfer;
ad2d30f7 378 } else {
5c6c0e51 379 return r->req.cmd.xfer;
2cc977e2 380 }
2cc977e2
TS
381}
382
383static int get_blocksize(BlockDriverState *bdrv)
384{
385 uint8_t cmd[10];
386 uint8_t buf[8];
387 uint8_t sensebuf[8];
388 sg_io_hdr_t io_header;
389 int ret;
390
4f26a486
AL
391 memset(cmd, 0, sizeof(cmd));
392 memset(buf, 0, sizeof(buf));
2cc977e2
TS
393 cmd[0] = READ_CAPACITY;
394
395 memset(&io_header, 0, sizeof(io_header));
396 io_header.interface_id = 'S';
397 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
398 io_header.dxfer_len = sizeof(buf);
399 io_header.dxferp = buf;
400 io_header.cmdp = cmd;
401 io_header.cmd_len = sizeof(cmd);
402 io_header.mx_sb_len = sizeof(sensebuf);
403 io_header.sbp = sensebuf;
404 io_header.timeout = 6000; /* XXX */
405
221f715d 406 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 407 if (ret < 0)
2cc977e2
TS
408 return -1;
409
410 return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
411}
412
89c0f643
AJ
413static int get_stream_blocksize(BlockDriverState *bdrv)
414{
415 uint8_t cmd[6];
416 uint8_t buf[12];
417 uint8_t sensebuf[8];
418 sg_io_hdr_t io_header;
419 int ret;
420
421 memset(cmd, 0, sizeof(cmd));
422 memset(buf, 0, sizeof(buf));
423 cmd[0] = MODE_SENSE;
424 cmd[4] = sizeof(buf);
425
426 memset(&io_header, 0, sizeof(io_header));
427 io_header.interface_id = 'S';
428 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
429 io_header.dxfer_len = sizeof(buf);
430 io_header.dxferp = buf;
431 io_header.cmdp = cmd;
432 io_header.cmd_len = sizeof(cmd);
433 io_header.mx_sb_len = sizeof(sensebuf);
434 io_header.sbp = sensebuf;
435 io_header.timeout = 6000; /* XXX */
436
221f715d 437 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 438 if (ret < 0)
89c0f643
AJ
439 return -1;
440
441 return (buf[9] << 16) | (buf[10] << 8) | buf[11];
442}
443
f8b6d672
BK
444static void scsi_generic_reset(DeviceState *dev)
445{
446 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
447
c557e889 448 scsi_device_purge_requests(&s->qdev);
f8b6d672
BK
449}
450
451static void scsi_destroy(SCSIDevice *d)
452{
453 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
454
c557e889 455 scsi_device_purge_requests(&s->qdev);
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),
f8b6d672 529 .qdev.reset = scsi_generic_reset,
d52affa7
GH
530 .init = scsi_generic_initfn,
531 .destroy = scsi_destroy,
5c6c0e51 532 .alloc_req = scsi_new_request,
ad2d30f7 533 .free_req = scsi_free_request,
d52affa7
GH
534 .send_command = scsi_send_command,
535 .read_data = scsi_read_data,
536 .write_data = scsi_write_data,
537 .cancel_io = scsi_cancel_io,
538 .get_buf = scsi_get_buf,
539 .qdev.props = (Property[]) {
428c149b 540 DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
d52affa7
GH
541 DEFINE_PROP_END_OF_LIST(),
542 },
543};
2cc977e2 544
d52affa7
GH
545static void scsi_generic_register_devices(void)
546{
547 scsi_qdev_register(&scsi_generic_info);
2cc977e2 548}
d52affa7
GH
549device_init(scsi_generic_register_devices)
550
2cc977e2 551#endif /* __linux__ */