From bd21677868b80d655792c711cf1f21dcf8c24052 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Sat, 25 Apr 2009 21:10:06 +0000 Subject: [PATCH] Track the usb device state as its powered on, addressed and configured. This helps to avoid touching the device when it is not going to respond and would otherwise timeout. Implement the suspend tracking as a udev state too. --- sys/dev/usb/controller/ehci.c | 2 +- sys/dev/usb/controller/ohci.c | 4 ++-- sys/dev/usb/controller/uhci.c | 6 ++--- sys/dev/usb/usb_core.h | 1 + sys/dev/usb/usb_device.c | 41 +++++++++++++++++++++++++++++---- sys/dev/usb/usb_device.h | 5 ++-- sys/dev/usb/usb_generic.c | 2 +- sys/dev/usb/usb_hub.c | 20 ++++++++-------- sys/dev/usb/usb_request.c | 6 +++++ sys/dev/usb/usb_revision.h | 12 ++++++++++ sys/dev/usb/usb_transfer.c | 43 +++++++++++++++++++++++------------ 11 files changed, 103 insertions(+), 39 deletions(-) diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index a0b23503682e..b3d5f70bf10f 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -1964,7 +1964,7 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) usb2_pc_cpu_flush(qh->page_cache); - if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->state != USB_STATE_SUSPENDED) { EHCI_APPEND_QH(qh, *qh_last); } } diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c index eb98ff3c0220..ea59c54cf0b0 100644 --- a/sys/dev/usb/controller/ohci.c +++ b/sys/dev/usb/controller/ohci.c @@ -1020,7 +1020,7 @@ ohci_check_transfer_sub(struct usb2_xfer *xfer) * writing the BLF and CLF bits: */ - if (xfer->xroot->udev->pwr_save.suspended) { + if (xfer->xroot->udev->state == USB_STATE_SUSPENDED) { /* nothing to do */ } else if (xfer->pipe->methods == &ohci_device_bulk_methods) { ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus); @@ -1589,7 +1589,7 @@ ohci_setup_standard_chain(struct usb2_xfer *xfer, ohci_ed_t **ed_last) ed->ed_headp = td->td_self; - if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->state != USB_STATE_SUSPENDED) { /* the append function will flush the endpoint descriptor */ OHCI_APPEND_QH(ed, *ed_last); diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c index ebbcb2559509..eaadf9ea82bf 100644 --- a/sys/dev/usb/controller/uhci.c +++ b/sys/dev/usb/controller/uhci.c @@ -1921,7 +1921,7 @@ uhci_device_bulk_start(struct usb2_xfer *xfer) qh->e_next = td; qh->qh_e_next = td->td_self; - if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->state != USB_STATE_SUSPENDED) { UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); uhci_add_loop(sc); xfer->flags_int.bandwidth_reclaimed = 1; @@ -1982,7 +1982,7 @@ uhci_device_ctrl_start(struct usb2_xfer *xfer) * NOTE: some devices choke on bandwidth- reclamation for control * transfers */ - if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->state != USB_STATE_SUSPENDED) { if (xfer->xroot->udev->speed == USB_SPEED_LOW) { UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); } else { @@ -2071,7 +2071,7 @@ uhci_device_intr_start(struct usb2_xfer *xfer) qh->e_next = td; qh->qh_e_next = td->td_self; - if (xfer->xroot->udev->pwr_save.suspended == 0) { + if (xfer->xroot->udev->state != USB_STATE_SUSPENDED) { /* enter QHs into the controller data structures */ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h index e44a7d6aed05..69344524397c 100644 --- a/sys/dev/usb/usb_core.h +++ b/sys/dev/usb/usb_core.h @@ -552,5 +552,6 @@ void usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index, uint8_t usb2_get_bus_index(struct usb2_device *udev); uint8_t usb2_get_device_index(struct usb2_device *udev); void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode); +int usb2_device_attached(struct usb2_device *udev); #endif /* _USB2_CORE_H_ */ diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index 56dc7ae2cb89..2cd55e259c74 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -457,6 +457,8 @@ usb2_set_config_index(struct usb2_device *udev, uint8_t index) * the current config number and index. */ err = usb2_req_set_config(udev, NULL, USB_UNCONFIG_NO); + if (udev->state == USB_STATE_CONFIGURED) + usb2_set_device_state(udev, USB_STATE_ADDRESSED); goto done; } /* get the full config descriptor */ @@ -524,6 +526,7 @@ usb2_set_config_index(struct usb2_device *udev, uint8_t index) udev->power = power; udev->curr_config_no = cdp->bConfigurationValue; udev->curr_config_index = index; + usb2_set_device_state(udev, USB_STATE_CONFIGURED); /* Set the actual configuration value. */ err = usb2_req_set_config(udev, NULL, cdp->bConfigurationValue); @@ -980,7 +983,7 @@ usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev, udev->port_no, udev->address); if (device_is_attached(dev)) { - if (udev->flags.suspended) { + if (udev->state == USB_STATE_SUSPENDED) { err = DEVICE_RESUME(dev); if (err) { device_printf(dev, "Resume failed!\n"); @@ -1120,7 +1123,7 @@ usb2_probe_and_attach_sub(struct usb2_device *udev, uaa->temp_dev = NULL; device_set_ivars(iface->subdev, NULL); - if (udev->flags.suspended) { + if (udev->state == USB_STATE_SUSPENDED) { err = DEVICE_SUSPEND(iface->subdev); if (err) device_printf(iface->subdev, "Suspend failed\n"); @@ -1341,12 +1344,12 @@ usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend) USB_BUS_LOCK(udev->bus); /* filter the suspend events */ - if (udev->flags.suspended == do_suspend) { + if ((udev->state == USB_STATE_SUSPENDED && do_suspend) || + (udev->state != USB_STATE_SUSPENDED && !do_suspend)) { USB_BUS_UNLOCK(udev->bus); /* nothing to do */ return (0); } - udev->flags.suspended = do_suspend; USB_BUS_UNLOCK(udev->bus); /* do the suspend or resume */ @@ -1471,6 +1474,7 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb2_ticks_t)ticks; + usb2_set_device_state(udev, USB_STATE_POWERED); /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with @@ -1572,6 +1576,7 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, goto done; } } + usb2_set_device_state(udev, USB_STATE_ADDRESSED); /* * Get the first 8 bytes of the device descriptor ! @@ -1927,7 +1932,8 @@ usb2_free_device(struct usb2_device *udev, uint8_t flag) DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); - bus = udev->bus;; + bus = udev->bus; + usb2_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_UGEN usb2_notify_addq("-", udev); @@ -2438,3 +2444,28 @@ usb2_peer_can_wakeup(struct usb2_device *udev) } return (0); /* not supported */ } + +void +usb2_set_device_state(struct usb2_device *udev, enum usb_dev_state state) +{ + static const char* statestr[USB_STATE_MAX] = { + [USB_STATE_DETACHED] = "DETACHED", + [USB_STATE_ATTACHED] = "ATTACHED", + [USB_STATE_POWERED] = "POWERED", + [USB_STATE_ADDRESSED] = "ADDRESSED", + [USB_STATE_CONFIGURED] = "CONFIGURED", + [USB_STATE_SUSPENDED] = "SUSPENDED" + }; + + KASSERT(state < USB_STATE_MAX, ("invalid udev state")); + + DPRINTF("udev %p state %s -> %s\n", udev, + statestr[udev->state], statestr[state]); + udev->state = state; +} + +int +usb2_device_attached(struct usb2_device *udev) +{ + return (udev->state > USB_STATE_DETACHED); +} diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h index 437bf8a72423..e16127a7cebf 100644 --- a/sys/dev/usb/usb_device.h +++ b/sys/dev/usb/usb_device.h @@ -85,7 +85,6 @@ struct usb2_interface { struct usb2_device_flags { uint8_t usb2_mode:1; /* USB mode (see USB_MODE_XXX) */ uint8_t self_powered:1; /* set if USB device is self powered */ - uint8_t suspended:1; /* set if USB device is suspended */ uint8_t no_strings:1; /* set if USB device does not support * strings */ uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */ @@ -101,7 +100,6 @@ struct usb2_power_save { usb2_size_t type_refs[4]; /* transfer reference count */ usb2_size_t read_refs; /* data read references */ usb2_size_t write_refs; /* data write references */ - uint8_t suspended; /* set if USB device is suspended */ }; /* @@ -139,6 +137,7 @@ struct usb2_device { #endif usb2_ticks_t plugtime; /* copy of "ticks" */ + enum usb_dev_state state; uint16_t refcount; #define USB_DEV_REF_MAX 0xffff @@ -205,5 +204,7 @@ void *usb2_find_descriptor(struct usb2_device *udev, void *id, void usb_linux_free_device(struct usb_device *dev); uint8_t usb2_peer_can_wakeup(struct usb2_device *udev); struct usb2_pipe *usb2_pipe_foreach(struct usb2_device *udev, struct usb2_pipe *pipe); +void usb2_set_device_state(struct usb2_device *udev, + enum usb_dev_state state); #endif /* _USB2_DEVICE_H_ */ diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c index 27de49b3b639..c3fe72d4eb70 100644 --- a/sys/dev/usb/usb_generic.c +++ b/sys/dev/usb/usb_generic.c @@ -823,7 +823,7 @@ usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di) di->udi_speed = udev->speed; di->udi_mode = udev->flags.usb2_mode; di->udi_power_mode = udev->power_mode; - if (udev->flags.suspended) { + if (udev->state == USB_STATE_SUSPENDED) { di->udi_suspended = 1; } else { di->udi_suspended = 0; diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c index 969251493c8b..5c435d088892 100644 --- a/sys/dev/usb/usb_hub.c +++ b/sys/dev/usb/usb_hub.c @@ -544,7 +544,7 @@ uhub_explore(struct usb2_device *udev) if (udev->depth > USB_HUB_MAX_DEPTH) { return (USB_ERR_TOO_DEEP); } - if (udev->pwr_save.suspended) { + if (udev->state == USB_STATE_SUSPENDED) { /* need to wait until the child signals resume */ DPRINTF("Device is suspended!\n"); return (0); @@ -1518,7 +1518,7 @@ usb2_transfer_power_ref(struct usb2_xfer *xfer, int val) udev->pwr_save.write_refs += val; } - if (udev->pwr_save.suspended) + if (udev->state == USB_STATE_SUSPENDED) needs_explore = (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && @@ -1600,7 +1600,7 @@ usb2_bus_powerd(struct usb2_bus *bus) (rem_wakeup == 0))) { /* check if we are suspended */ - if (udev->pwr_save.suspended != 0) { + if (udev->state == USB_STATE_SUSPENDED) { USB_BUS_UNLOCK(bus); usb2_dev_resume_peer(udev); USB_BUS_LOCK(bus); @@ -1608,7 +1608,7 @@ usb2_bus_powerd(struct usb2_bus *bus) } else if (temp >= limit) { /* check if we are not suspended */ - if (udev->pwr_save.suspended == 0) { + if (udev->state != USB_STATE_SUSPENDED) { USB_BUS_UNLOCK(bus); usb2_dev_suspend_peer(udev); USB_BUS_LOCK(bus); @@ -1647,7 +1647,7 @@ usb2_bus_powerd(struct usb2_bus *bus) if (temp < mintime) mintime = temp; - if (udev->pwr_save.suspended == 0) { + if (udev->state != USB_STATE_SUSPENDED) { type_refs[0] += udev->pwr_save.type_refs[0]; type_refs[1] += udev->pwr_save.type_refs[1]; type_refs[2] += udev->pwr_save.type_refs[2]; @@ -1697,7 +1697,7 @@ usb2_dev_resume_peer(struct usb2_device *udev) return; /* check if already resumed */ - if (udev->pwr_save.suspended == 0) + if (udev->state != USB_STATE_SUSPENDED) return; /* we need a parent HUB to do resume */ @@ -1737,7 +1737,7 @@ usb2_dev_resume_peer(struct usb2_device *udev) } USB_BUS_LOCK(bus); /* set that this device is now resumed */ - udev->pwr_save.suspended = 0; + usb2_set_device_state(udev, USB_STATE_CONFIGURED); #if USB_HAVE_POWERD /* make sure that we don't go into suspend right away */ udev->pwr_save.last_xfer_time = ticks; @@ -1797,7 +1797,7 @@ repeat: return; /* check if already suspended */ - if (udev->pwr_save.suspended) + if (udev->state == USB_STATE_SUSPENDED) return; /* we need a parent HUB to do suspend */ @@ -1819,7 +1819,7 @@ repeat: if (child == NULL) continue; - if (child->pwr_save.suspended) + if (child->state == USB_STATE_SUSPENDED) continue; DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1); @@ -1846,7 +1846,7 @@ repeat: * Set that this device is suspended. This variable must be set * before calling USB controller suspend callbacks. */ - udev->pwr_save.suspended = 1; + usb2_set_device_state(udev, USB_STATE_SUSPENDED); USB_BUS_UNLOCK(udev->bus); if (udev->bus->methods->device_suspend != NULL) { diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c index d643b37825f7..8b18bbc5cb5c 100644 --- a/sys/dev/usb/usb_request.c +++ b/sys/dev/usb/usb_request.c @@ -271,6 +271,12 @@ usb2_do_request_flags(struct usb2_device *udev, struct mtx *mtx, req->wIndex[1], req->wIndex[0], req->wLength[1], req->wLength[0]); + /* Check if the device is still alive */ + if (udev->state < USB_STATE_POWERED) { + DPRINTF("usb device has gone\n"); + return (USB_ERR_NOT_CONFIGURED); + } + /* * Set "actlen" to a known value in case the caller does not * check the return value: diff --git a/sys/dev/usb/usb_revision.h b/sys/dev/usb/usb_revision.h index 06d1b21e5b21..6460858c363d 100644 --- a/sys/dev/usb/usb_revision.h +++ b/sys/dev/usb/usb_revision.h @@ -62,4 +62,16 @@ enum { USB_MODE_MAX }; +/* + * The "USB_MODE" macro defines all the supported device states. + */ +enum usb_dev_state { + USB_STATE_DETACHED, + USB_STATE_ATTACHED, + USB_STATE_POWERED, + USB_STATE_ADDRESSED, + USB_STATE_CONFIGURED, + USB_STATE_SUSPENDED, + USB_STATE_MAX, +}; #endif /* _USB2_REVISION_H_ */ diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index bc4417750cec..d4efaff06888 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -1365,24 +1365,37 @@ error: void usb2_start_hardware(struct usb2_xfer *xfer) { + struct usb2_xfer_root *info; + struct usb2_bus *bus; usb2_frcount_t x; + info = xfer->xroot; + bus = info->bus; + DPRINTF("xfer=%p, pipe=%p, nframes=%d, dir=%s\n", xfer, xfer->pipe, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); + /* Check if the device is still alive */ + if (info->udev->state < USB_STATE_POWERED) { + USB_BUS_LOCK(bus); + usb2_transfer_done(xfer, USB_ERR_NOT_CONFIGURED); + USB_BUS_UNLOCK(bus); + return; + } + #if USB_DEBUG if (USB_DEBUG_VAR > 0) { - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); usb2_dump_pipe(xfer->pipe); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); - USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_NOTOWNED); + USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { @@ -1390,9 +1403,9 @@ usb2_start_hardware(struct usb2_xfer *xfer) DPRINTF("open\n"); - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); (xfer->pipe->methods->open) (xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; @@ -1406,9 +1419,9 @@ usb2_start_hardware(struct usb2_xfer *xfer) * frequently the "done_q": */ if (xfer->wait_queue) { - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); usb2_transfer_dequeue(xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; @@ -1441,16 +1454,16 @@ usb2_start_hardware(struct usb2_xfer *xfer) */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); return; } - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_INVAL); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); return; } /* compute total transfer length */ @@ -1459,9 +1472,9 @@ usb2_start_hardware(struct usb2_xfer *xfer) xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_INVAL); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); return; } } @@ -1476,9 +1489,9 @@ usb2_start_hardware(struct usb2_xfer *xfer) if (xfer->flags_int.control_xfr) { if (usb2_start_hardware_sub(xfer)) { - USB_BUS_LOCK(xfer->xroot->bus); + USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_STALLED); - USB_BUS_UNLOCK(xfer->xroot->bus); + USB_BUS_UNLOCK(bus); return; } }