|
@@ -49,10 +49,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
|
|
|
|
|
typedef struct SCSIDiskState SCSIDiskState;
|
|
typedef struct SCSIDiskState SCSIDiskState;
|
|
|
|
|
|
-typedef struct SCSISense {
|
|
|
|
- uint8_t key;
|
|
|
|
-} SCSISense;
|
|
|
|
-
|
|
|
|
typedef struct SCSIDiskReq {
|
|
typedef struct SCSIDiskReq {
|
|
SCSIRequest req;
|
|
SCSIRequest req;
|
|
/* ??? We should probably keep track of whether the data transfer is
|
|
/* ??? We should probably keep track of whether the data transfer is
|
|
@@ -111,24 +107,19 @@ static void scsi_disk_clear_sense(SCSIDiskState *s)
|
|
memset(&s->sense, 0, sizeof(s->sense));
|
|
memset(&s->sense, 0, sizeof(s->sense));
|
|
}
|
|
}
|
|
|
|
|
|
-static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key)
|
|
|
|
-{
|
|
|
|
- s->sense.key = key;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code)
|
|
|
|
|
|
+static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense)
|
|
{
|
|
{
|
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
|
|
|
|
|
r->req.status = status;
|
|
r->req.status = status;
|
|
- scsi_disk_set_sense(s, sense_code);
|
|
|
|
|
|
+ s->sense = sense;
|
|
}
|
|
}
|
|
|
|
|
|
/* Helper function for command completion. */
|
|
/* Helper function for command completion. */
|
|
-static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
|
|
|
|
|
|
+static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense)
|
|
{
|
|
{
|
|
- DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
|
|
|
|
- r->req.tag, status, sense);
|
|
|
|
|
|
+ DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n",
|
|
|
|
+ r->req.tag, status, sense.key, sense.asc, sense.ascq);
|
|
scsi_req_set_status(r, status, sense);
|
|
scsi_req_set_status(r, status, sense);
|
|
scsi_req_complete(&r->req);
|
|
scsi_req_complete(&r->req);
|
|
}
|
|
}
|
|
@@ -182,7 +173,7 @@ static void scsi_read_data(SCSIRequest *req)
|
|
}
|
|
}
|
|
DPRINTF("Read sector_count=%d\n", r->sector_count);
|
|
DPRINTF("Read sector_count=%d\n", r->sector_count);
|
|
if (r->sector_count == 0) {
|
|
if (r->sector_count == 0) {
|
|
- scsi_command_complete(r, GOOD, NO_SENSE);
|
|
|
|
|
|
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -225,8 +216,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
|
|
if (type == SCSI_REQ_STATUS_RETRY_READ) {
|
|
if (type == SCSI_REQ_STATUS_RETRY_READ) {
|
|
scsi_req_data(&r->req, 0);
|
|
scsi_req_data(&r->req, 0);
|
|
}
|
|
}
|
|
- scsi_command_complete(r, CHECK_CONDITION,
|
|
|
|
- HARDWARE_ERROR);
|
|
|
|
|
|
+ if (error == ENOMEM) {
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION,
|
|
|
|
+ SENSE_CODE(TARGET_FAILURE));
|
|
|
|
+ } else {
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION,
|
|
|
|
+ SENSE_CODE(IO_ERROR));
|
|
|
|
+ }
|
|
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
|
|
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -251,7 +247,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
|
r->sector += n;
|
|
r->sector += n;
|
|
r->sector_count -= n;
|
|
r->sector_count -= n;
|
|
if (r->sector_count == 0) {
|
|
if (r->sector_count == 0) {
|
|
- scsi_command_complete(r, GOOD, NO_SENSE);
|
|
|
|
|
|
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
|
} else {
|
|
} else {
|
|
len = r->sector_count * 512;
|
|
len = r->sector_count * 512;
|
|
if (len > SCSI_DMA_BUF_SIZE) {
|
|
if (len > SCSI_DMA_BUF_SIZE) {
|
|
@@ -278,7 +274,7 @@ static int scsi_write_data(SCSIRequest *req)
|
|
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
|
|
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
|
|
scsi_write_complete, r);
|
|
scsi_write_complete, r);
|
|
if (r->req.aiocb == NULL) {
|
|
if (r->req.aiocb == NULL) {
|
|
- scsi_write_complete(r, -EIO);
|
|
|
|
|
|
+ scsi_write_complete(r, -ENOMEM);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
/* Invoke completion routine to fetch data from host. */
|
|
/* Invoke completion routine to fetch data from host. */
|
|
@@ -316,7 +312,7 @@ static void scsi_dma_restart_bh(void *opaque)
|
|
case SCSI_REQ_STATUS_RETRY_FLUSH:
|
|
case SCSI_REQ_STATUS_RETRY_FLUSH:
|
|
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
|
|
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
|
|
if (ret == 0) {
|
|
if (ret == 0) {
|
|
- scsi_command_complete(r, GOOD, NO_SENSE);
|
|
|
|
|
|
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -815,19 +811,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
|
|
case REQUEST_SENSE:
|
|
case REQUEST_SENSE:
|
|
if (req->cmd.xfer < 4)
|
|
if (req->cmd.xfer < 4)
|
|
goto illegal_request;
|
|
goto illegal_request;
|
|
- memset(outbuf, 0, 4);
|
|
|
|
- buflen = 4;
|
|
|
|
- if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) {
|
|
|
|
- memset(outbuf, 0, 18);
|
|
|
|
- buflen = 18;
|
|
|
|
- outbuf[7] = 10;
|
|
|
|
- /* asc 0x3a, ascq 0: Medium not present */
|
|
|
|
- outbuf[12] = 0x3a;
|
|
|
|
- outbuf[13] = 0;
|
|
|
|
- }
|
|
|
|
- outbuf[0] = 0xf0;
|
|
|
|
- outbuf[1] = 0;
|
|
|
|
- outbuf[2] = s->sense.key;
|
|
|
|
|
|
+ buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer,
|
|
|
|
+ req->cmd.xfer > 13);
|
|
scsi_disk_clear_sense(s);
|
|
scsi_disk_clear_sense(s);
|
|
break;
|
|
break;
|
|
case INQUIRY:
|
|
case INQUIRY:
|
|
@@ -965,17 +950,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
- goto illegal_request;
|
|
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
- scsi_req_set_status(r, GOOD, NO_SENSE);
|
|
|
|
|
|
+ scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
|
|
return buflen;
|
|
return buflen;
|
|
|
|
|
|
not_ready:
|
|
not_ready:
|
|
- scsi_command_complete(r, CHECK_CONDITION, NOT_READY);
|
|
|
|
|
|
+ if (!bdrv_is_inserted(s->bs)) {
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM));
|
|
|
|
+ } else {
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY));
|
|
|
|
+ }
|
|
return -1;
|
|
return -1;
|
|
|
|
|
|
illegal_request:
|
|
illegal_request:
|
|
- scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
|
|
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1002,7 +992,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|
|
|
|
|
if (scsi_req_parse(&r->req, buf) != 0) {
|
|
if (scsi_req_parse(&r->req, buf) != 0) {
|
|
BADF("Unsupported command length, command %x\n", command);
|
|
BADF("Unsupported command length, command %x\n", command);
|
|
- goto fail;
|
|
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
#ifdef DEBUG_SCSI
|
|
#ifdef DEBUG_SCSI
|
|
{
|
|
{
|
|
@@ -1017,8 +1008,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|
if (req->lun || buf[1] >> 5) {
|
|
if (req->lun || buf[1] >> 5) {
|
|
/* Only LUN 0 supported. */
|
|
/* Only LUN 0 supported. */
|
|
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
|
|
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
|
|
- if (command != REQUEST_SENSE && command != INQUIRY)
|
|
|
|
- goto fail;
|
|
|
|
|
|
+ if (command != REQUEST_SENSE && command != INQUIRY) {
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION,
|
|
|
|
+ SENSE_CODE(LUN_NOT_SUPPORTED));
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
switch (command) {
|
|
switch (command) {
|
|
case TEST_UNIT_READY:
|
|
case TEST_UNIT_READY:
|
|
@@ -1126,15 +1120,17 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
|
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
|
|
|
+ return 0;
|
|
fail:
|
|
fail:
|
|
- scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
|
|
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
|
|
return 0;
|
|
return 0;
|
|
illegal_lba:
|
|
illegal_lba:
|
|
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
|
|
|
|
|
|
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE));
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
if (r->sector_count == 0 && r->iov.iov_len == 0) {
|
|
if (r->sector_count == 0 && r->iov.iov_len == 0) {
|
|
- scsi_command_complete(r, GOOD, NO_SENSE);
|
|
|
|
|
|
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
|
}
|
|
}
|
|
len = r->sector_count * 512 + r->iov.iov_len;
|
|
len = r->sector_count * 512 + r->iov.iov_len;
|
|
if (is_write) {
|
|
if (is_write) {
|