Serialise USB re-enumeration with the USB explore thread.
This patch can solve problems when multiple USB devices are re-enumerated at the same time on the same bus. Approved by: thompsa (mentor)
This commit is contained in:
parent
c4bf79bca3
commit
97650b7539
@ -964,7 +964,6 @@ usb_dev_uninit(void *arg)
|
||||
if (usb_dev != NULL) {
|
||||
destroy_dev(usb_dev);
|
||||
usb_dev = NULL;
|
||||
|
||||
}
|
||||
mtx_destroy(&usb_ref_lock);
|
||||
sx_destroy(&usb_sym_lock);
|
||||
@ -1058,21 +1057,45 @@ usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread*
|
||||
err = usb_ioctl_f_sub(f, cmd, addr, td);
|
||||
}
|
||||
KASSERT(f != NULL, ("fifo not found"));
|
||||
if (err == ENOIOCTL) {
|
||||
err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
|
||||
DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
|
||||
if (err == ENOIOCTL) {
|
||||
if (usb_usb_ref_device(cpd, &refs)) {
|
||||
err = ENXIO;
|
||||
goto done;
|
||||
}
|
||||
err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
|
||||
DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
|
||||
if (err != ENOIOCTL)
|
||||
goto done;
|
||||
|
||||
err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
|
||||
|
||||
DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
|
||||
|
||||
if (err != ENOIOCTL)
|
||||
goto done;
|
||||
|
||||
if (usb_usb_ref_device(cpd, &refs)) {
|
||||
err = ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
|
||||
|
||||
DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
|
||||
|
||||
if (err == ENOIOCTL)
|
||||
err = ENOTTY;
|
||||
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* Wait for re-enumeration, if any */
|
||||
|
||||
while (f->udev->re_enumerate_wait != 0) {
|
||||
|
||||
usb_unref_device(cpd, &refs);
|
||||
|
||||
usb_pause_mtx(NULL, hz / 128);
|
||||
|
||||
if (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
|
||||
err = ENXIO;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (err == ENOIOCTL) {
|
||||
err = ENOTTY;
|
||||
}
|
||||
|
||||
done:
|
||||
usb_unref_device(cpd, &refs);
|
||||
return (err);
|
||||
|
@ -160,6 +160,7 @@ struct usb_device {
|
||||
uint8_t hs_port_no; /* high-speed HUB port number */
|
||||
uint8_t driver_added_refcount; /* our driver added generation count */
|
||||
uint8_t power_mode; /* see USB_POWER_XXX */
|
||||
uint8_t re_enumerate_wait; /* set if re-enum. is in progress */
|
||||
uint8_t ifaces_max; /* number of interfaces present */
|
||||
uint8_t endpoints_max; /* number of endpoints present */
|
||||
|
||||
|
@ -109,7 +109,7 @@ static int usb_gen_fill_deviceinfo(struct usb_fifo *,
|
||||
static int ugen_re_enumerate(struct usb_fifo *);
|
||||
static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
|
||||
static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
|
||||
static int ugen_fs_uninit(struct usb_fifo *f);
|
||||
static int ugen_fs_uninit(struct usb_fifo *f);
|
||||
|
||||
/* structures */
|
||||
|
||||
@ -951,23 +951,19 @@ ugen_re_enumerate(struct usb_fifo *f)
|
||||
if (error) {
|
||||
return (error);
|
||||
}
|
||||
/* get the device unconfigured */
|
||||
error = ugen_set_config(f, USB_UNCONFIG_INDEX);
|
||||
if (error) {
|
||||
return (error);
|
||||
if (udev->flags.usb_mode != USB_MODE_HOST) {
|
||||
/* not possible in device side mode */
|
||||
return (ENOTTY);
|
||||
}
|
||||
/* do a bus-reset */
|
||||
mtx_lock(f->priv_mtx);
|
||||
error = usbd_req_re_enumerate(udev, f->priv_mtx);
|
||||
mtx_unlock(f->priv_mtx);
|
||||
|
||||
if (error) {
|
||||
return (ENXIO);
|
||||
/* make sure all FIFO's are gone */
|
||||
/* else there can be a deadlock */
|
||||
if (ugen_fs_uninit(f)) {
|
||||
/* ignore any errors */
|
||||
DPRINTFN(6, "no FIFOs\n");
|
||||
}
|
||||
/* restore configuration to index 0 */
|
||||
error = ugen_set_config(f, 0);
|
||||
if (error) {
|
||||
return (error);
|
||||
if (udev->re_enumerate_wait == 0) {
|
||||
udev->re_enumerate_wait = 1;
|
||||
usb_needs_explore(udev->bus, 0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@ -1775,9 +1771,11 @@ ugen_set_power_mode(struct usb_fifo *f, int mode)
|
||||
|
||||
/* if we are powered off we need to re-enumerate first */
|
||||
if (old_mode == USB_POWER_MODE_OFF) {
|
||||
err = ugen_re_enumerate(f);
|
||||
if (err)
|
||||
return (err);
|
||||
if (udev->flags.usb_mode == USB_MODE_HOST) {
|
||||
if (udev->re_enumerate_wait == 0)
|
||||
udev->re_enumerate_wait = 1;
|
||||
}
|
||||
/* set power mode will wake up the explore thread */
|
||||
}
|
||||
|
||||
/* set new power mode */
|
||||
|
@ -236,6 +236,26 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
|
||||
/* nothing to do */
|
||||
goto done;
|
||||
}
|
||||
/* check if device should be re-enumerated */
|
||||
|
||||
if (child->flags.usb_mode == USB_MODE_HOST) {
|
||||
usbd_enum_lock(child);
|
||||
if (child->re_enumerate_wait) {
|
||||
err = usbd_set_config_index(child, USB_UNCONFIG_INDEX);
|
||||
if (err == 0)
|
||||
err = usbd_req_re_enumerate(child, NULL);
|
||||
if (err == 0)
|
||||
err = usbd_set_config_index(child, 0);
|
||||
if (err == 0) {
|
||||
err = usb_probe_and_attach(child,
|
||||
USB_IFACE_INDEX_ANY);
|
||||
}
|
||||
child->re_enumerate_wait = 0;
|
||||
err = 0;
|
||||
}
|
||||
usbd_enum_unlock(child);
|
||||
}
|
||||
|
||||
/* check if probe and attach should be done */
|
||||
|
||||
if (child->driver_added_refcount != refcount) {
|
||||
@ -1763,6 +1783,8 @@ static uint8_t
|
||||
usb_peer_should_wakeup(struct usb_device *udev)
|
||||
{
|
||||
return ((udev->power_mode == USB_POWER_MODE_ON) ||
|
||||
(udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
|
||||
(udev->re_enumerate_wait != 0) ||
|
||||
(udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
|
||||
(udev->pwr_save.write_refs != 0) ||
|
||||
((udev->pwr_save.read_refs != 0) &&
|
||||
|
Loading…
x
Reference in New Issue
Block a user