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:
hselasky 2010-10-04 22:21:30 +00:00
parent c4bf79bca3
commit 97650b7539
4 changed files with 77 additions and 33 deletions

View File

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

View File

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

View File

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

View File

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