|
@@ -55,7 +55,7 @@ typedef struct {
|
|
|
uint8_t id;
|
|
|
uint8_t reserved;
|
|
|
uint16_t tag;
|
|
|
-} QEMU_PACKED uas_ui_header;
|
|
|
+} QEMU_PACKED uas_iu_header;
|
|
|
|
|
|
typedef struct {
|
|
|
uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */
|
|
@@ -65,7 +65,7 @@ typedef struct {
|
|
|
uint64_t lun;
|
|
|
uint8_t cdb[16];
|
|
|
uint8_t add_cdb[];
|
|
|
-} QEMU_PACKED uas_ui_command;
|
|
|
+} QEMU_PACKED uas_iu_command;
|
|
|
|
|
|
typedef struct {
|
|
|
uint16_t status_qualifier;
|
|
@@ -73,29 +73,29 @@ typedef struct {
|
|
|
uint8_t reserved[7];
|
|
|
uint16_t sense_length;
|
|
|
uint8_t sense_data[18];
|
|
|
-} QEMU_PACKED uas_ui_sense;
|
|
|
+} QEMU_PACKED uas_iu_sense;
|
|
|
|
|
|
typedef struct {
|
|
|
- uint16_t add_response_info;
|
|
|
+ uint8_t add_response_info[3];
|
|
|
uint8_t response_code;
|
|
|
-} QEMU_PACKED uas_ui_response;
|
|
|
+} QEMU_PACKED uas_iu_response;
|
|
|
|
|
|
typedef struct {
|
|
|
uint8_t function;
|
|
|
uint8_t reserved;
|
|
|
uint16_t task_tag;
|
|
|
uint64_t lun;
|
|
|
-} QEMU_PACKED uas_ui_task_mgmt;
|
|
|
+} QEMU_PACKED uas_iu_task_mgmt;
|
|
|
|
|
|
typedef struct {
|
|
|
- uas_ui_header hdr;
|
|
|
+ uas_iu_header hdr;
|
|
|
union {
|
|
|
- uas_ui_command command;
|
|
|
- uas_ui_sense sense;
|
|
|
- uas_ui_task_mgmt task;
|
|
|
- uas_ui_response response;
|
|
|
+ uas_iu_command command;
|
|
|
+ uas_iu_sense sense;
|
|
|
+ uas_iu_task_mgmt task;
|
|
|
+ uas_iu_response response;
|
|
|
};
|
|
|
-} QEMU_PACKED uas_ui;
|
|
|
+} QEMU_PACKED uas_iu;
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
|
@@ -122,8 +122,8 @@ struct UASDevice {
|
|
|
UASRequest *dataout2;
|
|
|
|
|
|
/* usb 3.0 only */
|
|
|
- USBPacket *data3[UAS_MAX_STREAMS];
|
|
|
- USBPacket *status3[UAS_MAX_STREAMS];
|
|
|
+ USBPacket *data3[UAS_MAX_STREAMS + 1];
|
|
|
+ USBPacket *status3[UAS_MAX_STREAMS + 1];
|
|
|
};
|
|
|
|
|
|
struct UASRequest {
|
|
@@ -145,7 +145,7 @@ struct UASRequest {
|
|
|
|
|
|
struct UASStatus {
|
|
|
uint32_t stream;
|
|
|
- uas_ui status;
|
|
|
+ uas_iu status;
|
|
|
uint32_t length;
|
|
|
QTAILQ_ENTRY(UASStatus) next;
|
|
|
};
|
|
@@ -338,7 +338,7 @@ static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
|
|
|
|
|
|
st->status.hdr.id = id;
|
|
|
st->status.hdr.tag = cpu_to_be16(tag);
|
|
|
- st->length = sizeof(uas_ui_header);
|
|
|
+ st->length = sizeof(uas_iu_header);
|
|
|
if (uas_using_streams(uas)) {
|
|
|
st->stream = tag;
|
|
|
}
|
|
@@ -392,15 +392,13 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
|
|
|
- uint8_t code, uint16_t add_info)
|
|
|
+static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code)
|
|
|
{
|
|
|
UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag);
|
|
|
|
|
|
trace_usb_uas_response(uas->dev.addr, tag, code);
|
|
|
st->status.response.response_code = code;
|
|
|
- st->status.response.add_response_info = cpu_to_be16(add_info);
|
|
|
- usb_uas_queue_status(uas, st, sizeof(uas_ui_response));
|
|
|
+ usb_uas_queue_status(uas, st, sizeof(uas_iu_response));
|
|
|
}
|
|
|
|
|
|
static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
|
|
@@ -416,10 +414,28 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
|
|
|
sizeof(st->status.sense.sense_data));
|
|
|
st->status.sense.sense_length = cpu_to_be16(slen);
|
|
|
}
|
|
|
- len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen;
|
|
|
+ len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen;
|
|
|
usb_uas_queue_status(req->uas, st, len);
|
|
|
}
|
|
|
|
|
|
+static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag,
|
|
|
+ struct SCSISense sense)
|
|
|
+{
|
|
|
+ UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag);
|
|
|
+ int len, slen = 0;
|
|
|
+
|
|
|
+ st->status.sense.status = CHECK_CONDITION;
|
|
|
+ st->status.sense.status_qualifier = cpu_to_be16(0);
|
|
|
+ st->status.sense.sense_data[0] = 0x70;
|
|
|
+ st->status.sense.sense_data[2] = sense.key;
|
|
|
+ st->status.sense.sense_data[7] = 10;
|
|
|
+ st->status.sense.sense_data[12] = sense.asc;
|
|
|
+ st->status.sense.sense_data[13] = sense.ascq;
|
|
|
+ slen = 18;
|
|
|
+ len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen;
|
|
|
+ usb_uas_queue_status(uas, st, len);
|
|
|
+}
|
|
|
+
|
|
|
static void usb_uas_queue_read_ready(UASRequest *req)
|
|
|
{
|
|
|
UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
|
|
@@ -518,14 +534,14 @@ static void usb_uas_start_next_transfer(UASDevice *uas)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
|
|
|
+static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu)
|
|
|
{
|
|
|
UASRequest *req;
|
|
|
|
|
|
req = g_new0(UASRequest, 1);
|
|
|
req->uas = uas;
|
|
|
- req->tag = be16_to_cpu(ui->hdr.tag);
|
|
|
- req->lun = be64_to_cpu(ui->command.lun);
|
|
|
+ req->tag = be16_to_cpu(iu->hdr.tag);
|
|
|
+ req->lun = be64_to_cpu(iu->command.lun);
|
|
|
req->dev = usb_uas_get_dev(req->uas, req->lun);
|
|
|
return req;
|
|
|
}
|
|
@@ -648,7 +664,7 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
|
|
|
return;
|
|
|
}
|
|
|
if (uas_using_streams(uas)) {
|
|
|
- for (i = 0; i < UAS_MAX_STREAMS; i++) {
|
|
|
+ for (i = 0; i <= UAS_MAX_STREAMS; i++) {
|
|
|
if (uas->status3[i] == p) {
|
|
|
uas->status3[i] = NULL;
|
|
|
return;
|
|
@@ -668,16 +684,20 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
|
|
|
assert(!"canceled usb packet not found");
|
|
|
}
|
|
|
|
|
|
-static void usb_uas_command(UASDevice *uas, uas_ui *ui)
|
|
|
+static void usb_uas_command(UASDevice *uas, uas_iu *iu)
|
|
|
{
|
|
|
UASRequest *req;
|
|
|
uint32_t len;
|
|
|
+ uint16_t tag = be16_to_cpu(iu->hdr.tag);
|
|
|
|
|
|
- req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
|
|
|
+ if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) {
|
|
|
+ goto invalid_tag;
|
|
|
+ }
|
|
|
+ req = usb_uas_find_request(uas, tag);
|
|
|
if (req) {
|
|
|
goto overlapped_tag;
|
|
|
}
|
|
|
- req = usb_uas_alloc_request(uas, ui);
|
|
|
+ req = usb_uas_alloc_request(uas, iu);
|
|
|
if (req->dev == NULL) {
|
|
|
goto bad_target;
|
|
|
}
|
|
@@ -694,7 +714,7 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui)
|
|
|
|
|
|
req->req = scsi_req_new(req->dev, req->tag,
|
|
|
usb_uas_get_lun(req->lun),
|
|
|
- ui->command.cdb, req);
|
|
|
+ iu->command.cdb, req);
|
|
|
if (uas->requestlog) {
|
|
|
scsi_req_print(req->req);
|
|
|
}
|
|
@@ -705,105 +725,97 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui)
|
|
|
}
|
|
|
return;
|
|
|
|
|
|
+invalid_tag:
|
|
|
+ usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG);
|
|
|
+ return;
|
|
|
+
|
|
|
overlapped_tag:
|
|
|
- usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
|
|
|
+ usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS);
|
|
|
return;
|
|
|
|
|
|
bad_target:
|
|
|
- /*
|
|
|
- * FIXME: Seems to upset linux, is this wrong?
|
|
|
- * NOTE: Happens only with no scsi devices at the bus, not sure
|
|
|
- * this is a valid UAS setup in the first place.
|
|
|
- */
|
|
|
- usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
|
|
|
+ usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED);
|
|
|
g_free(req);
|
|
|
}
|
|
|
|
|
|
-static void usb_uas_task(UASDevice *uas, uas_ui *ui)
|
|
|
+static void usb_uas_task(UASDevice *uas, uas_iu *iu)
|
|
|
{
|
|
|
- uint16_t tag = be16_to_cpu(ui->hdr.tag);
|
|
|
- uint64_t lun64 = be64_to_cpu(ui->task.lun);
|
|
|
+ uint16_t tag = be16_to_cpu(iu->hdr.tag);
|
|
|
+ uint64_t lun64 = be64_to_cpu(iu->task.lun);
|
|
|
SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
|
|
|
int lun = usb_uas_get_lun(lun64);
|
|
|
UASRequest *req;
|
|
|
uint16_t task_tag;
|
|
|
|
|
|
- req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
|
|
|
+ if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) {
|
|
|
+ goto invalid_tag;
|
|
|
+ }
|
|
|
+ req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag));
|
|
|
if (req) {
|
|
|
goto overlapped_tag;
|
|
|
}
|
|
|
+ if (dev == NULL) {
|
|
|
+ goto incorrect_lun;
|
|
|
+ }
|
|
|
|
|
|
- switch (ui->task.function) {
|
|
|
+ switch (iu->task.function) {
|
|
|
case UAS_TMF_ABORT_TASK:
|
|
|
- task_tag = be16_to_cpu(ui->task.task_tag);
|
|
|
+ task_tag = be16_to_cpu(iu->task.task_tag);
|
|
|
trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
|
|
|
- if (dev == NULL) {
|
|
|
- goto bad_target;
|
|
|
- }
|
|
|
- if (dev->lun != lun) {
|
|
|
- goto incorrect_lun;
|
|
|
- }
|
|
|
req = usb_uas_find_request(uas, task_tag);
|
|
|
if (req && req->dev == dev) {
|
|
|
scsi_req_cancel(req->req);
|
|
|
}
|
|
|
- usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
|
|
|
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE);
|
|
|
break;
|
|
|
|
|
|
case UAS_TMF_LOGICAL_UNIT_RESET:
|
|
|
trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
|
|
|
- if (dev == NULL) {
|
|
|
- goto bad_target;
|
|
|
- }
|
|
|
- if (dev->lun != lun) {
|
|
|
- goto incorrect_lun;
|
|
|
- }
|
|
|
qdev_reset_all(&dev->qdev);
|
|
|
- usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
|
|
|
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
- trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function);
|
|
|
- usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0);
|
|
|
+ trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function);
|
|
|
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED);
|
|
|
break;
|
|
|
}
|
|
|
return;
|
|
|
|
|
|
-overlapped_tag:
|
|
|
- usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
|
|
|
+invalid_tag:
|
|
|
+ usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT);
|
|
|
return;
|
|
|
|
|
|
-bad_target:
|
|
|
- /* FIXME: correct? [see long comment in usb_uas_command()] */
|
|
|
- usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0);
|
|
|
+overlapped_tag:
|
|
|
+ usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG);
|
|
|
return;
|
|
|
|
|
|
incorrect_lun:
|
|
|
- usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
|
|
|
+ usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN);
|
|
|
}
|
|
|
|
|
|
static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
|
|
|
{
|
|
|
UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
|
|
|
- uas_ui ui;
|
|
|
+ uas_iu iu;
|
|
|
UASStatus *st;
|
|
|
UASRequest *req;
|
|
|
int length;
|
|
|
|
|
|
switch (p->ep->nr) {
|
|
|
case UAS_PIPE_ID_COMMAND:
|
|
|
- length = MIN(sizeof(ui), p->iov.size);
|
|
|
- usb_packet_copy(p, &ui, length);
|
|
|
- switch (ui.hdr.id) {
|
|
|
+ length = MIN(sizeof(iu), p->iov.size);
|
|
|
+ usb_packet_copy(p, &iu, length);
|
|
|
+ switch (iu.hdr.id) {
|
|
|
case UAS_UI_COMMAND:
|
|
|
- usb_uas_command(uas, &ui);
|
|
|
+ usb_uas_command(uas, &iu);
|
|
|
break;
|
|
|
case UAS_UI_TASK_MGMT:
|
|
|
- usb_uas_task(uas, &ui);
|
|
|
+ usb_uas_task(uas, &iu);
|
|
|
break;
|
|
|
default:
|
|
|
- fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
|
|
|
- __func__, ui.hdr.id);
|
|
|
+ fprintf(stderr, "%s: unknown command iu: id 0x%x\n",
|
|
|
+ __func__, iu.hdr.id);
|
|
|
p->status = USB_RET_STALL;
|
|
|
break;
|
|
|
}
|