diff options
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/Makefile.objs | 30 | ||||
-rw-r--r-- | hw/usb/core.c | 35 | ||||
-rw-r--r-- | hw/usb/desc.c | 9 | ||||
-rw-r--r-- | hw/usb/dev-bluetooth.c | 2 | ||||
-rw-r--r-- | hw/usb/dev-hid.c | 2 | ||||
-rw-r--r-- | hw/usb/dev-hub.c | 10 | ||||
-rw-r--r-- | hw/usb/dev-network.c | 2 | ||||
-rw-r--r-- | hw/usb/dev-smartcard-reader.c | 2 | ||||
-rw-r--r-- | hw/usb/dev-storage.c | 5 | ||||
-rw-r--r-- | hw/usb/dev-uas.c | 247 | ||||
-rw-r--r-- | hw/usb/dev-wacom.c | 4 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 7 | ||||
-rw-r--r-- | hw/usb/hcd-musb.c | 2 | ||||
-rw-r--r-- | hw/usb/hcd-ohci.c | 4 | ||||
-rw-r--r-- | hw/usb/hcd-uhci.c | 2 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 275 | ||||
-rw-r--r-- | hw/usb/host-bsd.c | 6 | ||||
-rw-r--r-- | hw/usb/host-legacy.c | 144 | ||||
-rw-r--r-- | hw/usb/host-linux.c | 125 | ||||
-rw-r--r-- | hw/usb/host-stub.c | 5 | ||||
-rw-r--r-- | hw/usb/host.h | 44 | ||||
-rw-r--r-- | hw/usb/redirect.c | 18 |
22 files changed, 701 insertions, 279 deletions
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index d1bbbc0..e63e287 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,14 +1,30 @@ +# usb subsystem core +common-obj-y += core.o combined-packet.o bus.o desc.o +common-obj-y += libhw.o + +# usb host adapters common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o -common-obj-y += libhw.o +# emulated usb devices +common-obj-y += dev-hub.o +common-obj-y += dev-hid.o +common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o +common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o +common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o +common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o +common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o +common-obj-$(CONFIG_USB_NETWORK) += dev-network.o + +# FIXME: make configurable too +CONFIG_USB_BLUETOOTH := y +common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o +common-obj-$(CONFIG_USB_SMARTCARD) += dev-smartcard-reader.o + +# usb redirection common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o -common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o -common-obj-y += host-$(HOST_USB).o dev-bluetooth.o -common-obj-y += dev-hid.o dev-storage.o dev-wacom.o -common-obj-y += dev-serial.o dev-network.o dev-audio.o -common-obj-y += dev-smartcard-reader.o -common-obj-y += dev-uas.o +# usb pass-through +common-obj-y += $(patsubst %,host-%.o,$(HOST_USB)) diff --git a/hw/usb/core.c b/hw/usb/core.c index d057aab..15a150a 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -71,7 +71,7 @@ void usb_device_reset(USBDevice *dev) usb_device_handle_reset(dev); } -void usb_wakeup(USBEndpoint *ep) +void usb_wakeup(USBEndpoint *ep, unsigned int stream) { USBDevice *dev = ep->dev; USBBus *bus = usb_bus_from_device(dev); @@ -80,7 +80,7 @@ void usb_wakeup(USBEndpoint *ep) dev->port->ops->wakeup(dev->port); } if (bus->ops->wakeup_endpoint) { - bus->ops->wakeup_endpoint(bus, ep); + bus->ops->wakeup_endpoint(bus, ep, stream); } } @@ -545,14 +545,16 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) p->state = state; } -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, - bool short_not_ok, bool int_req) +void usb_packet_setup(USBPacket *p, int pid, + USBEndpoint *ep, unsigned int stream, + uint64_t id, bool short_not_ok, bool int_req) { assert(!usb_packet_is_inflight(p)); assert(p->iov.iov != NULL); p->id = id; p->pid = pid; p->ep = ep; + p->stream = stream; p->status = USB_RET_SUCCESS; p->actual_length = 0; p->parameter = 0; @@ -570,15 +572,17 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) { + QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; + assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= p->iov.size); + assert(p->actual_length + bytes <= iov->size); switch (p->pid) { case USB_TOKEN_SETUP: case USB_TOKEN_OUT: - iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes); + iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); break; case USB_TOKEN_IN: - iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes); + iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); break; default: fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); @@ -589,14 +593,21 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) void usb_packet_skip(USBPacket *p, size_t bytes) { + QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; + assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= p->iov.size); + assert(p->actual_length + bytes <= iov->size); if (p->pid == USB_TOKEN_IN) { - iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes); + iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes); } p->actual_length += bytes; } +size_t usb_packet_size(USBPacket *p) +{ + return p->combined ? p->combined->iov.size : p->iov.size; +} + void usb_packet_cleanup(USBPacket *p) { assert(!usb_packet_is_inflight(p)); @@ -755,6 +766,12 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) uep->pipeline = enabled; } +void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + uep->halted = halted; +} + USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, uint64_t id) { diff --git a/hw/usb/desc.c b/hw/usb/desc.c index b7c3233..b389381 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -225,12 +225,9 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, d->u.endpoint.bRefresh = ep->bRefresh; d->u.endpoint.bSynchAddress = ep->bSynchAddress; } - if (ep->extra) { - memcpy(dest + bLength, ep->extra, extralen); - } if (superlen) { - USBDescriptor *d = (void *)(dest + bLength + extralen); + USBDescriptor *d = (void *)(dest + bLength); d->bLength = 0x06; d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; @@ -243,6 +240,10 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, usb_hi(ep->wBytesPerInterval); } + if (ep->extra) { + memcpy(dest + bLength + superlen, ep->extra, extralen); + } + return bLength + extralen + superlen; } diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index adbf9d4..0f8aa48 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -478,7 +478,7 @@ static void usb_bt_out_hci_packet_event(void *opaque, struct USBBtState *s = (struct USBBtState *) opaque; if (s->evt.len == 0) { - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } usb_bt_fifo_enqueue(&s->evt, data, len); } diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 29b6481..9701048 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -423,7 +423,7 @@ static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - usb_wakeup(us->intr); + usb_wakeup(us->intr, 0); } static void usb_hid_handle_reset(USBDevice *dev) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 79f2f46..504c98c 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -164,7 +164,7 @@ static void usb_hub_attach(USBPort *port1) } else { port->wPortStatus &= ~PORT_STAT_LOW_SPEED; } - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_hub_detach(USBPort *port1) @@ -173,7 +173,7 @@ static void usb_hub_detach(USBPort *port1) USBHubPort *port = &s->ports[port1->index]; trace_usb_hub_detach(s->dev.addr, port1->index + 1); - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); /* Let upstream know the device on this port is gone */ s->dev.port->ops->child_detach(s->dev.port, port1->dev); @@ -184,7 +184,7 @@ static void usb_hub_detach(USBPort *port1) port->wPortStatus &= ~PORT_STAT_ENABLE; port->wPortChange |= PORT_STAT_C_ENABLE; } - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_hub_child_detach(USBPort *port1, USBDevice *child) @@ -202,7 +202,7 @@ static void usb_hub_wakeup(USBPort *port1) if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } } @@ -364,7 +364,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, port->wPortChange |= PORT_STAT_C_RESET; /* set enable bit */ port->wPortStatus |= PORT_STAT_ENABLE; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } break; case PORT_POWER: diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index a01a5e7..c08718b 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -855,7 +855,7 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length) g_malloc0(sizeof(struct rndis_response) + length); if (QTAILQ_EMPTY(&s->rndis_resp)) { - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 979a473..caebc1c 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -839,7 +839,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full) s->bmSlotICCState |= SLOT_0_CHANGED_MASK; } s->notify_slot_change = true; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void ccid_write_data_block_error( diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index b89d00f..d3f01aa 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -400,6 +400,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) struct usb_msd_cbw cbw; uint8_t devep = p->ep->nr; SCSIDevice *scsi_dev; + uint32_t len; switch (p->pid) { case USB_TOKEN_OUT: @@ -441,8 +442,8 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) #ifdef DEBUG_MSD scsi_req_print(s->req); #endif - scsi_req_enqueue(s->req); - if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) { + len = scsi_req_enqueue(s->req); + if (len) { scsi_req_continue(s->req); } break; diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index d904d1a..1ac5117 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -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 UASRequest UASRequest; typedef struct UASStatus UASStatus; @@ -106,12 +109,18 @@ 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; + + /* 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 { @@ -132,6 +141,7 @@ struct UASRequest { }; struct UASStatus { + uint32_t stream; uas_ui status; uint32_t length; QTAILQ_ENTRY(UASStatus) next; @@ -144,6 +154,7 @@ enum { STR_PRODUCT, STR_SERIALNUMBER, STR_CONFIG_HIGH, + STR_CONFIG_SUPER, }; static const USBDescStrings desc_strings = { @@ -151,6 +162,7 @@ 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 = { @@ -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 = { .bcdUSB = 0x0200, .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 = { .id = { .idVendor = 0x46f4, /* CRC16() of "QEMU" */ @@ -229,45 +315,68 @@ static const USBDesc desc = { .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); + 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. @@ -276,14 +385,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) } 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) { - 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; @@ -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) { - 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); @@ -310,7 +419,8 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status) 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); @@ -318,7 +428,8 @@ static void usb_uas_queue_read_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); usb_uas_queue_status(req->uas, st, 0); @@ -381,18 +492,22 @@ static void usb_uas_start_next_transfer(UASDevice *uas) { 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; @@ -417,11 +532,11 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv) 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); @@ -522,12 +637,25 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) { UASDevice *uas = DO_UPCAST(UASDevice, dev, 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; @@ -555,9 +683,18 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) 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); +#if 1 + scsi_req_print(req->req); +#endif len = scsi_req_enqueue(req->req); if (len) { req->data_size = len; @@ -669,12 +806,26 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) } 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); @@ -682,11 +833,23 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) 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 { + fprintf(stderr, "%s: no inflight request\n", __func__); + p->status = USB_RET_STALL; + break; + } } scsi_req_ref(req->req); req->data = p; diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index ab9fa2e..3be5cde 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -138,7 +138,7 @@ static void usb_mouse_event(void *opaque, s->dz += dz1; s->buttons_state = buttons_state; s->changed = 1; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_wacom_event(void *opaque, @@ -152,7 +152,7 @@ static void usb_wacom_event(void *opaque, s->dz += dz; s->buttons_state = buttons_state; s->changed = 1; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static inline int int_clamp(int val, int vmin, int vmax) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 7040659..5176251 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -874,7 +874,8 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], return 0; } -static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, + unsigned int stream) { EHCIState *s = container_of(bus, EHCIState, bus); uint32_t portsc = s->portsc[ep->dev->port->index]; @@ -1420,7 +1421,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) } spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); - usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd, + usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd, (p->qtd.token & QTD_TOKEN_IOC) != 0); usb_packet_map(&p->packet, &p->sgl); p->async = EHCI_ASYNC_INITIALIZED; @@ -1493,7 +1494,7 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep, addr, false, + usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false, (itd->transact[i] & ITD_XACT_IOC) != 0); usb_packet_map(&ehci->ipacket, &ehci->isgl); usb_handle_packet(dev, &ehci->ipacket); diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 64e9e83..7968e17 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -625,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, /* A wild guess on the FADDR semantics... */ dev = usb_find_device(&s->port, ep->faddr[idx]); uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - usb_packet_setup(&ep->packey[dir].p, pid, uep, + usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, (dev->addr << 16) | (uep->nr << 8) | pid, false, true); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index dd9967b..51241cd 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -830,7 +830,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, OHCI_BM(iso_td.flags, TD_DI) == 0; dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req); + usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); usb_handle_packet(dev, &ohci->usb_packet); if (ohci->usb_packet.status == USB_RET_ASYNC) { @@ -1034,7 +1034,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r, + usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); usb_handle_packet(dev, &ohci->usb_packet); diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 60645aa..f8c4286 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -879,7 +879,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, max_len = ((td->token >> 21) + 1) & 0x7ff; spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); - usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd, + usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd, (td->ctrl & TD_CTRL_IOC) != 0); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 5fb0c48..5796102 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -34,8 +34,8 @@ #else #define DPRINTF(...) do {} while (0) #endif -#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ - __func__, __LINE__); abort(); } while (0) +#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ + __func__, __LINE__, _msg); abort(); } while (0) #define MAXPORTS_2 15 #define MAXPORTS_3 15 @@ -301,6 +301,8 @@ typedef enum TRBCCode { #define SLOT_CONTEXT_ENTRIES_SHIFT 27 typedef struct XHCIState XHCIState; +typedef struct XHCIStreamContext XHCIStreamContext; +typedef struct XHCIEPContext XHCIEPContext; #define get_field(data, field) \ (((data) >> field##_SHIFT) & field##_MASK) @@ -351,6 +353,7 @@ typedef struct XHCITransfer { unsigned int iso_pkts; unsigned int slotid; unsigned int epid; + unsigned int streamid; bool in_xfer; bool iso_xfer; @@ -367,7 +370,14 @@ typedef struct XHCITransfer { uint64_t mfindex_kick; } XHCITransfer; -typedef struct XHCIEPContext { +struct XHCIStreamContext { + dma_addr_t pctx; + unsigned int sct; + XHCIRing ring; + XHCIStreamContext *sstreams; +}; + +struct XHCIEPContext { XHCIState *xhci; unsigned int slotid; unsigned int epid; @@ -382,11 +392,17 @@ typedef struct XHCIEPContext { unsigned int max_psize; uint32_t state; + /* streams */ + unsigned int max_pstreams; + bool lsa; + unsigned int nr_pstreams; + XHCIStreamContext *pstreams; + /* iso xfer scheduling */ unsigned int interval; int64_t mfindex_last; QEMUTimer *kick_timer; -} XHCIEPContext; +}; typedef struct XHCISlot { bool enabled; @@ -482,7 +498,7 @@ enum xhci_flags { }; static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid); + unsigned int epid, unsigned int streamid); static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); @@ -1068,18 +1084,116 @@ static void xhci_stop(XHCIState *xhci) xhci->crcr_low &= ~CRCR_CRR; } +static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count, + dma_addr_t base) +{ + XHCIStreamContext *stctx; + unsigned int i; + + stctx = g_new0(XHCIStreamContext, count); + for (i = 0; i < count; i++) { + stctx[i].pctx = base + i * 16; + stctx[i].sct = -1; + } + return stctx; +} + +static void xhci_reset_streams(XHCIEPContext *epctx) +{ + unsigned int i; + + for (i = 0; i < epctx->nr_pstreams; i++) { + epctx->pstreams[i].sct = -1; + g_free(epctx->pstreams[i].sstreams); + } +} + +static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) +{ + assert(epctx->pstreams == NULL); + epctx->nr_pstreams = 2 << epctx->max_pstreams; + epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); +} + +static void xhci_free_streams(XHCIEPContext *epctx) +{ + int i; + + assert(epctx->pstreams != NULL); + + if (!epctx->lsa) { + for (i = 0; i < epctx->nr_pstreams; i++) { + g_free(epctx->pstreams[i].sstreams); + } + } + g_free(epctx->pstreams); + epctx->pstreams = NULL; + epctx->nr_pstreams = 0; +} + +static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, + unsigned int streamid, + uint32_t *cc_error) +{ + XHCIStreamContext *sctx; + dma_addr_t base; + uint32_t ctx[2], sct; + + assert(streamid != 0); + if (epctx->lsa) { + if (streamid >= epctx->nr_pstreams) { + *cc_error = CC_INVALID_STREAM_ID_ERROR; + return NULL; + } + sctx = epctx->pstreams + streamid; + } else { + FIXME("secondary streams not implemented yet"); + } + + if (sctx->sct == -1) { + xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx)); + fprintf(stderr, "%s: init sctx #%d @ %lx: %08x %08x\n", __func__, + streamid, sctx->pctx, ctx[0], ctx[1]); + sct = (ctx[0] >> 1) & 0x07; + if (epctx->lsa && sct != 1) { + *cc_error = CC_INVALID_STREAM_TYPE_ERROR; + return NULL; + } + sctx->sct = sct; + base = xhci_addr64(ctx[0] & ~0xf, ctx[1]); + xhci_ring_init(epctx->xhci, &sctx->ring, base); + } + return sctx; +} + static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, - uint32_t state) + XHCIStreamContext *sctx, uint32_t state) { uint32_t ctx[5]; + uint32_t ctx2[2]; + fprintf(stderr, "%s: epid %d, state %d\n", + __func__, epctx->epid, state); xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); ctx[0] &= ~EP_STATE_MASK; ctx[0] |= state; - ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; - ctx[3] = (epctx->ring.dequeue >> 16) >> 16; - DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", - epctx->pctx, state, ctx[3], ctx[2]); + + /* update ring dequeue ptr */ + if (epctx->nr_pstreams) { + if (sctx != NULL) { + xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); + ctx2[0] &= 0xe; + ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; + ctx2[1] = (sctx->ring.dequeue >> 16) >> 16; + xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); + } + } else { + ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; + ctx[3] = (epctx->ring.dequeue >> 16) >> 16; + DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", + epctx->pctx, state, ctx[3], ctx[2]); + } + xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); epctx->state = state; } @@ -1087,7 +1201,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, static void xhci_ep_kick_timer(void *opaque) { XHCIEPContext *epctx = opaque; - xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid); + xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, @@ -1117,16 +1231,22 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot->eps[epid-1] = epctx; dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); - xhci_ring_init(xhci, &epctx->ring, dequeue); - epctx->ring.ccs = ctx[2] & 1; epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type); epctx->pctx = pctx; epctx->max_psize = ctx[1]>>16; epctx->max_psize *= 1+((ctx[1]>>8)&0xff); + epctx->max_pstreams = (ctx[0] >> 10) & 0xf; + epctx->lsa = (ctx[0] >> 15) & 1; DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", epid/2, epid%2, epctx->max_psize); + if (epctx->max_pstreams) { + xhci_alloc_streams(epctx, dequeue); + } else { + xhci_ring_init(xhci, &epctx->ring, dequeue); + epctx->ring.ccs = ctx[2] & 1; + } for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { usb_packet_init(&epctx->transfers[i].packet); } @@ -1227,7 +1347,11 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - xhci_set_ep_state(xhci, epctx, EP_DISABLED); + if (epctx->nr_pstreams) { + xhci_free_streams(epctx); + } + + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); qemu_free_timer(epctx->kick_timer); g_free(epctx); @@ -1264,7 +1388,11 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); + + if (epctx->nr_pstreams) { + xhci_reset_streams(epctx); + } return CC_SUCCESS; } @@ -1315,16 +1443,22 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, return CC_USB_TRANSACTION_ERROR; } - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); + + if (epctx->nr_pstreams) { + xhci_reset_streams(epctx); + } return CC_SUCCESS; } static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, - unsigned int epid, uint64_t pdequeue) + unsigned int epid, unsigned int streamid, + uint64_t pdequeue) { XHCISlot *slot; XHCIEPContext *epctx; + XHCIStreamContext *sctx; dma_addr_t dequeue; assert(slotid >= 1 && slotid <= xhci->numslots); @@ -1334,7 +1468,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, return CC_TRB_ERROR; } - trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue); + trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue); dequeue = xhci_mask64(pdequeue); slot = &xhci->slots[slotid-1]; @@ -1346,16 +1480,26 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - if (epctx->state != EP_STOPPED) { fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid); return CC_CONTEXT_STATE_ERROR; } - xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); - epctx->ring.ccs = dequeue & 1; + if (epctx->nr_pstreams) { + uint32_t err; + sctx = xhci_find_stream(epctx, streamid, &err); + if (sctx == NULL) { + return err; + } + xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf); + sctx->ring.ccs = dequeue & 1; + } else { + sctx = NULL; + xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); + epctx->ring.ccs = dequeue & 1; + } - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED); return CC_SUCCESS; } @@ -1484,12 +1628,22 @@ static void xhci_stall_ep(XHCITransfer *xfer) XHCIState *xhci = xfer->xhci; XHCISlot *slot = &xhci->slots[xfer->slotid-1]; XHCIEPContext *epctx = slot->eps[xfer->epid-1]; + uint32_t err; + XHCIStreamContext *sctx; - epctx->ring.dequeue = xfer->trbs[0].addr; - epctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, EP_HALTED); - DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid); - DPRINTF("xhci: will continue at "DMA_ADDR_FMT"\n", epctx->ring.dequeue); + if (epctx->nr_pstreams) { + sctx = xhci_find_stream(epctx, xfer->streamid, &err); + if (sctx == NULL) { + return; + } + sctx->ring.dequeue = xfer->trbs[0].addr; + sctx->ring.ccs = xfer->trbs[0].ccs; + xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED); + } else { + epctx->ring.dequeue = xfer->trbs[0].addr; + epctx->ring.ccs = xfer->trbs[0].ccs; + xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED); + } } static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, @@ -1518,8 +1672,8 @@ static int xhci_setup_packet(XHCITransfer *xfer) } xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ - usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false, - xfer->int_req); + usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, + xfer->trbs[0].addr, false, xfer->int_req); usb_packet_map(&xfer->packet, &xfer->sgl); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", xfer->packet.pid, dev->addr, ep->nr); @@ -1572,7 +1726,7 @@ static int xhci_complete_packet(XHCITransfer *xfer) default: fprintf(stderr, "%s: FIXME: status = %d\n", __func__, xfer->packet.status); - FIXME(); + FIXME("unhandled USB_RET_*"); } return 0; } @@ -1585,7 +1739,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) trb_setup = &xfer->trbs[0]; trb_status = &xfer->trbs[xfer->trb_count-1]; - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); /* at most one Event Data TRB allowed after STATUS */ if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { @@ -1627,7 +1781,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xhci_complete_packet(xfer); if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0); } return 0; } @@ -1710,26 +1864,29 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx xhci_complete_packet(xfer); if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid); } return 0; } static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) { - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); return xhci_submit(xhci, xfer, epctx); } -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) +static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid, unsigned int streamid) { + XHCIStreamContext *stctx; XHCIEPContext *epctx; + XHCIRing *ring; USBEndpoint *ep = NULL; uint64_t mfindex; int length; int i; - trace_usb_xhci_ep_kick(slotid, epid); + trace_usb_xhci_ep_kick(slotid, epid, streamid); assert(slotid >= 1 && slotid <= xhci->numslots); assert(epid >= 1 && epid <= 31); @@ -1782,14 +1939,28 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid return; } - xhci_set_ep_state(xhci, epctx, EP_RUNNING); + + if (epctx->nr_pstreams) { + uint32_t err; + stctx = xhci_find_stream(epctx, streamid, &err); + if (stctx == NULL) { + return; + } + ring = &stctx->ring; + xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING); + } else { + ring = &epctx->ring; + streamid = 0; + xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING); + } + assert(ring->base != 0); while (1) { XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; if (xfer->running_async || xfer->running_retry) { break; } - length = xhci_ring_chain_length(xhci, &epctx->ring); + length = xhci_ring_chain_length(xhci, ring); if (length < 0) { break; } else if (length == 0) { @@ -1808,11 +1979,12 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid xfer->trb_count = length; for (i = 0; i < length; i++) { - assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL)); + assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); } xfer->xhci = xhci; xfer->epid = epid; xfer->slotid = slotid; + xfer->streamid = streamid; if (epid == 1) { if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { @@ -1977,7 +2149,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, DPRINTF("xhci: device address is %d\n", slot->devaddr); usb_device_reset(dev); usb_packet_setup(&p, USB_TOKEN_OUT, - usb_ep_get(dev, USB_TOKEN_OUT, 0), + usb_ep_get(dev, USB_TOKEN_OUT, 0), 0, 0, false, false); usb_device_handle_control(dev, &p, DeviceOutRequest | USB_REQ_SET_ADDRESS, @@ -2357,11 +2529,14 @@ static void xhci_process_commands(XHCIState *xhci) } break; case CR_SET_TR_DEQUEUE: + fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__); slotid = xhci_get_slot(xhci, &event, &trb); if (slotid) { unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) & TRB_CR_EPID_MASK; - event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid, + unsigned int streamid = (trb.status >> 16) & 0xffff; + event.ccode = xhci_set_ep_dequeue(xhci, slotid, + epid, streamid, trb.parameter); } break; @@ -2554,9 +2729,9 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) break; case 0x10: /* HCCPARAMS */ if (sizeof(dma_addr_t) == 4) { - ret = 0x00081000; + ret = 0x00087000; } else { - ret = 0x00081001; + ret = 0x00087001; } break; case 0x14: /* DBOFF */ @@ -2880,6 +3055,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, uint64_t val, unsigned size) { XHCIState *xhci = ptr; + unsigned int epid, streamid; trace_usb_xhci_doorbell_write(reg, val); @@ -2898,13 +3074,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, (uint32_t)val); } } else { + epid = val & 0xff; + streamid = (val >> 16) & 0xffff; if (reg > xhci->numslots) { fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg); - } else if (val > 31) { + } else if (epid > 31) { fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", (int)reg, (uint32_t)val); } else { - xhci_kick_ep(xhci, reg, val); + xhci_kick_ep(xhci, reg, epid, streamid); } } } @@ -2988,7 +3166,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet) return; } xhci_complete_packet(xfer); - xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); } static void xhci_child_detach(USBPort *uport, USBDevice *child) @@ -3033,7 +3211,8 @@ static int xhci_find_epid(USBEndpoint *ep) } } -static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, + unsigned int stream) { XHCIState *xhci = container_of(bus, XHCIState, bus); int slotid; @@ -3044,7 +3223,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); return; } - xhci_kick_ep(xhci, slotid, xhci_find_epid(ep)); + xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream); } static USBBusOps xhci_bus_ops = { diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c index 07f0e01..39f2281 100644 --- a/hw/usb/host-bsd.c +++ b/hw/usb/host-bsd.c @@ -637,9 +637,3 @@ void usb_host_info(Monitor *mon, const QDict *qdict) { usb_host_scan(mon, usb_host_info_device); } - -/* XXX add this */ -int usb_host_device_close(const char *devname) -{ - return 0; -} diff --git a/hw/usb/host-legacy.c b/hw/usb/host-legacy.c new file mode 100644 index 0000000..3a5f705 --- /dev/null +++ b/hw/usb/host-legacy.c @@ -0,0 +1,144 @@ +/* + * Linux host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ <linux@tjworld.net> + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "hw/usb.h" +#include "hw/usb/host.h" + +/* + * Autoconnect filter + * Format: + * auto:bus:dev[:vid:pid] + * auto:bus.dev[:vid:pid] + * + * bus - bus number (dec, * means any) + * dev - device number (dec, * means any) + * vid - vendor id (hex, * means any) + * pid - product id (hex, * means any) + * + * See 'lsusb' output. + */ +static int parse_filter(const char *spec, struct USBAutoFilter *f) +{ + enum { BUS, DEV, VID, PID, DONE }; + const char *p = spec; + int i; + + f->bus_num = 0; + f->addr = 0; + f->vendor_id = 0; + f->product_id = 0; + + for (i = BUS; i < DONE; i++) { + p = strpbrk(p, ":."); + if (!p) { + break; + } + p++; + + if (*p == '*') { + continue; + } + switch (i) { + case BUS: + f->bus_num = strtol(p, NULL, 10); + break; + case DEV: + f->addr = strtol(p, NULL, 10); + break; + case VID: + f->vendor_id = strtol(p, NULL, 16); + break; + case PID: + f->product_id = strtol(p, NULL, 16); + break; + } + } + + if (i < DEV) { + fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); + return -1; + } + + return 0; +} + +USBDevice *usb_host_device_open(USBBus *bus, const char *devname) +{ + struct USBAutoFilter filter; + USBDevice *dev; + char *p; + + dev = usb_create(bus, "usb-host"); + + if (strstr(devname, "auto:")) { + if (parse_filter(devname, &filter) < 0) { + goto fail; + } + } else { + p = strchr(devname, '.'); + if (p) { + filter.bus_num = strtoul(devname, NULL, 0); + filter.addr = strtoul(p + 1, NULL, 0); + filter.vendor_id = 0; + filter.product_id = 0; + } else { + p = strchr(devname, ':'); + if (p) { + filter.bus_num = 0; + filter.addr = 0; + filter.vendor_id = strtoul(devname, NULL, 16); + filter.product_id = strtoul(p + 1, NULL, 16); + } else { + goto fail; + } + } + } + + qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); + qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); + qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); + qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); + qdev_init_nofail(&dev->qdev); + return dev; + +fail: + qdev_free(&dev->qdev); + return NULL; +} + +static void usb_host_register_types(void) +{ + usb_legacy_register("usb-host", "host", usb_host_device_open); +} + +type_init(usb_host_register_types) diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index a2cff8a..b67aeba 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -43,6 +43,7 @@ #include <linux/version.h> #include "hw/usb.h" #include "hw/usb/desc.h" +#include "hw/usb/host.h" /* We redefine it to avoid version problems */ struct usb_ctrltransfer { @@ -87,14 +88,6 @@ struct endp_data { int inflight; }; -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - enum USBHostDeviceOptions { USB_HOST_OPT_PIPELINE, }; @@ -131,7 +124,6 @@ typedef struct USBHostDevice { static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs); static int usb_host_close(USBHostDevice *dev); -static int parse_filter(const char *spec, struct USBAutoFilter *f); static void usb_host_auto_check(void *unused); static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); @@ -1544,75 +1536,10 @@ static const TypeInfo usb_host_dev_info = { static void usb_host_register_types(void) { type_register_static(&usb_host_dev_info); - usb_legacy_register("usb-host", "host", usb_host_device_open); } type_init(usb_host_register_types) -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - struct USBAutoFilter filter; - USBDevice *dev; - char *p; - - dev = usb_create(bus, "usb-host"); - - if (strstr(devname, "auto:")) { - if (parse_filter(devname, &filter) < 0) { - goto fail; - } - } else { - if ((p = strchr(devname, '.'))) { - filter.bus_num = strtoul(devname, NULL, 0); - filter.addr = strtoul(p + 1, NULL, 0); - filter.vendor_id = 0; - filter.product_id = 0; - } else if ((p = strchr(devname, ':'))) { - filter.bus_num = 0; - filter.addr = 0; - filter.vendor_id = strtoul(devname, NULL, 16); - filter.product_id = strtoul(p + 1, NULL, 16); - } else { - goto fail; - } - } - - qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); - qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); - qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); - qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); - qdev_init_nofail(&dev->qdev); - return dev; - -fail: - qdev_free(&dev->qdev); - return NULL; -} - -int usb_host_device_close(const char *devname) -{ -#if 0 - char product_name[PRODUCT_NAME_SZ]; - int bus_num, addr; - USBHostDevice *s; - - if (strstr(devname, "auto:")) { - return usb_host_auto_del(devname); - } - if (usb_host_find_device(&bus_num, &addr, product_name, - sizeof(product_name), devname) < 0) { - return -1; - } - s = hostdev_find(bus_num, addr); - if (s) { - usb_device_delete_addr(s->bus_num, s->dev.addr); - return 0; - } -#endif - - return -1; -} - /* * Read sys file-system device file * @@ -1840,56 +1767,6 @@ static void usb_host_auto_check(void *unused) qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } -/* - * Autoconnect filter - * Format: - * auto:bus:dev[:vid:pid] - * auto:bus.dev[:vid:pid] - * - * bus - bus number (dec, * means any) - * dev - device number (dec, * means any) - * vid - vendor id (hex, * means any) - * pid - product id (hex, * means any) - * - * See 'lsusb' output. - */ -static int parse_filter(const char *spec, struct USBAutoFilter *f) -{ - enum { BUS, DEV, VID, PID, DONE }; - const char *p = spec; - int i; - - f->bus_num = 0; - f->addr = 0; - f->vendor_id = 0; - f->product_id = 0; - - for (i = BUS; i < DONE; i++) { - p = strpbrk(p, ":."); - if (!p) { - break; - } - p++; - - if (*p == '*') { - continue; - } - switch(i) { - case BUS: f->bus_num = strtol(p, NULL, 10); break; - case DEV: f->addr = strtol(p, NULL, 10); break; - case VID: f->vendor_id = strtol(p, NULL, 16); break; - case PID: f->product_id = strtol(p, NULL, 16); break; - } - } - - if (i < DEV) { - fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); - return -1; - } - - return 0; -} - /**********************/ /* USB host device info */ diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 8affba7..28d8032 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -45,8 +45,3 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) { return NULL; } - -int usb_host_device_close(const char *devname) -{ - return 0; -} diff --git a/hw/usb/host.h b/hw/usb/host.h new file mode 100644 index 0000000..048ff3b --- /dev/null +++ b/hw/usb/host.h @@ -0,0 +1,44 @@ +/* + * Linux host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ <linux@tjworld.net> + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_USB_HOST_H +#define QEMU_USB_HOST_H + +struct USBAutoFilter { + uint32_t bus_num; + uint32_t addr; + char *port; + uint32_t vendor_id; + uint32_t product_id; +}; + +#endif /* QEMU_USB_HOST_H */ diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 8c0ead0..7078403 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -737,7 +737,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, uint8_t ep) { struct usb_redir_bulk_packet_header bulk_packet; - size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; + size_t size = usb_packet_size(p); const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; if (usbredir_already_in_flight(dev, p->id)) { @@ -771,12 +771,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, &bulk_packet, NULL, 0); } else { uint8_t buf[size]; - if (p->combined) { - iov_to_buf(p->combined->iov.iov, p->combined->iov.niov, - 0, buf, size); - } else { - usb_packet_copy(p, buf, size); - } + usb_packet_copy(p, buf, size); usbredir_log_data(dev, "bulk data out:", buf, size); usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, buf, size); @@ -1830,7 +1825,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p = usbredir_find_packet_by_id(dev, ep, id); if (p) { - size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; + size_t size = usb_packet_size(p); usbredir_handle_status(dev, p, bulk_packet->status); if (data_len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); @@ -1840,12 +1835,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p->status = USB_RET_BABBLE; data_len = len = size; } - if (p->combined) { - iov_from_buf(p->combined->iov.iov, p->combined->iov.niov, - 0, data, data_len); - } else { - usb_packet_copy(p, data, data_len); - } + usb_packet_copy(p, data, data_len); } p->actual_length = len; if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { |