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