uas-uas: usb3 streams

Add usb3 streams support to the uas (usb attached scsi) emulation.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Gerd Hoffmann 2013-01-25 17:38:59 +01:00
parent 024426acc0
commit 89a453d4a5

View File

@ -99,6 +99,9 @@ typedef struct {
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
#define UAS_STREAM_BM_ATTR 4
#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR)
typedef struct UASDevice UASDevice; typedef struct UASDevice UASDevice;
typedef struct UASRequest UASRequest; typedef struct UASRequest UASRequest;
typedef struct UASStatus UASStatus; typedef struct UASStatus UASStatus;
@ -106,12 +109,18 @@ typedef struct UASStatus UASStatus;
struct UASDevice { struct UASDevice {
USBDevice dev; USBDevice dev;
SCSIBus bus; SCSIBus bus;
UASRequest *datain;
UASRequest *dataout;
USBPacket *status;
QEMUBH *status_bh; QEMUBH *status_bh;
QTAILQ_HEAD(, UASStatus) results; QTAILQ_HEAD(, UASStatus) results;
QTAILQ_HEAD(, UASRequest) requests; QTAILQ_HEAD(, UASRequest) requests;
/* usb 2.0 only */
USBPacket *status2;
UASRequest *datain2;
UASRequest *dataout2;
/* usb 3.0 only */
USBPacket *data3[UAS_MAX_STREAMS];
USBPacket *status3[UAS_MAX_STREAMS];
}; };
struct UASRequest { struct UASRequest {
@ -132,6 +141,7 @@ struct UASRequest {
}; };
struct UASStatus { struct UASStatus {
uint32_t stream;
uas_ui status; uas_ui status;
uint32_t length; uint32_t length;
QTAILQ_ENTRY(UASStatus) next; QTAILQ_ENTRY(UASStatus) next;
@ -144,6 +154,7 @@ enum {
STR_PRODUCT, STR_PRODUCT,
STR_SERIALNUMBER, STR_SERIALNUMBER,
STR_CONFIG_HIGH, STR_CONFIG_HIGH,
STR_CONFIG_SUPER,
}; };
static const USBDescStrings desc_strings = { static const USBDescStrings desc_strings = {
@ -151,6 +162,7 @@ static const USBDescStrings desc_strings = {
[STR_PRODUCT] = "USB Attached SCSI HBA", [STR_PRODUCT] = "USB Attached SCSI HBA",
[STR_SERIALNUMBER] = "27842", [STR_SERIALNUMBER] = "27842",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)", [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_high = {
@ -204,6 +216,64 @@ 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 = { static const USBDescDevice desc_device_high = {
.bcdUSB = 0x0200, .bcdUSB = 0x0200,
.bMaxPacketSize0 = 64, .bMaxPacketSize0 = 64,
@ -220,6 +290,22 @@ static const USBDescDevice desc_device_high = {
}, },
}; };
static const USBDescDevice desc_device_super = {
.bcdUSB = 0x0300,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_SUPER,
.bmAttributes = 0xc0,
.nif = 1,
.ifs = &desc_iface_super,
},
},
};
static const USBDesc desc = { static const USBDesc desc = {
.id = { .id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */ .idVendor = 0x46f4, /* CRC16() of "QEMU" */
@ -229,45 +315,68 @@ static const USBDesc desc = {
.iProduct = STR_PRODUCT, .iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER, .iSerialNumber = STR_SERIALNUMBER,
}, },
.high = &desc_device_high, .high = &desc_device_high,
.str = desc_strings, .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); UASStatus *st = g_new0(UASStatus, 1);
st->status.hdr.id = id; st->status.hdr.id = id;
st->status.hdr.tag = cpu_to_be16(tag); st->status.hdr.tag = cpu_to_be16(tag);
st->length = sizeof(uas_ui_header); st->length = sizeof(uas_ui_header);
if (uas_using_streams(uas)) {
st->stream = tag;
}
return st; return st;
} }
static void usb_uas_send_status_bh(void *opaque) static void usb_uas_send_status_bh(void *opaque)
{ {
UASDevice *uas = opaque; UASDevice *uas = opaque;
UASStatus *st = QTAILQ_FIRST(&uas->results); UASStatus *st;
USBPacket *p = uas->status; USBPacket *p;
assert(p != NULL); while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
assert(st != 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);
usb_packet_copy(p, &st->status, st->length); QTAILQ_REMOVE(&uas->results, st, next);
QTAILQ_REMOVE(&uas->results, st, next); g_free(st);
g_free(st);
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
usb_packet_complete(&uas->dev, p); usb_packet_complete(&uas->dev, p);
}
} }
static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) 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; st->length += length;
QTAILQ_INSERT_TAIL(&uas->results, st, next); QTAILQ_INSERT_TAIL(&uas->results, st, next);
if (uas->status) { if (p) {
/* /*
* Just schedule bh make sure any in-flight data transaction * Just schedule bh make sure any in-flight data transaction
* is finished before completing (sending) the status packet. * is finished before completing (sending) the status packet.
@ -276,14 +385,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
} else { } else {
USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN, USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
UAS_PIPE_ID_STATUS); UAS_PIPE_ID_STATUS);
usb_wakeup(ep, 0); usb_wakeup(ep, st->stream);
} }
} }
static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
uint8_t code, uint16_t add_info) uint8_t code, uint16_t add_info)
{ {
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); trace_usb_uas_response(uas->dev.addr, tag, code);
st->status.response.response_code = code; st->status.response.response_code = code;
@ -293,7 +402,7 @@ static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
static void usb_uas_queue_sense(UASRequest *req, uint8_t status) 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; int len, slen = 0;
trace_usb_uas_sense(req->uas->dev.addr, req->tag, status); trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
@ -310,7 +419,8 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
static void usb_uas_queue_read_ready(UASRequest *req) 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); trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0); usb_uas_queue_status(req->uas, st, 0);
@ -318,7 +428,8 @@ static void usb_uas_queue_read_ready(UASRequest *req)
static void usb_uas_queue_write_ready(UASRequest *req) 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); trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0); usb_uas_queue_status(req->uas, st, 0);
@ -381,18 +492,22 @@ static void usb_uas_start_next_transfer(UASDevice *uas)
{ {
UASRequest *req; UASRequest *req;
if (uas_using_streams(uas)) {
return;
}
QTAILQ_FOREACH(req, &uas->requests, next) { QTAILQ_FOREACH(req, &uas->requests, next) {
if (req->active || req->complete) { if (req->active || req->complete) {
continue; continue;
} }
if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) { if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
uas->datain = req; uas->datain2 = req;
usb_uas_queue_read_ready(req); usb_uas_queue_read_ready(req);
req->active = true; req->active = true;
return; return;
} }
if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) { if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
uas->dataout = req; uas->dataout2 = req;
usb_uas_queue_write_ready(req); usb_uas_queue_write_ready(req);
req->active = true; req->active = true;
return; return;
@ -417,11 +532,11 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
UASRequest *req = priv; UASRequest *req = priv;
UASDevice *uas = req->uas; UASDevice *uas = req->uas;
if (req == uas->datain) { if (req == uas->datain2) {
uas->datain = NULL; uas->datain2 = NULL;
} }
if (req == uas->dataout) { if (req == uas->dataout2) {
uas->dataout = NULL; uas->dataout2 = NULL;
} }
QTAILQ_REMOVE(&uas->requests, req, next); QTAILQ_REMOVE(&uas->requests, req, next);
g_free(req); g_free(req);
@ -522,12 +637,25 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
{ {
UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
UASRequest *req, *nreq; UASRequest *req, *nreq;
int i;
if (uas->status == p) { if (uas->status2 == p) {
uas->status = NULL; uas->status2 = NULL;
qemu_bh_cancel(uas->status_bh); qemu_bh_cancel(uas->status_bh);
return; 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) { QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
if (req->data == p) { if (req->data == p) {
req->data = NULL; req->data = NULL;
@ -555,9 +683,18 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui)
usb_uas_get_lun(req->lun), usb_uas_get_lun(req->lun),
req->lun >> 32, req->lun & 0xffffffff); req->lun >> 32, req->lun & 0xffffffff);
QTAILQ_INSERT_TAIL(&uas->requests, req, next); 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, req->req = scsi_req_new(req->dev, req->tag,
usb_uas_get_lun(req->lun), usb_uas_get_lun(req->lun),
ui->command.cdb, req); ui->command.cdb, req);
#if 1
scsi_req_print(req->req);
#endif
len = scsi_req_enqueue(req->req); len = scsi_req_enqueue(req->req);
if (len) { if (len) {
req->data_size = len; req->data_size = len;
@ -669,12 +806,26 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
} }
break; break;
case UAS_PIPE_ID_STATUS: case UAS_PIPE_ID_STATUS:
st = QTAILQ_FIRST(&uas->results); if (p->stream) {
if (st == NULL) { QTAILQ_FOREACH(st, &uas->results, next) {
assert(uas->status == NULL); if (st->stream == p->stream) {
uas->status = p; break;
p->status = USB_RET_ASYNC; }
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); usb_packet_copy(p, &st->status, st->length);
QTAILQ_REMOVE(&uas->results, st, next); QTAILQ_REMOVE(&uas->results, st, next);
@ -682,11 +833,23 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
break; break;
case UAS_PIPE_ID_DATA_IN: case UAS_PIPE_ID_DATA_IN:
case UAS_PIPE_ID_DATA_OUT: 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) { if (req == NULL) {
fprintf(stderr, "%s: no inflight request\n", __func__); if (p->stream) {
p->status = USB_RET_STALL; assert(uas->data3[p->stream] == NULL);
break; uas->data3[p->stream] = p;
p->status = USB_RET_ASYNC;
break;
} else {
fprintf(stderr, "%s: no inflight request\n", __func__);
p->status = USB_RET_STALL;
break;
}
} }
scsi_req_ref(req->req); scsi_req_ref(req->req);
req->data = p; req->data = p;