aboutsummaryrefslogtreecommitdiff
path: root/hw/usb/hcd-xhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb/hcd-xhci.c')
-rw-r--r--hw/usb/hcd-xhci.c79
1 files changed, 49 insertions, 30 deletions
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index efd4b0d..a26b78e 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -408,7 +408,6 @@ typedef struct XHCISlot {
bool enabled;
dma_addr_t ctx;
USBPort *uport;
- unsigned int devaddr;
XHCIEPContext * eps[31];
} XHCISlot;
@@ -452,7 +451,6 @@ struct XHCIState {
MemoryRegion mem_oper;
MemoryRegion mem_runtime;
MemoryRegion mem_doorbell;
- unsigned int devaddr;
/* properties */
uint32_t numports_2;
@@ -2141,16 +2139,18 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
} else {
USBPacket p;
- slot->devaddr = xhci->devaddr++;
- slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
- DPRINTF("xhci: device address is %d\n", slot->devaddr);
+ uint8_t buf[1];
+
+ slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
usb_device_reset(dev);
+ memset(&p, 0, sizeof(p));
+ usb_packet_addbuf(&p, buf, sizeof(buf));
usb_packet_setup(&p, USB_TOKEN_OUT,
usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
0, false, false);
usb_device_handle_control(dev, &p,
DeviceOutRequest | USB_REQ_SET_ADDRESS,
- slot->devaddr, 0, 0, NULL);
+ slotid, 0, 0, NULL);
assert(p.status != USB_RET_ASYNC);
}
@@ -2526,7 +2526,6 @@ 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)
@@ -2593,6 +2592,7 @@ static void xhci_port_notify(XHCIPort *port, uint32_t bits)
if ((port->portsc & bits) == bits) {
return;
}
+ trace_usb_xhci_port_notify(port->portnr, bits);
port->portsc |= bits;
if (!xhci_running(port->xhci)) {
return;
@@ -2674,7 +2674,6 @@ static void xhci_reset(DeviceState *dev)
xhci->dcbaap_low = 0;
xhci->dcbaap_high = 0;
xhci->config = 0;
- xhci->devaddr = 2;
for (i = 0; i < xhci->numslots; i++) {
xhci_disable_slot(xhci, i+1);
@@ -2799,29 +2798,56 @@ static void xhci_port_write(void *ptr, hwaddr reg,
uint64_t val, unsigned size)
{
XHCIPort *port = ptr;
- uint32_t portsc;
+ uint32_t portsc, notify;
trace_usb_xhci_port_write(port->portnr, reg, val);
switch (reg) {
case 0x00: /* PORTSC */
+ /* write-1-to-start bits */
+ if (val & PORTSC_PR) {
+ xhci_port_reset(port);
+ break;
+ }
+
portsc = port->portsc;
+ notify = 0;
/* write-1-to-clear bits*/
portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
if (val & PORTSC_LWS) {
/* overwrite PLS only when LWS=1 */
- uint32_t pls = get_field(val, PORTSC_PLS);
- set_field(&portsc, pls, PORTSC_PLS);
- trace_usb_xhci_port_link(port->portnr, pls);
+ uint32_t old_pls = get_field(port->portsc, PORTSC_PLS);
+ uint32_t new_pls = get_field(val, PORTSC_PLS);
+ switch (new_pls) {
+ case PLS_U0:
+ if (old_pls != PLS_U0) {
+ set_field(&portsc, new_pls, PORTSC_PLS);
+ trace_usb_xhci_port_link(port->portnr, new_pls);
+ notify = PORTSC_PLC;
+ }
+ break;
+ case PLS_U3:
+ if (old_pls < PLS_U3) {
+ set_field(&portsc, new_pls, PORTSC_PLS);
+ trace_usb_xhci_port_link(port->portnr, new_pls);
+ }
+ break;
+ case PLS_RESUME:
+ /* windows does this for some reason, don't spam stderr */
+ break;
+ default:
+ fprintf(stderr, "%s: ignore pls write (old %d, new %d)\n",
+ __func__, old_pls, new_pls);
+ break;
+ }
}
/* read/write bits */
portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
port->portsc = portsc;
- /* write-1-to-start bits */
- if (val & PORTSC_PR) {
- xhci_port_reset(port);
+ if (notify) {
+ xhci_port_notify(port, notify);
}
break;
case 0x04: /* PORTPMSC */
@@ -3080,8 +3106,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
}
}
+static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ /* nothing */
+}
+
static const MemoryRegionOps xhci_cap_ops = {
.read = xhci_cap_read,
+ .write = xhci_cap_write,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.impl.min_access_size = 4,
@@ -3178,20 +3211,6 @@ static USBPortOps xhci_uport_ops = {
.child_detach = xhci_child_detach,
};
-static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
-{
- XHCISlot *slot;
- int slotid;
-
- for (slotid = 1; slotid <= xhci->numslots; slotid++) {
- slot = &xhci->slots[slotid-1];
- if (slot->devaddr == dev->addr) {
- return slotid;
- }
- }
- return 0;
-}
-
static int xhci_find_epid(USBEndpoint *ep)
{
if (ep->nr == 0) {
@@ -3211,7 +3230,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
int slotid;
DPRINTF("%s\n", __func__);
- slotid = xhci_find_slotid(xhci, ep->dev);
+ slotid = ep->dev->addr;
if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
return;