blob: 3d3946a9debdb60221d29e29ccf0907bba4a037d [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/*
2 * SCSI Device emulation
3 *
4 * Copyright (c) 2006 CodeSourcery.
5 * Based on code by Fabrice Bellard
6 *
7 * Written by Paul Brook
8 *
9 * This code is licenced under the LGPL.
10 *
11 * Note that this file only handles the SCSI architecture model and device
12 * commands. Emulation of interface/link layer protocols is handled by
13 * the host adapter emulator.
14 */
15
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070016#include <qemu-common.h>
17#include <sysemu.h>
David 'Digit' Turnercb42a1b2010-12-23 02:54:08 +010018#include "blockdev.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080019//#define DEBUG_SCSI
20
21#ifdef DEBUG_SCSI
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070022#define DPRINTF(fmt, ...) \
23do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080024#else
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070025#define DPRINTF(fmt, ...) do {} while(0)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080026#endif
27
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070028#define BADF(fmt, ...) \
29do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080030
31#include "qemu-common.h"
32#include "block.h"
33#include "scsi-disk.h"
34
35#define SENSE_NO_SENSE 0
36#define SENSE_NOT_READY 2
37#define SENSE_HARDWARE_ERROR 4
38#define SENSE_ILLEGAL_REQUEST 5
39
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070040#define STATUS_GOOD 0
41#define STATUS_CHECK_CONDITION 2
42
43#define SCSI_DMA_BUF_SIZE 131072
44#define SCSI_MAX_INQUIRY_LEN 256
45
46#define SCSI_REQ_STATUS_RETRY 0x01
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080047
48typedef struct SCSIRequest {
49 SCSIDeviceState *dev;
50 uint32_t tag;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070051 /* ??? We should probably keep track of whether the data transfer is
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080052 a read or a write. Currently we rely on the host getting it right. */
53 /* Both sector and sector_count are in terms of qemu 512 byte blocks. */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070054 uint64_t sector;
55 uint32_t sector_count;
56 struct iovec iov;
57 QEMUIOVector qiov;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080058 BlockDriverAIOCB *aiocb;
59 struct SCSIRequest *next;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070060 uint32_t status;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080061} SCSIRequest;
62
63struct SCSIDeviceState
64{
65 BlockDriverState *bdrv;
66 SCSIRequest *requests;
67 /* The qemu block layer uses a fixed 512 byte sector size.
68 This is the number of 512 byte blocks in a single scsi sector. */
69 int cluster_size;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070070 uint64_t max_lba;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080071 int sense;
72 int tcq;
73 /* Completion functions may be called from either scsi_{read,write}_data
74 or from the AIO completion routines. */
75 scsi_completionfn completion;
76 void *opaque;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070077 char drive_serial_str[21];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080078};
79
80/* Global pool of SCSIRequest structures. */
81static SCSIRequest *free_requests = NULL;
82
83static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
84{
85 SCSIRequest *r;
86
87 if (free_requests) {
88 r = free_requests;
89 free_requests = r->next;
90 } else {
91 r = qemu_malloc(sizeof(SCSIRequest));
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070092 r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080093 }
94 r->dev = s;
95 r->tag = tag;
96 r->sector_count = 0;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070097 r->iov.iov_len = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080098 r->aiocb = NULL;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070099 r->status = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800100
101 r->next = s->requests;
102 s->requests = r;
103 return r;
104}
105
106static void scsi_remove_request(SCSIRequest *r)
107{
108 SCSIRequest *last;
109 SCSIDeviceState *s = r->dev;
110
111 if (s->requests == r) {
112 s->requests = r->next;
113 } else {
114 last = s->requests;
115 while (last && last->next != r)
116 last = last->next;
117 if (last) {
118 last->next = r->next;
119 } else {
120 BADF("Orphaned request\n");
121 }
122 }
123 r->next = free_requests;
124 free_requests = r;
125}
126
127static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
128{
129 SCSIRequest *r;
130
131 r = s->requests;
132 while (r && r->tag != tag)
133 r = r->next;
134
135 return r;
136}
137
138/* Helper function for command completion. */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700139static void scsi_command_complete(SCSIRequest *r, int status, int sense)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800140{
141 SCSIDeviceState *s = r->dev;
142 uint32_t tag;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700143 DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800144 s->sense = sense;
145 tag = r->tag;
146 scsi_remove_request(r);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700147 s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800148}
149
150/* Cancel a pending data transfer. */
151static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
152{
153 SCSIDeviceState *s = d->state;
154 SCSIRequest *r;
155 DPRINTF("Cancel tag=0x%x\n", tag);
156 r = scsi_find_request(s, tag);
157 if (r) {
158 if (r->aiocb)
159 bdrv_aio_cancel(r->aiocb);
160 r->aiocb = NULL;
161 scsi_remove_request(r);
162 }
163}
164
165static void scsi_read_complete(void * opaque, int ret)
166{
167 SCSIRequest *r = (SCSIRequest *)opaque;
168 SCSIDeviceState *s = r->dev;
169
170 if (ret) {
171 DPRINTF("IO error\n");
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700172 s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 0);
173 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800174 return;
175 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700176 DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, r->iov.iov_len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800177
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700178 s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800179}
180
181/* Read more data from scsi device into buffer. */
182static void scsi_read_data(SCSIDevice *d, uint32_t tag)
183{
184 SCSIDeviceState *s = d->state;
185 SCSIRequest *r;
186 uint32_t n;
187
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. */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700192 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800193 return;
194 }
195 if (r->sector_count == (uint32_t)-1) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700196 DPRINTF("Read buf_len=%d\n", r->iov.iov_len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800197 r->sector_count = 0;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700198 s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800199 return;
200 }
201 DPRINTF("Read sector_count=%d\n", r->sector_count);
202 if (r->sector_count == 0) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700203 scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800204 return;
205 }
206
207 n = r->sector_count;
208 if (n > SCSI_DMA_BUF_SIZE / 512)
209 n = SCSI_DMA_BUF_SIZE / 512;
210
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700211 r->iov.iov_len = n * 512;
212 qemu_iovec_init_external(&r->qiov, &r->iov, 1);
213 r->aiocb = bdrv_aio_readv(s->bdrv, r->sector, &r->qiov, n,
214 scsi_read_complete, r);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800215 if (r->aiocb == NULL)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700216 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800217 r->sector += n;
218 r->sector_count -= n;
219}
220
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700221static int scsi_handle_write_error(SCSIRequest *r, int error)
222{
David 'Digit' Turnercb42a1b2010-12-23 02:54:08 +0100223 BlockErrorAction action = bdrv_get_on_error(r->dev->bdrv, 0);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700224
225 if (action == BLOCK_ERR_IGNORE)
226 return 0;
227
228 if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
229 || action == BLOCK_ERR_STOP_ANY) {
230 r->status |= SCSI_REQ_STATUS_RETRY;
231 vm_stop(0);
232 } else {
233 scsi_command_complete(r, STATUS_CHECK_CONDITION,
234 SENSE_HARDWARE_ERROR);
235 }
236
237 return 1;
238}
239
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800240static void scsi_write_complete(void * opaque, int ret)
241{
242 SCSIRequest *r = (SCSIRequest *)opaque;
243 SCSIDeviceState *s = r->dev;
244 uint32_t len;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700245 uint32_t n;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800246
247 r->aiocb = NULL;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700248
249 if (ret) {
250 if (scsi_handle_write_error(r, -ret))
251 return;
252 }
253
254 n = r->iov.iov_len / 512;
255 r->sector += n;
256 r->sector_count -= n;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800257 if (r->sector_count == 0) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700258 scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800259 } else {
260 len = r->sector_count * 512;
261 if (len > SCSI_DMA_BUF_SIZE) {
262 len = SCSI_DMA_BUF_SIZE;
263 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700264 r->iov.iov_len = len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800265 DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
266 s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
267 }
268}
269
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700270static void scsi_write_request(SCSIRequest *r)
271{
272 SCSIDeviceState *s = r->dev;
273 uint32_t n;
274
275 n = r->iov.iov_len / 512;
276 if (n) {
277 qemu_iovec_init_external(&r->qiov, &r->iov, 1);
278 r->aiocb = bdrv_aio_writev(s->bdrv, r->sector, &r->qiov, n,
279 scsi_write_complete, r);
280 if (r->aiocb == NULL)
281 scsi_command_complete(r, STATUS_CHECK_CONDITION,
282 SENSE_HARDWARE_ERROR);
283 } else {
284 /* Invoke completion routine to fetch data from host. */
285 scsi_write_complete(r, 0);
286 }
287}
288
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800289/* Write data to a scsi device. Returns nonzero on failure.
290 The transfer may complete asynchronously. */
291static int scsi_write_data(SCSIDevice *d, uint32_t tag)
292{
293 SCSIDeviceState *s = d->state;
294 SCSIRequest *r;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800295
296 DPRINTF("Write data tag=0x%x\n", tag);
297 r = scsi_find_request(s, tag);
298 if (!r) {
299 BADF("Bad write tag 0x%x\n", tag);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700300 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800301 return 1;
302 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700303
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800304 if (r->aiocb)
305 BADF("Data transfer already in progress\n");
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700306
307 scsi_write_request(r);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800308
309 return 0;
310}
311
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700312static void scsi_dma_restart_cb(void *opaque, int running, int reason)
313{
314 SCSIDeviceState *s = opaque;
315 SCSIRequest *r = s->requests;
316 if (!running)
317 return;
318
319 while (r) {
320 if (r->status & SCSI_REQ_STATUS_RETRY) {
321 r->status &= ~SCSI_REQ_STATUS_RETRY;
David 'Digit' Turnercb42a1b2010-12-23 02:54:08 +0100322 scsi_write_request(r);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700323 }
324 r = r->next;
325 }
326}
327
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800328/* Return a pointer to the data buffer. */
329static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
330{
331 SCSIDeviceState *s = d->state;
332 SCSIRequest *r;
333
334 r = scsi_find_request(s, tag);
335 if (!r) {
336 BADF("Bad buffer tag 0x%x\n", tag);
337 return NULL;
338 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700339 return (uint8_t *)r->iov.iov_base;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800340}
341
342/* Execute a scsi command. Returns the length of the data expected by the
343 command. This will be Positive for data transfers from the device
344 (eg. disk reads), negative for transfers to the device (eg. disk writes),
345 and zero if the command does not transfer any data. */
346
347static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
348 uint8_t *buf, int lun)
349{
350 SCSIDeviceState *s = d->state;
351 uint64_t nb_sectors;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700352 uint64_t lba;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800353 uint32_t len;
354 int cmdlen;
355 int is_write;
356 uint8_t command;
357 uint8_t *outbuf;
358 SCSIRequest *r;
359
360 command = buf[0];
361 r = scsi_find_request(s, tag);
362 if (r) {
363 BADF("Tag 0x%x already in use\n", tag);
364 scsi_cancel_io(d, tag);
365 }
366 /* ??? Tags are not unique for different luns. We only implement a
367 single lun, so this should not matter. */
368 r = scsi_new_request(s, tag);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700369 outbuf = (uint8_t *)r->iov.iov_base;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800370 is_write = 0;
371 DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
372 switch (command >> 5) {
373 case 0:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700374 lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) |
375 (((uint64_t) buf[1] & 0x1f) << 16);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800376 len = buf[4];
377 cmdlen = 6;
378 break;
379 case 1:
380 case 2:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700381 lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
382 ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800383 len = buf[8] | (buf[7] << 8);
384 cmdlen = 10;
385 break;
386 case 4:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700387 lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) |
388 ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) |
389 ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) |
390 ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800391 len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
392 cmdlen = 16;
393 break;
394 case 5:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700395 lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
396 ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800397 len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
398 cmdlen = 12;
399 break;
400 default:
401 BADF("Unsupported command length, command %x\n", command);
402 goto fail;
403 }
404#ifdef DEBUG_SCSI
405 {
406 int i;
407 for (i = 1; i < cmdlen; i++) {
408 printf(" 0x%02x", buf[i]);
409 }
410 printf("\n");
411 }
412#endif
413 if (lun || buf[1] >> 5) {
414 /* Only LUN 0 supported. */
415 DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700416 if (command != 0x03 && command != 0x12) /* REQUEST SENSE and INQUIRY */
417 goto fail;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800418 }
419 switch (command) {
420 case 0x0:
421 DPRINTF("Test Unit Ready\n");
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700422 if (!bdrv_is_inserted(s->bdrv))
423 goto notready;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800424 break;
425 case 0x03:
426 DPRINTF("Request Sense (len %d)\n", len);
427 if (len < 4)
428 goto fail;
429 memset(outbuf, 0, 4);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700430 r->iov.iov_len = 4;
431 if (s->sense == SENSE_NOT_READY && len >= 18) {
432 memset(outbuf, 0, 18);
433 r->iov.iov_len = 18;
434 outbuf[7] = 10;
435 /* asc 0x3a, ascq 0: Medium not present */
436 outbuf[12] = 0x3a;
437 outbuf[13] = 0;
438 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800439 outbuf[0] = 0xf0;
440 outbuf[1] = 0;
441 outbuf[2] = s->sense;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800442 break;
443 case 0x12:
444 DPRINTF("Inquiry (len %d)\n", len);
445 if (buf[1] & 0x2) {
446 /* Command support data - optional, not implemented */
447 BADF("optional INQUIRY command support request not implemented\n");
448 goto fail;
449 }
450 else if (buf[1] & 0x1) {
451 /* Vital product data */
452 uint8_t page_code = buf[2];
453 if (len < 4) {
454 BADF("Error: Inquiry (EVPD[%02X]) buffer size %d is "
455 "less than 4\n", page_code, len);
456 goto fail;
457 }
458
459 switch (page_code) {
460 case 0x00:
461 {
462 /* Supported page codes, mandatory */
463 DPRINTF("Inquiry EVPD[Supported pages] "
464 "buffer size %d\n", len);
465
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700466 r->iov.iov_len = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800467
468 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700469 outbuf[r->iov.iov_len++] = 5;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800470 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700471 outbuf[r->iov.iov_len++] = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800472 }
473
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700474 outbuf[r->iov.iov_len++] = 0x00; // this page
475 outbuf[r->iov.iov_len++] = 0x00;
476 outbuf[r->iov.iov_len++] = 3; // number of pages
477 outbuf[r->iov.iov_len++] = 0x00; // list of supported pages (this page)
478 outbuf[r->iov.iov_len++] = 0x80; // unit serial number
479 outbuf[r->iov.iov_len++] = 0x83; // device identification
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800480 }
481 break;
482 case 0x80:
483 {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700484 int l;
485
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800486 /* Device serial number, optional */
487 if (len < 4) {
488 BADF("Error: EVPD[Serial number] Inquiry buffer "
489 "size %d too small, %d needed\n", len, 4);
490 goto fail;
491 }
492
493 DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700494 l = MIN(len, strlen(s->drive_serial_str));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800495
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700496 r->iov.iov_len = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800497
498 /* Supported page codes */
499 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700500 outbuf[r->iov.iov_len++] = 5;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800501 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700502 outbuf[r->iov.iov_len++] = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800503 }
504
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700505 outbuf[r->iov.iov_len++] = 0x80; // this page
506 outbuf[r->iov.iov_len++] = 0x00;
507 outbuf[r->iov.iov_len++] = l;
508 memcpy(&outbuf[r->iov.iov_len], s->drive_serial_str, l);
509 r->iov.iov_len += l;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800510 }
511
512 break;
513 case 0x83:
514 {
515 /* Device identification page, mandatory */
516 int max_len = 255 - 8;
517 int id_len = strlen(bdrv_get_device_name(s->bdrv));
518 if (id_len > max_len)
519 id_len = max_len;
520
521 DPRINTF("Inquiry EVPD[Device identification] "
522 "buffer size %d\n", len);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700523 r->iov.iov_len = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800524 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700525 outbuf[r->iov.iov_len++] = 5;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800526 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700527 outbuf[r->iov.iov_len++] = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800528 }
529
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700530 outbuf[r->iov.iov_len++] = 0x83; // this page
531 outbuf[r->iov.iov_len++] = 0x00;
532 outbuf[r->iov.iov_len++] = 3 + id_len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800533
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700534 outbuf[r->iov.iov_len++] = 0x2; // ASCII
535 outbuf[r->iov.iov_len++] = 0; // not officially assigned
536 outbuf[r->iov.iov_len++] = 0; // reserved
537 outbuf[r->iov.iov_len++] = id_len; // length of data following
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800538
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700539 memcpy(&outbuf[r->iov.iov_len],
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800540 bdrv_get_device_name(s->bdrv), id_len);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700541 r->iov.iov_len += id_len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800542 }
543 break;
544 default:
545 BADF("Error: unsupported Inquiry (EVPD[%02X]) "
546 "buffer size %d\n", page_code, len);
547 goto fail;
548 }
549 /* done with EVPD */
550 break;
551 }
552 else {
553 /* Standard INQUIRY data */
554 if (buf[2] != 0) {
555 BADF("Error: Inquiry (STANDARD) page or code "
556 "is non-zero [%02X]\n", buf[2]);
557 goto fail;
558 }
559
560 /* PAGE CODE == 0 */
561 if (len < 5) {
562 BADF("Error: Inquiry (STANDARD) buffer size %d "
563 "is less than 5\n", len);
564 goto fail;
565 }
566
567 if (len < 36) {
568 BADF("Error: Inquiry (STANDARD) buffer size %d "
569 "is less than 36 (TODO: only 5 required)\n", len);
570 }
571 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700572
573 if(len > SCSI_MAX_INQUIRY_LEN)
574 len = SCSI_MAX_INQUIRY_LEN;
575
576 memset(outbuf, 0, len);
577
578 if (lun || buf[1] >> 5) {
579 outbuf[0] = 0x7f; /* LUN not supported */
580 } else if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800581 outbuf[0] = 5;
582 outbuf[1] = 0x80;
583 memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
584 } else {
585 outbuf[0] = 0;
586 memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
587 }
588 memcpy(&outbuf[8], "QEMU ", 8);
589 memcpy(&outbuf[32], QEMU_VERSION, 4);
590 /* Identify device as SCSI-3 rev 1.
591 Some later commands are also implemented. */
592 outbuf[2] = 3;
593 outbuf[3] = 2; /* Format 2 */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700594 outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800595 /* Sync data transfer and TCQ. */
596 outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700597 r->iov.iov_len = len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800598 break;
599 case 0x16:
600 DPRINTF("Reserve(6)\n");
601 if (buf[1] & 1)
602 goto fail;
603 break;
604 case 0x17:
605 DPRINTF("Release(6)\n");
606 if (buf[1] & 1)
607 goto fail;
608 break;
609 case 0x1a:
610 case 0x5a:
611 {
612 uint8_t *p;
613 int page;
614
615 page = buf[2] & 0x3f;
616 DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
617 p = outbuf;
618 memset(p, 0, 4);
619 outbuf[1] = 0; /* Default media type. */
620 outbuf[3] = 0; /* Block descriptor length. */
621 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
622 outbuf[2] = 0x80; /* Readonly. */
623 }
624 p += 4;
625 if (page == 4) {
626 int cylinders, heads, secs;
627
628 /* Rigid disk device geometry page. */
629 p[0] = 4;
630 p[1] = 0x16;
631 /* if a geometry hint is available, use it */
632 bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
633 p[2] = (cylinders >> 16) & 0xff;
634 p[3] = (cylinders >> 8) & 0xff;
635 p[4] = cylinders & 0xff;
636 p[5] = heads & 0xff;
637 /* Write precomp start cylinder, disabled */
638 p[6] = (cylinders >> 16) & 0xff;
639 p[7] = (cylinders >> 8) & 0xff;
640 p[8] = cylinders & 0xff;
641 /* Reduced current start cylinder, disabled */
642 p[9] = (cylinders >> 16) & 0xff;
643 p[10] = (cylinders >> 8) & 0xff;
644 p[11] = cylinders & 0xff;
645 /* Device step rate [ns], 200ns */
646 p[12] = 0;
647 p[13] = 200;
648 /* Landing zone cylinder */
649 p[14] = 0xff;
650 p[15] = 0xff;
651 p[16] = 0xff;
652 /* Medium rotation rate [rpm], 5400 rpm */
653 p[20] = (5400 >> 8) & 0xff;
654 p[21] = 5400 & 0xff;
655 p += 0x16;
656 } else if (page == 5) {
657 int cylinders, heads, secs;
658
659 /* Flexible disk device geometry page. */
660 p[0] = 5;
661 p[1] = 0x1e;
662 /* Transfer rate [kbit/s], 5Mbit/s */
663 p[2] = 5000 >> 8;
664 p[3] = 5000 & 0xff;
665 /* if a geometry hint is available, use it */
666 bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
667 p[4] = heads & 0xff;
668 p[5] = secs & 0xff;
669 p[6] = s->cluster_size * 2;
670 p[8] = (cylinders >> 8) & 0xff;
671 p[9] = cylinders & 0xff;
672 /* Write precomp start cylinder, disabled */
673 p[10] = (cylinders >> 8) & 0xff;
674 p[11] = cylinders & 0xff;
675 /* Reduced current start cylinder, disabled */
676 p[12] = (cylinders >> 8) & 0xff;
677 p[13] = cylinders & 0xff;
678 /* Device step rate [100us], 100us */
679 p[14] = 0;
680 p[15] = 1;
681 /* Device step pulse width [us], 1us */
682 p[16] = 1;
683 /* Device head settle delay [100us], 100us */
684 p[17] = 0;
685 p[18] = 1;
686 /* Motor on delay [0.1s], 0.1s */
687 p[19] = 1;
688 /* Motor off delay [0.1s], 0.1s */
689 p[20] = 1;
690 /* Medium rotation rate [rpm], 5400 rpm */
691 p[28] = (5400 >> 8) & 0xff;
692 p[29] = 5400 & 0xff;
693 p += 0x1e;
694 } else if ((page == 8 || page == 0x3f)) {
695 /* Caching page. */
696 memset(p,0,20);
697 p[0] = 8;
698 p[1] = 0x12;
699 p[2] = 4; /* WCE */
700 p += 20;
701 }
702 if ((page == 0x3f || page == 0x2a)
703 && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
704 /* CD Capabilities and Mechanical Status page. */
705 p[0] = 0x2a;
706 p[1] = 0x14;
707 p[2] = 3; // CD-R & CD-RW read
708 p[3] = 0; // Writing not supported
709 p[4] = 0x7f; /* Audio, composite, digital out,
710 mode 2 form 1&2, multi session */
711 p[5] = 0xff; /* CD DA, DA accurate, RW supported,
712 RW corrected, C2 errors, ISRC,
713 UPC, Bar code */
714 p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
715 /* Locking supported, jumper present, eject, tray */
716 p[7] = 0; /* no volume & mute control, no
717 changer */
718 p[8] = (50 * 176) >> 8; // 50x read speed
719 p[9] = (50 * 176) & 0xff;
720 p[10] = 0 >> 8; // No volume
721 p[11] = 0 & 0xff;
722 p[12] = 2048 >> 8; // 2M buffer
723 p[13] = 2048 & 0xff;
724 p[14] = (16 * 176) >> 8; // 16x read speed current
725 p[15] = (16 * 176) & 0xff;
726 p[18] = (16 * 176) >> 8; // 16x write speed
727 p[19] = (16 * 176) & 0xff;
728 p[20] = (16 * 176) >> 8; // 16x write speed current
729 p[21] = (16 * 176) & 0xff;
730 p += 22;
731 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700732 r->iov.iov_len = p - outbuf;
733 outbuf[0] = r->iov.iov_len - 4;
734 if (r->iov.iov_len > len)
735 r->iov.iov_len = len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800736 }
737 break;
738 case 0x1b:
739 DPRINTF("Start Stop Unit\n");
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700740 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM &&
741 (buf[4] & 2))
742 /* load/eject medium */
743 bdrv_eject(s->bdrv, !(buf[4] & 1));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800744 break;
745 case 0x1e:
746 DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
747 bdrv_set_locked(s->bdrv, buf[4] & 1);
748 break;
749 case 0x25:
750 DPRINTF("Read Capacity\n");
751 /* The normal LEN field for this command is zero. */
752 memset(outbuf, 0, 8);
753 bdrv_get_geometry(s->bdrv, &nb_sectors);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700754 nb_sectors /= s->cluster_size;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800755 /* Returned value is the address of the last sector. */
756 if (nb_sectors) {
757 nb_sectors--;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700758 /* Remember the new size for read/write sanity checking. */
759 s->max_lba = nb_sectors;
760 /* Clip to 2TB, instead of returning capacity modulo 2TB. */
761 if (nb_sectors > UINT32_MAX)
762 nb_sectors = UINT32_MAX;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800763 outbuf[0] = (nb_sectors >> 24) & 0xff;
764 outbuf[1] = (nb_sectors >> 16) & 0xff;
765 outbuf[2] = (nb_sectors >> 8) & 0xff;
766 outbuf[3] = nb_sectors & 0xff;
767 outbuf[4] = 0;
768 outbuf[5] = 0;
769 outbuf[6] = s->cluster_size * 2;
770 outbuf[7] = 0;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700771 r->iov.iov_len = 8;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800772 } else {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700773 notready:
774 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800775 return 0;
776 }
777 break;
778 case 0x08:
779 case 0x28:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700780 case 0x88:
781 DPRINTF("Read (sector %lld, count %d)\n", lba, len);
782 if (lba > s->max_lba)
783 goto illegal_lba;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800784 r->sector = lba * s->cluster_size;
785 r->sector_count = len * s->cluster_size;
786 break;
787 case 0x0a:
788 case 0x2a:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700789 case 0x8a:
790 DPRINTF("Write (sector %lld, count %d)\n", lba, len);
791 if (lba > s->max_lba)
792 goto illegal_lba;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800793 r->sector = lba * s->cluster_size;
794 r->sector_count = len * s->cluster_size;
795 is_write = 1;
796 break;
797 case 0x35:
798 DPRINTF("Synchronise cache (sector %d, count %d)\n", lba, len);
799 bdrv_flush(s->bdrv);
800 break;
801 case 0x43:
802 {
803 int start_track, format, msf, toclen;
804
805 msf = buf[1] & 2;
806 format = buf[2] & 0xf;
807 start_track = buf[6];
808 bdrv_get_geometry(s->bdrv, &nb_sectors);
809 DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700810 nb_sectors /= s->cluster_size;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800811 switch(format) {
812 case 0:
813 toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
814 break;
815 case 1:
816 /* multi session : only a single session defined */
817 toclen = 12;
818 memset(outbuf, 0, 12);
819 outbuf[1] = 0x0a;
820 outbuf[2] = 0x01;
821 outbuf[3] = 0x01;
822 break;
823 case 2:
824 toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
825 break;
826 default:
827 goto error_cmd;
828 }
829 if (toclen > 0) {
830 if (len > toclen)
831 len = toclen;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700832 r->iov.iov_len = len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800833 break;
834 }
835 error_cmd:
836 DPRINTF("Read TOC error\n");
837 goto fail;
838 }
839 case 0x46:
840 DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
841 memset(outbuf, 0, 8);
842 /* ??? This should probably return much more information. For now
843 just return the basic header indicating the CD-ROM profile. */
844 outbuf[7] = 8; // CD-ROM
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700845 r->iov.iov_len = 8;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800846 break;
847 case 0x56:
848 DPRINTF("Reserve(10)\n");
849 if (buf[1] & 3)
850 goto fail;
851 break;
852 case 0x57:
853 DPRINTF("Release(10)\n");
854 if (buf[1] & 3)
855 goto fail;
856 break;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700857 case 0x9e:
858 /* Service Action In subcommands. */
859 if ((buf[1] & 31) == 0x10) {
860 DPRINTF("SAI READ CAPACITY(16)\n");
861 memset(outbuf, 0, len);
862 bdrv_get_geometry(s->bdrv, &nb_sectors);
863 nb_sectors /= s->cluster_size;
864 /* Returned value is the address of the last sector. */
865 if (nb_sectors) {
866 nb_sectors--;
867 /* Remember the new size for read/write sanity checking. */
868 s->max_lba = nb_sectors;
869 outbuf[0] = (nb_sectors >> 56) & 0xff;
870 outbuf[1] = (nb_sectors >> 48) & 0xff;
871 outbuf[2] = (nb_sectors >> 40) & 0xff;
872 outbuf[3] = (nb_sectors >> 32) & 0xff;
873 outbuf[4] = (nb_sectors >> 24) & 0xff;
874 outbuf[5] = (nb_sectors >> 16) & 0xff;
875 outbuf[6] = (nb_sectors >> 8) & 0xff;
876 outbuf[7] = nb_sectors & 0xff;
877 outbuf[8] = 0;
878 outbuf[9] = 0;
879 outbuf[10] = s->cluster_size * 2;
880 outbuf[11] = 0;
881 /* Protection, exponent and lowest lba field left blank. */
882 r->iov.iov_len = len;
883 } else {
884 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
885 return 0;
886 }
887 break;
888 }
889 DPRINTF("Unsupported Service Action In\n");
890 goto fail;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800891 case 0xa0:
892 DPRINTF("Report LUNs (len %d)\n", len);
893 if (len < 16)
894 goto fail;
895 memset(outbuf, 0, 16);
896 outbuf[3] = 8;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700897 r->iov.iov_len = 16;
898 break;
899 case 0x2f:
900 DPRINTF("Verify (sector %d, count %d)\n", lba, len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800901 break;
902 default:
903 DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
904 fail:
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700905 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_ILLEGAL_REQUEST);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800906 return 0;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700907 illegal_lba:
908 scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
909 return 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800910 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700911 if (r->sector_count == 0 && r->iov.iov_len == 0) {
912 scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800913 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700914 len = r->sector_count * 512 + r->iov.iov_len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800915 if (is_write) {
916 return -len;
917 } else {
918 if (!r->sector_count)
919 r->sector_count = -1;
920 return len;
921 }
922}
923
924static void scsi_destroy(SCSIDevice *d)
925{
926 qemu_free(d->state);
927 qemu_free(d);
928}
929
930SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
931 scsi_completionfn completion, void *opaque)
932{
933 SCSIDevice *d;
934 SCSIDeviceState *s;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700935 uint64_t nb_sectors;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800936
937 s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
938 s->bdrv = bdrv;
939 s->tcq = tcq;
940 s->completion = completion;
941 s->opaque = opaque;
942 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
943 s->cluster_size = 4;
944 } else {
945 s->cluster_size = 1;
946 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700947 bdrv_get_geometry(s->bdrv, &nb_sectors);
948 nb_sectors /= s->cluster_size;
949 if (nb_sectors)
950 nb_sectors--;
951 s->max_lba = nb_sectors;
David 'Digit' Turnercb42a1b2010-12-23 02:54:08 +0100952#if 0
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700953 strncpy(s->drive_serial_str, drive_get_serial(s->bdrv),
954 sizeof(s->drive_serial_str));
955 if (strlen(s->drive_serial_str) == 0)
David 'Digit' Turnercb42a1b2010-12-23 02:54:08 +0100956#endif
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700957 pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), "0");
958 qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800959 d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
960 d->state = s;
961 d->destroy = scsi_destroy;
962 d->send_command = scsi_send_command;
963 d->read_data = scsi_read_data;
964 d->write_data = scsi_write_data;
965 d->cancel_io = scsi_cancel_io;
966 d->get_buf = scsi_get_buf;
967
968 return d;
969}