* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
-#include "qemu-option.h"
-#include "qemu-config.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
#include "trace.h"
+#include "qemu/error-report.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
-#include "hw/scsi.h"
-#include "hw/scsi-defs.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
/* --------------------------------------------------------------------- */
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 */
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;
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;
/* --------------------------------------------------------------------- */
+#define UAS_STREAM_BM_ATTR 4
+#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR)
+
typedef struct UASDevice UASDevice;
typedef struct UASRequest UASRequest;
typedef struct UASStatus UASStatus;
struct UASDevice {
USBDevice dev;
SCSIBus bus;
- UASRequest *datain;
- UASRequest *dataout;
- USBPacket *status;
QEMUBH *status_bh;
QTAILQ_HEAD(, UASStatus) results;
QTAILQ_HEAD(, UASRequest) requests;
+
+ /* properties */
+ uint32_t requestlog;
+
+ /* usb 2.0 only */
+ USBPacket *status2;
+ UASRequest *datain2;
+ UASRequest *dataout2;
+
+ /* usb 3.0 only */
+ USBPacket *data3[UAS_MAX_STREAMS + 1];
+ USBPacket *status3[UAS_MAX_STREAMS + 1];
};
+#define TYPE_USB_UAS "usb-uas"
+#define USB_UAS(obj) OBJECT_CHECK(UASDevice, (obj), TYPE_USB_UAS)
+
struct UASRequest {
uint16_t tag;
uint64_t lun;
};
struct UASStatus {
- uas_ui status;
+ uint32_t stream;
+ uas_iu status;
uint32_t length;
QTAILQ_ENTRY(UASStatus) next;
};
STR_PRODUCT,
STR_SERIALNUMBER,
STR_CONFIG_HIGH,
+ STR_CONFIG_SUPER,
};
static const USBDescStrings desc_strings = {
[STR_PRODUCT] = "USB Attached SCSI HBA",
[STR_SERIALNUMBER] = "27842",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+ [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
};
static const USBDescIface desc_iface_high = {
}
};
+static const USBDescIface desc_iface_super = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 4,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x62, /* UAS */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_COMMAND,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_STATUS,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_IN,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_OUT,
+ 0x00, /* u8 bReserved */
+ },
+ },
+ }
+};
+
static const USBDescDevice desc_device_high = {
.bcdUSB = 0x0200,
.bMaxPacketSize0 = 64,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_HIGH,
- .bmAttributes = 0xc0,
+ .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
.nif = 1,
.ifs = &desc_iface_high,
},
},
};
+static const USBDescDevice desc_device_super = {
+ .bcdUSB = 0x0300,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_SUPER,
+ .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
+ .nif = 1,
+ .ifs = &desc_iface_super,
+ },
+ },
+};
+
static const USBDesc desc = {
.id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
- .high = &desc_device_high,
- .str = desc_strings,
+ .high = &desc_device_high,
+ .super = &desc_device_super,
+ .str = desc_strings,
};
/* --------------------------------------------------------------------- */
-static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag)
+static bool uas_using_streams(UASDevice *uas)
+{
+ return uas->dev.speed == USB_SPEED_SUPER;
+}
+
+/* --------------------------------------------------------------------- */
+
+static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
{
UASStatus *st = g_new0(UASStatus, 1);
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;
+ }
return st;
}
static void usb_uas_send_status_bh(void *opaque)
{
UASDevice *uas = opaque;
- UASStatus *st = QTAILQ_FIRST(&uas->results);
- USBPacket *p = uas->status;
+ UASStatus *st;
+ USBPacket *p;
- assert(p != NULL);
- assert(st != NULL);
+ while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
+ if (uas_using_streams(uas)) {
+ p = uas->status3[st->stream];
+ uas->status3[st->stream] = NULL;
+ } else {
+ p = uas->status2;
+ uas->status2 = NULL;
+ }
+ if (p == NULL) {
+ break;
+ }
- uas->status = NULL;
- usb_packet_copy(p, &st->status, st->length);
- QTAILQ_REMOVE(&uas->results, st, next);
- g_free(st);
+ usb_packet_copy(p, &st->status, st->length);
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
- p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
- usb_packet_complete(&uas->dev, p);
+ p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
+ usb_packet_complete(&uas->dev, p);
+ }
}
static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
{
+ USBPacket *p = uas_using_streams(uas) ?
+ uas->status3[st->stream] : uas->status2;
+
st->length += length;
QTAILQ_INSERT_TAIL(&uas->results, st, next);
- if (uas->status) {
+ if (p) {
/*
* Just schedule bh make sure any in-flight data transaction
* is finished before completing (sending) the status packet.
} else {
USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
UAS_PIPE_ID_STATUS);
- usb_wakeup(ep);
+ usb_wakeup(ep, st->stream);
}
}
-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_UI_RESPONSE, tag);
+ 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)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
int len, slen = 0;
trace_usb_uas_sense(req->uas->dev.addr, req->tag, 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(UAS_UI_READ_READY, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
+ req->tag);
trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0);
static void usb_uas_queue_write_ready(UASRequest *req)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
+ req->tag);
trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0);
{
UASRequest *req;
+ if (uas_using_streams(uas)) {
+ return;
+ }
+
QTAILQ_FOREACH(req, &uas->requests, next) {
if (req->active || req->complete) {
continue;
}
- if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
- uas->datain = req;
+ if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
+ uas->datain2 = req;
usb_uas_queue_read_ready(req);
req->active = true;
return;
}
- if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) {
- uas->dataout = req;
+ if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
+ uas->dataout2 = req;
usb_uas_queue_write_ready(req);
req->active = true;
return;
}
}
-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;
}
UASRequest *req = priv;
UASDevice *uas = req->uas;
- if (req == uas->datain) {
- uas->datain = NULL;
+ if (req == uas->datain2) {
+ uas->datain2 = NULL;
}
- if (req == uas->dataout) {
- uas->dataout = NULL;
+ if (req == uas->dataout2) {
+ uas->dataout2 = NULL;
}
QTAILQ_REMOVE(&uas->requests, req, next);
g_free(req);
static void usb_uas_handle_reset(USBDevice *dev)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
UASRequest *req, *nreq;
UASStatus *st, *nst;
if (ret >= 0) {
return;
}
- fprintf(stderr, "%s: unhandled control request\n", __func__);
+ error_report("%s: unhandled control request", __func__);
p->status = USB_RET_STALL;
}
static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
UASRequest *req, *nreq;
+ int i;
- if (uas->status == p) {
- uas->status = NULL;
+ if (uas->status2 == p) {
+ uas->status2 = NULL;
qemu_bh_cancel(uas->status_bh);
return;
}
+ if (uas_using_streams(uas)) {
+ for (i = 0; i <= UAS_MAX_STREAMS; i++) {
+ if (uas->status3[i] == p) {
+ uas->status3[i] = NULL;
+ return;
+ }
+ if (uas->data3[i] == p) {
+ uas->data3[i] = NULL;
+ return;
+ }
+ }
+ }
QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
if (req->data == p) {
req->data = NULL;
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;
}
usb_uas_get_lun(req->lun),
req->lun >> 32, req->lun & 0xffffffff);
QTAILQ_INSERT_TAIL(&uas->requests, req, next);
+ if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
+ req->data = uas->data3[req->tag];
+ req->data_async = true;
+ uas->data3[req->tag] = NULL;
+ }
+
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);
+ }
len = scsi_req_enqueue(req->req);
if (len) {
req->data_size = len;
}
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;
+ UASDevice *uas = USB_UAS(dev);
+ 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);
+ error_report("%s: unknown command iu: id 0x%x",
+ __func__, iu.hdr.id);
p->status = USB_RET_STALL;
break;
}
break;
case UAS_PIPE_ID_STATUS:
- st = QTAILQ_FIRST(&uas->results);
- if (st == NULL) {
- assert(uas->status == NULL);
- uas->status = p;
- p->status = USB_RET_ASYNC;
- break;
+ if (p->stream) {
+ QTAILQ_FOREACH(st, &uas->results, next) {
+ if (st->stream == p->stream) {
+ break;
+ }
+ }
+ if (st == NULL) {
+ assert(uas->status3[p->stream] == NULL);
+ uas->status3[p->stream] = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ }
+ } else {
+ st = QTAILQ_FIRST(&uas->results);
+ if (st == NULL) {
+ assert(uas->status2 == NULL);
+ uas->status2 = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ }
}
usb_packet_copy(p, &st->status, st->length);
QTAILQ_REMOVE(&uas->results, st, next);
break;
case UAS_PIPE_ID_DATA_IN:
case UAS_PIPE_ID_DATA_OUT:
- req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
+ if (p->stream) {
+ req = usb_uas_find_request(uas, p->stream);
+ } else {
+ req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
+ ? uas->datain2 : uas->dataout2;
+ }
if (req == NULL) {
- fprintf(stderr, "%s: no inflight request\n", __func__);
- p->status = USB_RET_STALL;
- break;
+ if (p->stream) {
+ assert(uas->data3[p->stream] == NULL);
+ uas->data3[p->stream] = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ } else {
+ error_report("%s: no inflight request", __func__);
+ p->status = USB_RET_STALL;
+ break;
+ }
}
scsi_req_ref(req->req);
req->data = p;
usb_uas_start_next_transfer(uas);
break;
default:
- fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
+ error_report("%s: invalid endpoint %d", __func__, p->ep->nr);
p->status = USB_RET_STALL;
break;
}
static void usb_uas_handle_destroy(USBDevice *dev)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
qemu_bh_delete(uas->status_bh);
}
-static int usb_uas_init(USBDevice *dev)
+static void usb_uas_realize(USBDevice *dev, Error **errp)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
QTAILQ_INIT(&uas->requests);
uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas);
- scsi_bus_new(&uas->bus, &uas->dev.qdev, &usb_uas_scsi_info);
-
- return 0;
+ scsi_bus_new(&uas->bus, sizeof(uas->bus), DEVICE(dev),
+ &usb_uas_scsi_info, NULL);
}
static const VMStateDescription vmstate_usb_uas = {
}
};
+static Property uas_properties[] = {
+ DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void usb_uas_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- uc->init = usb_uas_init;
+ uc->realize = usb_uas_realize;
uc->product_desc = desc_strings[STR_PRODUCT];
uc->usb_desc = &desc;
uc->cancel_packet = usb_uas_cancel_io;
uc->handle_control = usb_uas_handle_control;
uc->handle_data = usb_uas_handle_data;
uc->handle_destroy = usb_uas_handle_destroy;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->fw_name = "storage";
dc->vmsd = &vmstate_usb_uas;
+ dc->props = uas_properties;
}
-static TypeInfo uas_info = {
- .name = "usb-uas",
+static const TypeInfo uas_info = {
+ .name = TYPE_USB_UAS,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(UASDevice),
.class_init = usb_uas_class_initfn,