Commit 2cc977e27d2e6a1212b449ab7f58beb2e294ff83
1 parent
985a03b0
scsi-generic implemnentation, missing in last commit.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3852 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
653 additions
and
0 deletions
hw/scsi-generic.c
0 → 100644
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" | ||
15 | +#include "block.h" | ||
16 | +#include "scsi-disk.h" | ||
17 | + | ||
18 | +#ifndef __linux__ | ||
19 | + | ||
20 | +SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, | ||
21 | + scsi_completionfn completion, void *opaque) | ||
22 | +{ | ||
23 | + return NULL; | ||
24 | +} | ||
25 | + | ||
26 | +#else /* __linux__ */ | ||
27 | + | ||
28 | +//#define DEBUG_SCSI | ||
29 | + | ||
30 | +#ifdef DEBUG_SCSI | ||
31 | +#define DPRINTF(fmt, args...) \ | ||
32 | +do { printf("scsi-generic: " fmt , ##args); } while (0) | ||
33 | +#else | ||
34 | +#define DPRINTF(fmt, args...) do {} while(0) | ||
35 | +#endif | ||
36 | + | ||
37 | +#define BADF(fmt, args...) \ | ||
38 | +do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0) | ||
39 | + | ||
40 | +#include <stdio.h> | ||
41 | +#include <sys/types.h> | ||
42 | +#include <sys/stat.h> | ||
43 | +#include <unistd.h> | ||
44 | +#include <scsi/sg.h> | ||
45 | +#include <scsi/scsi.h> | ||
46 | + | ||
47 | +#define LOAD_UNLOAD 0xa6 | ||
48 | +#define SET_CD_SPEED 0xbb | ||
49 | +#define BLANK 0xa1 | ||
50 | + | ||
51 | +#define SCSI_CMD_BUF_SIZE 16 | ||
52 | +#define SCSI_SENSE_BUF_SIZE 32 | ||
53 | + | ||
54 | +#define SG_ERR_DRIVER_TIMEOUT 0x06 | ||
55 | +#define SG_ERR_DRIVER_SENSE 0x08 | ||
56 | + | ||
57 | +#ifndef MAX_UINT | ||
58 | +#define MAX_UINT ((unsigned int)-1) | ||
59 | +#endif | ||
60 | + | ||
61 | +typedef struct SCSIRequest { | ||
62 | + BlockDriverAIOCB *aiocb; | ||
63 | + struct SCSIRequest *next; | ||
64 | + SCSIDeviceState *dev; | ||
65 | + uint32_t tag; | ||
66 | + uint8_t cmd[SCSI_CMD_BUF_SIZE]; | ||
67 | + int cmdlen; | ||
68 | + uint8_t *buf; | ||
69 | + int buflen; | ||
70 | + int len; | ||
71 | + sg_io_hdr_t io_header; | ||
72 | +} SCSIRequest; | ||
73 | + | ||
74 | +struct SCSIDeviceState | ||
75 | +{ | ||
76 | + SCSIRequest *requests; | ||
77 | + BlockDriverState *bdrv; | ||
78 | + int blocksize; | ||
79 | + int lun; | ||
80 | + scsi_completionfn completion; | ||
81 | + void *opaque; | ||
82 | + int driver_status; | ||
83 | + uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; | ||
84 | +}; | ||
85 | + | ||
86 | +/* Global pool of SCSIRequest structures. */ | ||
87 | +static SCSIRequest *free_requests = NULL; | ||
88 | + | ||
89 | +static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag) | ||
90 | +{ | ||
91 | + SCSIRequest *r; | ||
92 | + | ||
93 | + if (free_requests) { | ||
94 | + r = free_requests; | ||
95 | + free_requests = r->next; | ||
96 | + } else { | ||
97 | + r = qemu_malloc(sizeof(SCSIRequest)); | ||
98 | + r->buf = NULL; | ||
99 | + r->buflen = 0; | ||
100 | + } | ||
101 | + r->dev = s; | ||
102 | + r->tag = tag; | ||
103 | + memset(r->cmd, 0, sizeof(r->cmd)); | ||
104 | + memset(&r->io_header, 0, sizeof(r->io_header)); | ||
105 | + r->cmdlen = 0; | ||
106 | + r->len = 0; | ||
107 | + r->aiocb = NULL; | ||
108 | + | ||
109 | + /* link */ | ||
110 | + | ||
111 | + r->next = s->requests; | ||
112 | + s->requests = r; | ||
113 | + return r; | ||
114 | +} | ||
115 | + | ||
116 | +static void scsi_remove_request(SCSIRequest *r) | ||
117 | +{ | ||
118 | + SCSIRequest *last; | ||
119 | + SCSIDeviceState *s = r->dev; | ||
120 | + | ||
121 | + if (s->requests == r) { | ||
122 | + s->requests = r->next; | ||
123 | + } else { | ||
124 | + last = s->requests; | ||
125 | + while (last && last->next != r) | ||
126 | + last = last->next; | ||
127 | + if (last) { | ||
128 | + last->next = r->next; | ||
129 | + } else { | ||
130 | + BADF("Orphaned request\n"); | ||
131 | + } | ||
132 | + } | ||
133 | + r->next = free_requests; | ||
134 | + free_requests = r; | ||
135 | +} | ||
136 | + | ||
137 | +static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag) | ||
138 | +{ | ||
139 | + SCSIRequest *r; | ||
140 | + | ||
141 | + r = s->requests; | ||
142 | + while (r && r->tag != tag) | ||
143 | + r = r->next; | ||
144 | + | ||
145 | + return r; | ||
146 | +} | ||
147 | + | ||
148 | +/* Helper function for command completion. */ | ||
149 | +static void scsi_command_complete(void *opaque, int ret) | ||
150 | +{ | ||
151 | + SCSIRequest *r = (SCSIRequest *)opaque; | ||
152 | + SCSIDeviceState *s = r->dev; | ||
153 | + uint32_t tag; | ||
154 | + int sense; | ||
155 | + | ||
156 | + s->driver_status = r->io_header.driver_status; | ||
157 | + if (ret != 0) | ||
158 | + sense = HARDWARE_ERROR; | ||
159 | + else { | ||
160 | + if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { | ||
161 | + sense = HARDWARE_ERROR; | ||
162 | + BADF("Driver Timeout\n"); | ||
163 | + } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0) | ||
164 | + sense = NO_SENSE; | ||
165 | + else | ||
166 | + sense = s->sensebuf[2] & 0x0f; | ||
167 | + } | ||
168 | + | ||
169 | + DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense); | ||
170 | + tag = r->tag; | ||
171 | + scsi_remove_request(r); | ||
172 | + s->completion(s->opaque, SCSI_REASON_DONE, tag, sense); | ||
173 | +} | ||
174 | + | ||
175 | +/* Cancel a pending data transfer. */ | ||
176 | +static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) | ||
177 | +{ | ||
178 | + DPRINTF("scsi_cancel_io 0x%x\n", tag); | ||
179 | + SCSIDeviceState *s = d->state; | ||
180 | + SCSIRequest *r; | ||
181 | + DPRINTF("Cancel tag=0x%x\n", tag); | ||
182 | + r = scsi_find_request(s, tag); | ||
183 | + if (r) { | ||
184 | + if (r->aiocb) | ||
185 | + bdrv_aio_cancel(r->aiocb); | ||
186 | + r->aiocb = NULL; | ||
187 | + scsi_remove_request(r); | ||
188 | + } | ||
189 | +} | ||
190 | + | ||
191 | +static int execute_command(BlockDriverState *bdrv, | ||
192 | + SCSIRequest *r, int direction, | ||
193 | + BlockDriverCompletionFunc *complete) | ||
194 | +{ | ||
195 | + | ||
196 | + r->io_header.interface_id = 'S'; | ||
197 | + r->io_header.dxfer_direction = direction; | ||
198 | + r->io_header.dxferp = r->buf; | ||
199 | + r->io_header.dxfer_len = r->buflen; | ||
200 | + r->io_header.cmdp = r->cmd; | ||
201 | + r->io_header.cmd_len = r->cmdlen; | ||
202 | + r->io_header.mx_sb_len = sizeof(r->dev->sensebuf); | ||
203 | + r->io_header.sbp = r->dev->sensebuf; | ||
204 | + r->io_header.timeout = MAX_UINT; | ||
205 | + r->io_header.usr_ptr = r; | ||
206 | + r->io_header.flags |= SG_FLAG_DIRECT_IO; | ||
207 | + | ||
208 | + if (bdrv_pwrite(bdrv, -1, &r->io_header, sizeof(r->io_header)) == -1) { | ||
209 | + BADF("execute_command: write failed ! (%d)\n", errno); | ||
210 | + return -1; | ||
211 | + } | ||
212 | + if (complete == NULL) { | ||
213 | + int ret; | ||
214 | + r->aiocb = NULL; | ||
215 | + while ((ret = bdrv_pread(bdrv, -1, &r->io_header, | ||
216 | + sizeof(r->io_header))) == -1 && | ||
217 | + errno == EINTR); | ||
218 | + if (ret == -1) { | ||
219 | + BADF("execute_command: read failed !\n"); | ||
220 | + return -1; | ||
221 | + } | ||
222 | + return 0; | ||
223 | + } | ||
224 | + | ||
225 | + r->aiocb = bdrv_aio_read(bdrv, 0, (uint8_t*)&r->io_header, | ||
226 | + -(int64_t)sizeof(r->io_header), complete, r); | ||
227 | + if (r->aiocb == NULL) { | ||
228 | + BADF("execute_command: read failed !\n"); | ||
229 | + return -1; | ||
230 | + } | ||
231 | + | ||
232 | + return 0; | ||
233 | +} | ||
234 | + | ||
235 | +static void scsi_read_complete(void * opaque, int ret) | ||
236 | +{ | ||
237 | + SCSIRequest *r = (SCSIRequest *)opaque; | ||
238 | + SCSIDeviceState *s = r->dev; | ||
239 | + int len; | ||
240 | + | ||
241 | + if (ret) { | ||
242 | + DPRINTF("IO error\n"); | ||
243 | + scsi_command_complete(r, ret); | ||
244 | + return; | ||
245 | + } | ||
246 | + len = r->io_header.dxfer_len - r->io_header.resid; | ||
247 | + DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len); | ||
248 | + | ||
249 | + r->len = -1; | ||
250 | + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len); | ||
251 | +} | ||
252 | + | ||
253 | +/* Read more data from scsi device into buffer. */ | ||
254 | +static void scsi_read_data(SCSIDevice *d, uint32_t tag) | ||
255 | +{ | ||
256 | + SCSIDeviceState *s = d->state; | ||
257 | + SCSIRequest *r; | ||
258 | + int ret; | ||
259 | + | ||
260 | + DPRINTF("scsi_read_data 0x%x\n", tag); | ||
261 | + r = scsi_find_request(s, tag); | ||
262 | + if (!r) { | ||
263 | + BADF("Bad read tag 0x%x\n", tag); | ||
264 | + /* ??? This is the wrong error. */ | ||
265 | + scsi_command_complete(r, -EINVAL); | ||
266 | + return; | ||
267 | + } | ||
268 | + | ||
269 | + if (r->len == -1) { | ||
270 | + scsi_command_complete(r, 0); | ||
271 | + return; | ||
272 | + } | ||
273 | + | ||
274 | + if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) | ||
275 | + { | ||
276 | + memcpy(r->buf, s->sensebuf, 16); | ||
277 | + r->io_header.driver_status = 0; | ||
278 | + r->len = -1; | ||
279 | + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 16); | ||
280 | + return; | ||
281 | + } | ||
282 | + | ||
283 | + ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete); | ||
284 | + if (ret == -1) { | ||
285 | + scsi_command_complete(r, -EINVAL); | ||
286 | + return; | ||
287 | + } | ||
288 | +} | ||
289 | + | ||
290 | +static void scsi_write_complete(void * opaque, int ret) | ||
291 | +{ | ||
292 | + SCSIRequest *r = (SCSIRequest *)opaque; | ||
293 | + | ||
294 | + DPRINTF("scsi_write_complete() ret = %d\n", ret); | ||
295 | + if (ret) { | ||
296 | + DPRINTF("IO error\n"); | ||
297 | + scsi_command_complete(r, ret); | ||
298 | + return; | ||
299 | + } | ||
300 | + | ||
301 | + scsi_command_complete(r, ret); | ||
302 | +} | ||
303 | + | ||
304 | +/* Write data to a scsi device. Returns nonzero on failure. | ||
305 | + The transfer may complete asynchronously. */ | ||
306 | +static int scsi_write_data(SCSIDevice *d, uint32_t tag) | ||
307 | +{ | ||
308 | + SCSIDeviceState *s = d->state; | ||
309 | + SCSIRequest *r; | ||
310 | + int ret; | ||
311 | + | ||
312 | + DPRINTF("scsi_write_data 0x%x\n", tag); | ||
313 | + r = scsi_find_request(s, tag); | ||
314 | + if (!r) { | ||
315 | + BADF("Bad write tag 0x%x\n", tag); | ||
316 | + /* ??? This is the wrong error. */ | ||
317 | + scsi_command_complete(r, -EINVAL); | ||
318 | + return 0; | ||
319 | + } | ||
320 | + | ||
321 | + if (r->len == 0) { | ||
322 | + r->len = r->buflen; | ||
323 | + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->len); | ||
324 | + return 0; | ||
325 | + } | ||
326 | + | ||
327 | + ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete); | ||
328 | + if (ret == -1) { | ||
329 | + scsi_command_complete(r, -EINVAL); | ||
330 | + return 1; | ||
331 | + } | ||
332 | + | ||
333 | + return 0; | ||
334 | +} | ||
335 | + | ||
336 | +/* Return a pointer to the data buffer. */ | ||
337 | +static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) | ||
338 | +{ | ||
339 | + SCSIDeviceState *s = d->state; | ||
340 | + SCSIRequest *r; | ||
341 | + r = scsi_find_request(s, tag); | ||
342 | + if (!r) { | ||
343 | + BADF("Bad buffer tag 0x%x\n", tag); | ||
344 | + return NULL; | ||
345 | + } | ||
346 | + return r->buf; | ||
347 | +} | ||
348 | + | ||
349 | +static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) | ||
350 | +{ | ||
351 | + switch (cmd[0] >> 5) { | ||
352 | + case 0: | ||
353 | + *len = cmd[4]; | ||
354 | + *cmdlen = 6; | ||
355 | + break; | ||
356 | + case 1: | ||
357 | + case 2: | ||
358 | + *len = cmd[8] | (cmd[7] << 8); | ||
359 | + *cmdlen = 10; | ||
360 | + break; | ||
361 | + case 4: | ||
362 | + *len = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24); | ||
363 | + *cmdlen = 16; | ||
364 | + break; | ||
365 | + case 5: | ||
366 | + *len = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24); | ||
367 | + *cmdlen = 12; | ||
368 | + break; | ||
369 | + default: | ||
370 | + return -1; | ||
371 | + } | ||
372 | + | ||
373 | + switch(cmd[0]) { | ||
374 | + case TEST_UNIT_READY: | ||
375 | + case REZERO_UNIT: | ||
376 | + case START_STOP: | ||
377 | + case SEEK_6: | ||
378 | + case WRITE_FILEMARKS: | ||
379 | + case SPACE: | ||
380 | + case ERASE: | ||
381 | + case ALLOW_MEDIUM_REMOVAL: | ||
382 | + case VERIFY: | ||
383 | + case SEEK_10: | ||
384 | + case SYNCHRONIZE_CACHE: | ||
385 | + case LOCK_UNLOCK_CACHE: | ||
386 | + case LOAD_UNLOAD: | ||
387 | + case SET_CD_SPEED: | ||
388 | + case SET_LIMITS: | ||
389 | + case WRITE_LONG: | ||
390 | + case MOVE_MEDIUM: | ||
391 | + case UPDATE_BLOCK: | ||
392 | + *len = 0; | ||
393 | + break; | ||
394 | + case MODE_SENSE: | ||
395 | + break; | ||
396 | + case WRITE_SAME: | ||
397 | + *len = 1; | ||
398 | + break; | ||
399 | + case READ_CAPACITY: | ||
400 | + *len = 8; | ||
401 | + break; | ||
402 | + case READ_BLOCK_LIMITS: | ||
403 | + *len = 6; | ||
404 | + break; | ||
405 | + case READ_POSITION: | ||
406 | + *len = 20; | ||
407 | + break; | ||
408 | + case SEND_VOLUME_TAG: | ||
409 | + *len *= 40; | ||
410 | + break; | ||
411 | + case MEDIUM_SCAN: | ||
412 | + *len *= 8; | ||
413 | + break; | ||
414 | + case WRITE_10: | ||
415 | + cmd[1] &= ~0x08; /* disable FUA */ | ||
416 | + case WRITE_VERIFY: | ||
417 | + case WRITE_6: | ||
418 | + case WRITE_12: | ||
419 | + case WRITE_VERIFY_12: | ||
420 | + *len *= blocksize; | ||
421 | + break; | ||
422 | + case READ_10: | ||
423 | + cmd[1] &= ~0x08; /* disable FUA */ | ||
424 | + case READ_6: | ||
425 | + case READ_REVERSE: | ||
426 | + case RECOVER_BUFFERED_DATA: | ||
427 | + case READ_12: | ||
428 | + *len *= blocksize; | ||
429 | + break; | ||
430 | + } | ||
431 | + return 0; | ||
432 | +} | ||
433 | + | ||
434 | +static int is_write(int command) | ||
435 | +{ | ||
436 | + switch (command) { | ||
437 | + case COPY: | ||
438 | + case COPY_VERIFY: | ||
439 | + case COMPARE: | ||
440 | + case CHANGE_DEFINITION: | ||
441 | + case LOG_SELECT: | ||
442 | + case MODE_SELECT: | ||
443 | + case MODE_SELECT_10: | ||
444 | + case SEND_DIAGNOSTIC: | ||
445 | + case WRITE_BUFFER: | ||
446 | + case FORMAT_UNIT: | ||
447 | + case REASSIGN_BLOCKS: | ||
448 | + case RESERVE: | ||
449 | + case SEARCH_EQUAL: | ||
450 | + case SEARCH_HIGH: | ||
451 | + case SEARCH_LOW: | ||
452 | + case WRITE_6: | ||
453 | + case WRITE_10: | ||
454 | + case WRITE_VERIFY: | ||
455 | + case UPDATE_BLOCK: | ||
456 | + case WRITE_LONG: | ||
457 | + case WRITE_SAME: | ||
458 | + case SEARCH_HIGH_12: | ||
459 | + case SEARCH_EQUAL_12: | ||
460 | + case SEARCH_LOW_12: | ||
461 | + case WRITE_12: | ||
462 | + case WRITE_VERIFY_12: | ||
463 | + case SET_WINDOW: | ||
464 | + case MEDIUM_SCAN: | ||
465 | + case SEND_VOLUME_TAG: | ||
466 | + case WRITE_LONG_2: | ||
467 | + return 1; | ||
468 | + } | ||
469 | + return 0; | ||
470 | +} | ||
471 | + | ||
472 | +/* Execute a scsi command. Returns the length of the data expected by the | ||
473 | + command. This will be Positive for data transfers from the device | ||
474 | + (eg. disk reads), negative for transfers to the device (eg. disk writes), | ||
475 | + and zero if the command does not transfer any data. */ | ||
476 | + | ||
477 | +static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | ||
478 | + uint8_t *cmd, int lun) | ||
479 | +{ | ||
480 | + SCSIDeviceState *s = d->state; | ||
481 | + uint32_t len; | ||
482 | + int cmdlen; | ||
483 | + SCSIRequest *r; | ||
484 | + int ret; | ||
485 | + | ||
486 | + /* ??? Tags are not unique for different luns. We only implement a | ||
487 | + single lun, so this should not matter. */ | ||
488 | + | ||
489 | + if (lun != s->lun || (cmd[1] >> 5) != s->lun) { | ||
490 | + DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); | ||
491 | + s->completion(s->opaque, SCSI_REASON_DONE, tag, ILLEGAL_REQUEST); | ||
492 | + return 0; | ||
493 | + } | ||
494 | + | ||
495 | + if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) { | ||
496 | + BADF("Unsupported command length, command %x\n", cmd[0]); | ||
497 | + return 0; | ||
498 | + } | ||
499 | + | ||
500 | + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, | ||
501 | + cmd[0], len); | ||
502 | + | ||
503 | + r = scsi_find_request(s, tag); | ||
504 | + if (r) { | ||
505 | + BADF("Tag 0x%x already in use %p\n", tag, r); | ||
506 | + scsi_cancel_io(d, tag); | ||
507 | + } | ||
508 | + r = scsi_new_request(s, tag); | ||
509 | + | ||
510 | + memcpy(r->cmd, cmd, cmdlen); | ||
511 | + r->cmdlen = cmdlen; | ||
512 | + | ||
513 | + if (len == 0) { | ||
514 | + if (r->buf != NULL) | ||
515 | + free(r->buf); | ||
516 | + r->buflen = 0; | ||
517 | + r->buf = NULL; | ||
518 | + ret = execute_command(s->bdrv, r, SG_DXFER_NONE, scsi_command_complete); | ||
519 | + if (ret == -1) { | ||
520 | + scsi_command_complete(r, -EINVAL); | ||
521 | + return 0; | ||
522 | + } | ||
523 | + return 0; | ||
524 | + } | ||
525 | + | ||
526 | + if (r->buflen != len) { | ||
527 | + if (r->buf != NULL) | ||
528 | + free(r->buf); | ||
529 | + r->buf = qemu_malloc(len); | ||
530 | + r->buflen = len; | ||
531 | + } | ||
532 | + | ||
533 | + memset(r->buf, 0, r->buflen); | ||
534 | + r->len = len; | ||
535 | + if (is_write(cmd[0])) { | ||
536 | + r->len = 0; | ||
537 | + return -len; | ||
538 | + } | ||
539 | + | ||
540 | + return len; | ||
541 | +} | ||
542 | + | ||
543 | +static int get_blocksize(BlockDriverState *bdrv) | ||
544 | +{ | ||
545 | + uint8_t cmd[10]; | ||
546 | + uint8_t buf[8]; | ||
547 | + uint8_t sensebuf[8]; | ||
548 | + sg_io_hdr_t io_header; | ||
549 | + int ret; | ||
550 | + | ||
551 | + memset(cmd, sizeof(cmd), 0); | ||
552 | + memset(buf, sizeof(buf), 0); | ||
553 | + cmd[0] = READ_CAPACITY; | ||
554 | + | ||
555 | + memset(&io_header, 0, sizeof(io_header)); | ||
556 | + io_header.interface_id = 'S'; | ||
557 | + io_header.dxfer_direction = SG_DXFER_FROM_DEV; | ||
558 | + io_header.dxfer_len = sizeof(buf); | ||
559 | + io_header.dxferp = buf; | ||
560 | + io_header.cmdp = cmd; | ||
561 | + io_header.cmd_len = sizeof(cmd); | ||
562 | + io_header.mx_sb_len = sizeof(sensebuf); | ||
563 | + io_header.sbp = sensebuf; | ||
564 | + io_header.timeout = 6000; /* XXX */ | ||
565 | + | ||
566 | + ret = bdrv_pwrite(bdrv, -1, &io_header, sizeof(io_header)); | ||
567 | + if (ret == -1) | ||
568 | + return -1; | ||
569 | + | ||
570 | + while ((ret = bdrv_pread(bdrv, -1, &io_header, sizeof(io_header))) == -1 && | ||
571 | + errno == EINTR); | ||
572 | + | ||
573 | + if (ret == -1) | ||
574 | + return -1; | ||
575 | + | ||
576 | + return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; | ||
577 | +} | ||
578 | + | ||
579 | +static void scsi_destroy(SCSIDevice *d) | ||
580 | +{ | ||
581 | + SCSIRequest *r, *n; | ||
582 | + | ||
583 | + r = d->state->requests; | ||
584 | + while (r) { | ||
585 | + n = r->next; | ||
586 | + qemu_free(r); | ||
587 | + r = n; | ||
588 | + } | ||
589 | + | ||
590 | + r = free_requests; | ||
591 | + while (r) { | ||
592 | + n = r->next; | ||
593 | + qemu_free(r); | ||
594 | + r = n; | ||
595 | + } | ||
596 | + | ||
597 | + qemu_free(d->state); | ||
598 | + qemu_free(d); | ||
599 | +} | ||
600 | + | ||
601 | +SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, | ||
602 | + scsi_completionfn completion, void *opaque) | ||
603 | +{ | ||
604 | + int sg_version; | ||
605 | + SCSIDevice *d; | ||
606 | + SCSIDeviceState *s; | ||
607 | + struct sg_scsi_id scsiid; | ||
608 | + | ||
609 | + /* check we are really using a /dev/sg* file */ | ||
610 | + | ||
611 | + if (!bdrv_is_sg(bdrv)) | ||
612 | + return NULL; | ||
613 | + | ||
614 | + /* check we are using a driver managing SG_IO (version 3 and after */ | ||
615 | + | ||
616 | + if (bdrv_ioctl(bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 || | ||
617 | + sg_version < 30000) | ||
618 | + return NULL; | ||
619 | + | ||
620 | + /* get LUN of the /dev/sg? */ | ||
621 | + | ||
622 | + if (bdrv_ioctl(bdrv, SG_GET_SCSI_ID, &scsiid)) | ||
623 | + return NULL; | ||
624 | + | ||
625 | + /* define device state */ | ||
626 | + | ||
627 | + s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState)); | ||
628 | + s->bdrv = bdrv; | ||
629 | + s->requests = NULL; | ||
630 | + s->completion = completion; | ||
631 | + s->opaque = opaque; | ||
632 | + s->lun = scsiid.lun; | ||
633 | + s->blocksize = get_blocksize(s->bdrv); | ||
634 | + s->driver_status = 0; | ||
635 | + memset(s->sensebuf, 0, sizeof(s->sensebuf)); | ||
636 | + /* removable media returns 0 if not present */ | ||
637 | + if (s->blocksize <= 0) | ||
638 | + s->blocksize = 2048; | ||
639 | + | ||
640 | + /* define function to manage device */ | ||
641 | + | ||
642 | + d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice)); | ||
643 | + d->state = s; | ||
644 | + d->destroy = scsi_destroy; | ||
645 | + d->send_command = scsi_send_command; | ||
646 | + d->read_data = scsi_read_data; | ||
647 | + d->write_data = scsi_write_data; | ||
648 | + d->cancel_io = scsi_cancel_io; | ||
649 | + d->get_buf = scsi_get_buf; | ||
650 | + | ||
651 | + return d; | ||
652 | +} | ||
653 | +#endif /* __linux__ */ |