Commit a9dd6843be390ec94a4e51cbaa50c43b7cb0357a
1 parent
82889986
scsi-generic: decode correctly SCSI tape commands (Laurent Vivier)
This patch allows to use a "real" SCSI tape with qemu using "-drive /dev/sgX,if=scsi". It allows to decode correctly transfer length when the type of the device is a tape. Some issues remain when the application reading the tape tries to go beyond the end of the stream (but they must be corrected at the SCSI controller level). Signed-off-by: Laurent Vivier <Laurent.Vivier@bull.net> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5305 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
54 additions
and
9 deletions
hw/scsi-generic.c
... | ... | @@ -44,12 +44,14 @@ do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0) |
44 | 44 | #include <scsi/sg.h> |
45 | 45 | #include <scsi/scsi.h> |
46 | 46 | |
47 | +#define REWIND 0x01 | |
48 | +#define REPORT_DENSITY_SUPPORT 0x44 | |
47 | 49 | #define LOAD_UNLOAD 0xa6 |
48 | 50 | #define SET_CD_SPEED 0xbb |
49 | 51 | #define BLANK 0xa1 |
50 | 52 | |
51 | 53 | #define SCSI_CMD_BUF_SIZE 16 |
52 | -#define SCSI_SENSE_BUF_SIZE 32 | |
54 | +#define SCSI_SENSE_BUF_SIZE 96 | |
53 | 55 | |
54 | 56 | #define SG_ERR_DRIVER_TIMEOUT 0x06 |
55 | 57 | #define SG_ERR_DRIVER_SENSE 0x08 |
... | ... | @@ -75,6 +77,7 @@ struct SCSIDeviceState |
75 | 77 | { |
76 | 78 | SCSIRequest *requests; |
77 | 79 | BlockDriverState *bdrv; |
80 | + int type; | |
78 | 81 | int blocksize; |
79 | 82 | int lun; |
80 | 83 | scsi_completionfn completion; |
... | ... | @@ -163,7 +166,7 @@ static void scsi_command_complete(void *opaque, int ret) |
163 | 166 | } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0) |
164 | 167 | sense = NO_SENSE; |
165 | 168 | else |
166 | - sense = s->sensebuf[2] & 0x0f; | |
169 | + sense = s->sensebuf[2]; | |
167 | 170 | } |
168 | 171 | |
169 | 172 | DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense); |
... | ... | @@ -273,10 +276,14 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) |
273 | 276 | |
274 | 277 | if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) |
275 | 278 | { |
276 | - memcpy(r->buf, s->sensebuf, 16); | |
279 | + int len = MIN(r->len, SCSI_SENSE_BUF_SIZE); | |
280 | + memcpy(r->buf, s->sensebuf, len); | |
277 | 281 | r->io_header.driver_status = 0; |
278 | 282 | r->len = -1; |
279 | - s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 16); | |
283 | + DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", | |
284 | + r->buf[0], r->buf[1], r->buf[2], r->buf[3], | |
285 | + r->buf[4], r->buf[5], r->buf[6], r->buf[7]); | |
286 | + s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len); | |
280 | 287 | return; |
281 | 288 | } |
282 | 289 | |
... | ... | @@ -434,6 +441,32 @@ static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) |
434 | 441 | return 0; |
435 | 442 | } |
436 | 443 | |
444 | +static int scsi_stream_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) | |
445 | +{ | |
446 | + switch(cmd[0]) { | |
447 | + /* stream commands */ | |
448 | + case READ_6: | |
449 | + case READ_REVERSE: | |
450 | + case RECOVER_BUFFERED_DATA: | |
451 | + case WRITE_6: | |
452 | + *cmdlen = 6; | |
453 | + *len = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16); | |
454 | + if (cmd[1] & 0x01) /* fixed */ | |
455 | + *len *= blocksize; | |
456 | + break; | |
457 | + case REWIND: | |
458 | + case START_STOP: | |
459 | + *cmdlen = 6; | |
460 | + *len = 0; | |
461 | + cmd[1] = 0x01; /* force IMMED, otherwise qemu waits end of command */ | |
462 | + break; | |
463 | + /* generic commands */ | |
464 | + default: | |
465 | + return scsi_length(cmd, blocksize, cmdlen, len); | |
466 | + } | |
467 | + return 0; | |
468 | +} | |
469 | + | |
437 | 470 | static int is_write(int command) |
438 | 471 | { |
439 | 472 | switch (command) { |
... | ... | @@ -495,9 +528,16 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, |
495 | 528 | return 0; |
496 | 529 | } |
497 | 530 | |
498 | - if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) { | |
499 | - BADF("Unsupported command length, command %x\n", cmd[0]); | |
500 | - return 0; | |
531 | + if (s->type == TYPE_TAPE) { | |
532 | + if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) { | |
533 | + BADF("Unsupported command length, command %x\n", cmd[0]); | |
534 | + return 0; | |
535 | + } | |
536 | + } else { | |
537 | + if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) { | |
538 | + BADF("Unsupported command length, command %x\n", cmd[0]); | |
539 | + return 0; | |
540 | + } | |
501 | 541 | } |
502 | 542 | |
503 | 543 | DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, |
... | ... | @@ -633,12 +673,17 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, |
633 | 673 | s->completion = completion; |
634 | 674 | s->opaque = opaque; |
635 | 675 | s->lun = scsiid.lun; |
676 | + s->type = scsiid.scsi_type; | |
636 | 677 | s->blocksize = get_blocksize(s->bdrv); |
637 | 678 | s->driver_status = 0; |
638 | 679 | memset(s->sensebuf, 0, sizeof(s->sensebuf)); |
639 | 680 | /* removable media returns 0 if not present */ |
640 | - if (s->blocksize <= 0) | |
641 | - s->blocksize = 2048; | |
681 | + if (s->blocksize <= 0) { | |
682 | + if (s->type == TYPE_ROM || s->type == TYPE_WORM) | |
683 | + s->blocksize = 2048; | |
684 | + else | |
685 | + s->blocksize = 512; | |
686 | + } | |
642 | 687 | |
643 | 688 | /* define function to manage device */ |
644 | 689 | ... | ... |