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