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.
This commit is contained in:
Andrew Thompson 2009-04-25 21:10:06 +00:00
parent d73b356abc
commit bd21677868
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=191494
11 changed files with 103 additions and 39 deletions

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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]);

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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;

View file

@ -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 @@ usb2_dev_suspend_peer(struct usb2_device *udev)
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 @@ usb2_dev_suspend_peer(struct usb2_device *udev)
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 @@ usb2_dev_suspend_peer(struct usb2_device *udev)
* 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) {

View file

@ -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:

View file

@ -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_ */

View file

@ -1365,24 +1365,37 @@ usb2_start_hardware_sub(struct usb2_xfer *xfer)
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;
}
}