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:
parent
d73b356abc
commit
bd21677868
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]);
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user