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,12 +44,14 @@ do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0) | ||
| 44 | #include <scsi/sg.h> | 44 | #include <scsi/sg.h> |
| 45 | #include <scsi/scsi.h> | 45 | #include <scsi/scsi.h> |
| 46 | 46 | ||
| 47 | +#define REWIND 0x01 | ||
| 48 | +#define REPORT_DENSITY_SUPPORT 0x44 | ||
| 47 | #define LOAD_UNLOAD 0xa6 | 49 | #define LOAD_UNLOAD 0xa6 |
| 48 | #define SET_CD_SPEED 0xbb | 50 | #define SET_CD_SPEED 0xbb |
| 49 | #define BLANK 0xa1 | 51 | #define BLANK 0xa1 |
| 50 | 52 | ||
| 51 | #define SCSI_CMD_BUF_SIZE 16 | 53 | #define SCSI_CMD_BUF_SIZE 16 |
| 52 | -#define SCSI_SENSE_BUF_SIZE 32 | 54 | +#define SCSI_SENSE_BUF_SIZE 96 |
| 53 | 55 | ||
| 54 | #define SG_ERR_DRIVER_TIMEOUT 0x06 | 56 | #define SG_ERR_DRIVER_TIMEOUT 0x06 |
| 55 | #define SG_ERR_DRIVER_SENSE 0x08 | 57 | #define SG_ERR_DRIVER_SENSE 0x08 |
| @@ -75,6 +77,7 @@ struct SCSIDeviceState | @@ -75,6 +77,7 @@ struct SCSIDeviceState | ||
| 75 | { | 77 | { |
| 76 | SCSIRequest *requests; | 78 | SCSIRequest *requests; |
| 77 | BlockDriverState *bdrv; | 79 | BlockDriverState *bdrv; |
| 80 | + int type; | ||
| 78 | int blocksize; | 81 | int blocksize; |
| 79 | int lun; | 82 | int lun; |
| 80 | scsi_completionfn completion; | 83 | scsi_completionfn completion; |
| @@ -163,7 +166,7 @@ static void scsi_command_complete(void *opaque, int ret) | @@ -163,7 +166,7 @@ static void scsi_command_complete(void *opaque, int ret) | ||
| 163 | } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0) | 166 | } else if ((s->driver_status & SG_ERR_DRIVER_SENSE) == 0) |
| 164 | sense = NO_SENSE; | 167 | sense = NO_SENSE; |
| 165 | else | 168 | else |
| 166 | - sense = s->sensebuf[2] & 0x0f; | 169 | + sense = s->sensebuf[2]; |
| 167 | } | 170 | } |
| 168 | 171 | ||
| 169 | DPRINTF("Command complete 0x%p tag=0x%x sense=%d\n", r, r->tag, sense); | 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,10 +276,14 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) | ||
| 273 | 276 | ||
| 274 | if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) | 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 | r->io_header.driver_status = 0; | 281 | r->io_header.driver_status = 0; |
| 278 | r->len = -1; | 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 | return; | 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,6 +441,32 @@ static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) | ||
| 434 | return 0; | 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 | static int is_write(int command) | 470 | static int is_write(int command) |
| 438 | { | 471 | { |
| 439 | switch (command) { | 472 | switch (command) { |
| @@ -495,9 +528,16 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | @@ -495,9 +528,16 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, | ||
| 495 | return 0; | 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 | DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, | 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,12 +673,17 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, | ||
| 633 | s->completion = completion; | 673 | s->completion = completion; |
| 634 | s->opaque = opaque; | 674 | s->opaque = opaque; |
| 635 | s->lun = scsiid.lun; | 675 | s->lun = scsiid.lun; |
| 676 | + s->type = scsiid.scsi_type; | ||
| 636 | s->blocksize = get_blocksize(s->bdrv); | 677 | s->blocksize = get_blocksize(s->bdrv); |
| 637 | s->driver_status = 0; | 678 | s->driver_status = 0; |
| 638 | memset(s->sensebuf, 0, sizeof(s->sensebuf)); | 679 | memset(s->sensebuf, 0, sizeof(s->sensebuf)); |
| 639 | /* removable media returns 0 if not present */ | 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 | /* define function to manage device */ | 688 | /* define function to manage device */ |
| 644 | 689 |