]> git.proxmox.com Git - qemu.git/blame - hw/scsi-generic.c
usb: Pass the packet to the device's handle_control callback
[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)
f0171327 99 r->req.status = BUSY;
2cc977e2
TS
100 else {
101 if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
f0171327 102 r->req.status = BUSY;
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)
f0171327 107 r->req.status = CHECK_CONDITION;
2cc977e2 108 else
f0171327 109 r->req.status = GOOD;
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) {
aa2b1e89 167 DPRINTF("IO error ret %d\n", ret);
2cc977e2
TS
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];
aa2b1e89 239 DPRINTF("block size %d\n", s->qdev.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 335 bus = scsi_bus_from_device(d);
f0171327 336 bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
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
aa2b1e89
BK
354 DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
355 r->req.cmd.xfer, cmd[0]);
356
357#ifdef DEBUG_SCSI
358 {
359 int i;
360 for (i = 1; i < r->req.cmd.len; i++) {
361 printf(" 0x%02x", cmd[i]);
362 }
363 printf("\n");
364 }
365#endif
2cc977e2 366
2ec749cb 367 if (r->req.cmd.xfer == 0) {
2cc977e2 368 if (r->buf != NULL)
e3c916e6 369 qemu_free(r->buf);
2cc977e2
TS
370 r->buflen = 0;
371 r->buf = NULL;
428c149b 372 ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
2cc977e2
TS
373 if (ret == -1) {
374 scsi_command_complete(r, -EINVAL);
375 return 0;
376 }
377 return 0;
378 }
379
2ec749cb 380 if (r->buflen != r->req.cmd.xfer) {
2cc977e2 381 if (r->buf != NULL)
e3c916e6 382 qemu_free(r->buf);
2ec749cb
GH
383 r->buf = qemu_malloc(r->req.cmd.xfer);
384 r->buflen = r->req.cmd.xfer;
2cc977e2
TS
385 }
386
387 memset(r->buf, 0, r->buflen);
2ec749cb 388 r->len = r->req.cmd.xfer;
97a06435 389 if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
2cc977e2 390 r->len = 0;
2ec749cb 391 return -r->req.cmd.xfer;
2cc977e2
TS
392 }
393
2ec749cb 394 return r->req.cmd.xfer;
2cc977e2
TS
395}
396
397static int get_blocksize(BlockDriverState *bdrv)
398{
399 uint8_t cmd[10];
400 uint8_t buf[8];
401 uint8_t sensebuf[8];
402 sg_io_hdr_t io_header;
403 int ret;
404
4f26a486
AL
405 memset(cmd, 0, sizeof(cmd));
406 memset(buf, 0, sizeof(buf));
2cc977e2
TS
407 cmd[0] = READ_CAPACITY;
408
409 memset(&io_header, 0, sizeof(io_header));
410 io_header.interface_id = 'S';
411 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
412 io_header.dxfer_len = sizeof(buf);
413 io_header.dxferp = buf;
414 io_header.cmdp = cmd;
415 io_header.cmd_len = sizeof(cmd);
416 io_header.mx_sb_len = sizeof(sensebuf);
417 io_header.sbp = sensebuf;
418 io_header.timeout = 6000; /* XXX */
419
221f715d 420 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 421 if (ret < 0)
2cc977e2
TS
422 return -1;
423
424 return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
425}
426
89c0f643
AJ
427static int get_stream_blocksize(BlockDriverState *bdrv)
428{
429 uint8_t cmd[6];
430 uint8_t buf[12];
431 uint8_t sensebuf[8];
432 sg_io_hdr_t io_header;
433 int ret;
434
435 memset(cmd, 0, sizeof(cmd));
436 memset(buf, 0, sizeof(buf));
437 cmd[0] = MODE_SENSE;
438 cmd[4] = sizeof(buf);
439
440 memset(&io_header, 0, sizeof(io_header));
441 io_header.interface_id = 'S';
442 io_header.dxfer_direction = SG_DXFER_FROM_DEV;
443 io_header.dxfer_len = sizeof(buf);
444 io_header.dxferp = buf;
445 io_header.cmdp = cmd;
446 io_header.cmd_len = sizeof(cmd);
447 io_header.mx_sb_len = sizeof(sensebuf);
448 io_header.sbp = sensebuf;
449 io_header.timeout = 6000; /* XXX */
450
221f715d 451 ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
7d780669 452 if (ret < 0)
89c0f643
AJ
453 return -1;
454
455 return (buf[9] << 16) | (buf[10] << 8) | buf[11];
456}
457
f8b6d672 458static void scsi_generic_purge_requests(SCSIGenericState *s)
2cc977e2 459{
9af99d98 460 SCSIGenericReq *r;
2cc977e2 461
9af99d98
GH
462 while (!QTAILQ_EMPTY(&s->qdev.requests)) {
463 r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
f8b6d672
BK
464 if (r->req.aiocb) {
465 bdrv_aio_cancel(r->req.aiocb);
466 }
9af99d98 467 scsi_remove_request(r);
2cc977e2 468 }
f8b6d672
BK
469}
470
471static void scsi_generic_reset(DeviceState *dev)
472{
473 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
474
475 scsi_generic_purge_requests(s);
476}
477
478static void scsi_destroy(SCSIDevice *d)
479{
480 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
481
482 scsi_generic_purge_requests(s);
f8b6cc00 483 blockdev_mark_auto_del(s->qdev.conf.bs);
2cc977e2
TS
484}
485
d52affa7 486static int scsi_generic_initfn(SCSIDevice *dev)
2cc977e2 487{
d52affa7 488 SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
2cc977e2 489 int sg_version;
2cc977e2
TS
490 struct sg_scsi_id scsiid;
491
f8b6cc00 492 if (!s->qdev.conf.bs) {
1ecda02b 493 error_report("scsi-generic: drive property not set");
d52affa7
GH
494 return -1;
495 }
f8b6cc00 496 s->bs = s->qdev.conf.bs;
2cc977e2 497
d52affa7 498 /* check we are really using a /dev/sg* file */
428c149b 499 if (!bdrv_is_sg(s->bs)) {
1ecda02b 500 error_report("scsi-generic: not /dev/sg*");
d52affa7
GH
501 return -1;
502 }
2cc977e2 503
620f862e
MA
504 if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
505 error_report("Device doesn't support drive option werror");
506 return -1;
507 }
508 if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
509 error_report("Device doesn't support drive option rerror");
510 return -1;
511 }
512
2cc977e2 513 /* check we are using a driver managing SG_IO (version 3 and after */
428c149b 514 if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
d52affa7 515 sg_version < 30000) {
1ecda02b 516 error_report("scsi-generic: scsi generic interface too old");
d52affa7
GH
517 return -1;
518 }
2cc977e2
TS
519
520 /* get LUN of the /dev/sg? */
428c149b 521 if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) {
1ecda02b 522 error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed");
d52affa7
GH
523 return -1;
524 }
2cc977e2
TS
525
526 /* define device state */
2cc977e2 527 s->lun = scsiid.lun;
89c0f643 528 DPRINTF("LUN %d\n", s->lun);
91376656
GH
529 s->qdev.type = scsiid.scsi_type;
530 DPRINTF("device type %d\n", s->qdev.type);
531 if (s->qdev.type == TYPE_TAPE) {
428c149b 532 s->qdev.blocksize = get_stream_blocksize(s->bs);
b07995e3
GH
533 if (s->qdev.blocksize == -1)
534 s->qdev.blocksize = 0;
89c0f643 535 } else {
428c149b 536 s->qdev.blocksize = get_blocksize(s->bs);
89c0f643 537 /* removable media returns 0 if not present */
b07995e3 538 if (s->qdev.blocksize <= 0) {
91376656 539 if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM)
b07995e3 540 s->qdev.blocksize = 2048;
89c0f643 541 else
b07995e3 542 s->qdev.blocksize = 512;
89c0f643
AJ
543 }
544 }
b07995e3 545 DPRINTF("block size %d\n", s->qdev.blocksize);
2cc977e2
TS
546 s->driver_status = 0;
547 memset(s->sensebuf, 0, sizeof(s->sensebuf));
7d0d6950 548 bdrv_set_removable(s->bs, 0);
d52affa7
GH
549 return 0;
550}
2cc977e2 551
d52affa7
GH
552static SCSIDeviceInfo scsi_generic_info = {
553 .qdev.name = "scsi-generic",
554 .qdev.desc = "pass through generic scsi device (/dev/sg*)",
555 .qdev.size = sizeof(SCSIGenericState),
f8b6d672 556 .qdev.reset = scsi_generic_reset,
d52affa7
GH
557 .init = scsi_generic_initfn,
558 .destroy = scsi_destroy,
559 .send_command = scsi_send_command,
560 .read_data = scsi_read_data,
561 .write_data = scsi_write_data,
562 .cancel_io = scsi_cancel_io,
563 .get_buf = scsi_get_buf,
564 .qdev.props = (Property[]) {
428c149b 565 DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
d52affa7
GH
566 DEFINE_PROP_END_OF_LIST(),
567 },
568};
2cc977e2 569
d52affa7
GH
570static void scsi_generic_register_devices(void)
571{
572 scsi_qdev_register(&scsi_generic_info);
2cc977e2 573}
d52affa7
GH
574device_init(scsi_generic_register_devices)
575
2cc977e2 576#endif /* __linux__ */