diff options
Diffstat (limited to 'hw/scsi-bus.c')
-rw-r--r-- | hw/scsi-bus.c | 97 |
1 files changed, 87 insertions, 10 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 7efcbd83fc..4d9ff35fd8 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -649,6 +649,31 @@ static void scsi_req_dequeue(SCSIRequest *req) } } +static int scsi_get_performance_length(int num_desc, int type, int data_type) +{ + /* MMC-6, paragraph 6.7. */ + switch (type) { + case 0: + if ((data_type & 3) == 0) { + /* Each descriptor is as in Table 295 - Nominal performance. */ + return 16 * num_desc + 8; + } else { + /* Each descriptor is as in Table 296 - Exceptions. */ + return 6 * num_desc + 8; + } + case 1: + case 4: + case 5: + return 8 * num_desc + 8; + case 2: + return 2048 * num_desc + 8; + case 3: + return 16 * num_desc + 8; + default: + return 8; + } +} + static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { switch (buf[0] >> 5) { @@ -666,11 +691,11 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) cmd->len = 10; break; case 4: - cmd->xfer = ldl_be_p(&buf[10]); + cmd->xfer = ldl_be_p(&buf[10]) & 0xffffffffULL; cmd->len = 16; break; case 5: - cmd->xfer = ldl_be_p(&buf[6]); + cmd->xfer = ldl_be_p(&buf[6]) & 0xffffffffULL; cmd->len = 12; break; default: @@ -683,6 +708,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case START_STOP: case SET_CAPACITY: case WRITE_FILEMARKS: + case WRITE_FILEMARKS_16: case SPACE: case RESERVE: case RELEASE: @@ -691,6 +717,8 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case VERIFY_10: case SEEK_10: case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: + case LOCATE_16: case LOCK_UNLOCK_CACHE: case LOAD_UNLOAD: case SET_CD_SPEED: @@ -698,6 +726,11 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case WRITE_LONG_10: case MOVE_MEDIUM: case UPDATE_BLOCK: + case RESERVE_TRACK: + case SET_READ_AHEAD: + case PRE_FETCH: + case PRE_FETCH_16: + case ALLOW_OVERWRITE: cmd->xfer = 0; break; case MODE_SENSE: @@ -711,14 +744,13 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case READ_BLOCK_LIMITS: cmd->xfer = 6; break; - case READ_POSITION: - cmd->xfer = 20; - break; case SEND_VOLUME_TAG: - cmd->xfer *= 40; - break; - case MEDIUM_SCAN: - cmd->xfer *= 8; + /* GPCMD_SET_STREAMING from multimedia commands. */ + if (dev->type == TYPE_ROM) { + cmd->xfer = buf[10] | (buf[9] << 8); + } else { + cmd->xfer = buf[9] | (buf[8] << 8); + } break; case WRITE_10: case WRITE_VERIFY_10: @@ -737,9 +769,39 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case READ_16: cmd->xfer *= dev->blocksize; break; + case FORMAT_UNIT: + /* MMC mandates the parameter list to be 12-bytes long. Parameters + * for block devices are restricted to the header right now. */ + if (dev->type == TYPE_ROM && (buf[1] & 16)) { + cmd->xfer = 12; + } else { + cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4); + } + break; case INQUIRY: + case RECEIVE_DIAGNOSTIC: + case SEND_DIAGNOSTIC: cmd->xfer = buf[4] | (buf[3] << 8); break; + case READ_CD: + case READ_BUFFER: + case WRITE_BUFFER: + case SEND_CUE_SHEET: + cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16); + break; + case PERSISTENT_RESERVE_OUT: + cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL; + break; + case ERASE_12: + if (dev->type == TYPE_ROM) { + /* MMC command GET PERFORMANCE. */ + cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8), + buf[10], buf[1] & 0x1f); + } + break; + case MECHANISM_STATUS: + case READ_DVD_STRUCTURE: + case SEND_DVD_STRUCTURE: case MAINTENANCE_OUT: case MAINTENANCE_IN: if (dev->type == TYPE_ROM) { @@ -755,6 +817,10 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu { switch (buf[0]) { /* stream commands */ + case ERASE_12: + case ERASE_16: + cmd->xfer = 0; + break; case READ_6: case READ_REVERSE: case RECOVER_BUFFERED_DATA: @@ -770,6 +836,15 @@ static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *bu cmd->len = 6; cmd->xfer = 0; break; + case SPACE_16: + cmd->xfer = buf[13] | (buf[12] << 8); + break; + case READ_POSITION: + cmd->xfer = buf[8] | (buf[7] << 8); + break; + case FORMAT_UNIT: + cmd->xfer = buf[4] | (buf[3] << 8); + break; /* generic commands */ default: return scsi_req_length(cmd, dev, buf); @@ -809,6 +884,8 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) case SEARCH_LOW_12: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case SEND_CUE_SHEET: + case SEND_DVD_STRUCTURE: case PERSISTENT_RESERVE_OUT: case MAINTENANCE_OUT: cmd->mode = SCSI_XFER_TO_DEV; @@ -835,7 +912,7 @@ static uint64_t scsi_cmd_lba(SCSICommand *cmd) case 1: case 2: case 5: - lba = ldl_be_p(&buf[2]); + lba = ldl_be_p(&buf[2]) & 0xffffffffULL; break; case 4: lba = ldq_be_p(&buf[2]); |