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 | ... | ... |