[PATCH] Fix for USB ugen panics
Jay Cornwall
jay at evilrealms.net
Tue Jan 6 15:17:45 PST 2004
Hi
I can't remember the last time I actually remembered to attach a patch that I
was talking about.
My apologies. :)
--
Cheers,
Jay
http://www.evilrealms.net/ - Systems Administrator & Developer
http://www.imperial.ac.uk/ - 3rd year CS student
-------------- next part --------------
Index: sys/dev/usb/ugen.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ugen.c,v
retrieving revision 1.81
diff -u -3 -p -r1.81 ugen.c
--- sys/dev/usb/ugen.c 9 Nov 2003 09:17:22 -0000 1.81
+++ sys/dev/usb/ugen.c 6 Jan 2004 22:29:32 -0000
@@ -313,8 +313,8 @@ ugen_set_config(struct ugen_softc *sc, i
usbd_device_handle dev = sc->sc_udev;
usbd_interface_handle iface;
usb_endpoint_descriptor_t *ed;
- struct ugen_endpoint *sce;
- u_int8_t niface, nendpt;
+ struct ugen_endpoint *sce, **sce_cache, ***sce_cache_arr;
+ u_int8_t niface, niface_cache, nendpt, *nendpt_cache;
int ifaceno, endptno, endpt;
usbd_status err;
int dir;
@@ -322,10 +322,6 @@ ugen_set_config(struct ugen_softc *sc, i
DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
USBDEVNAME(sc->sc_dev), configno, sc));
-#if defined(__FreeBSD__)
- ugen_destroy_devnodes(sc);
-#endif
-
/* We start at 1, not 0, because we don't care whether the
* control endpoint is open or not. It is always present.
*/
@@ -337,37 +333,104 @@ ugen_set_config(struct ugen_softc *sc, i
return (USBD_IN_USE);
}
- /* Avoid setting the current value. */
- if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {
- err = usbd_set_config_no(dev, configno, 1);
- if (err)
- return (err);
- }
-
err = usbd_interface_count(dev, &niface);
if (err)
return (err);
- memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+
+ /* store an array of endpoint descriptors to clear if the configuration
+ * change succeeds - these aren't available afterwards */
+ nendpt_cache = malloc(sizeof(u_int8_t) * niface, M_TEMP, M_WAITOK);
+ sce_cache_arr = malloc(sizeof(struct ugen_endpoint **) * niface, M_TEMP,
+ M_WAITOK);
+ niface_cache = niface;
+
for (ifaceno = 0; ifaceno < niface; ifaceno++) {
DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
err = usbd_device2interface_handle(dev, ifaceno, &iface);
if (err)
- return (err);
+ panic("ugen_set_config: can't obtain interface handle");
err = usbd_endpoint_count(iface, &nendpt);
if (err)
- return (err);
+ panic("ugen_set_config: endpoint count failed");
+
+ /* store endpoint descriptors for each interface */
+ nendpt_cache[ifaceno] = nendpt;
+ sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP,
+ M_WAITOK);
+ sce_cache_arr[ifaceno] = sce_cache;
+
for (endptno = 0; endptno < nendpt; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
- sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
- DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
- "(%d,%d), sce=%p\n",
- endptno, endpt, UE_GET_ADDR(endpt),
- UE_GET_DIR(endpt), sce));
- sce->sc = sc;
- sce->edesc = ed;
- sce->iface = iface;
+ sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ }
+ }
+
+ /* Avoid setting the current value. */
+ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {
+ /* attempt to perform the configuration change */
+ err = usbd_set_config_no(dev, configno, 1);
+ if (err) {
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++)
+ free(sce_cache_arr[ifaceno], M_TEMP);
+ free(sce_cache_arr, M_TEMP);
+ free(nendpt_cache, M_TEMP);
+ return (err);
+ }
+ }
+
+#if defined(__FreeBSD__)
+ ugen_destroy_devnodes(sc);
+#endif
+
+ /* now we can clear the old interface's ugen_endpoints */
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++) {
+ sce_cache = sce_cache_arr[ifaceno];
+ for(endptno = 0; endptno < nendpt_cache[ifaceno]; endptno++) {
+ sce = sce_cache[endptno];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+ }
+
+ /* and free the cache storing them */
+ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++)
+ free(sce_cache_arr[ifaceno], M_TEMP);
+ free(sce_cache_arr, M_TEMP);
+ free(nendpt_cache, M_TEMP);
+
+ /* no endpoints if the device is in the unconfigured state */
+ if (configno != USB_UNCONFIG_NO)
+ {
+ /* set the new configuration's ugen_endpoints */
+ err = usbd_interface_count(dev, &niface);
+ if (err)
+ panic("ugen_set_config: interface count failed");
+
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+ for (ifaceno = 0; ifaceno < niface; ifaceno++) {
+ DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
+ err = usbd_device2interface_handle(dev, ifaceno, &iface);
+ if (err)
+ panic("ugen_set_config: can't obtain interface handle");
+ err = usbd_endpoint_count(iface, &nendpt);
+ if (err)
+ panic("ugen_set_config: endpoint count failed");
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
+ DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
+ "(%d,%d), sce=%p\n",
+ endptno, endpt, UE_GET_ADDR(endpt),
+ UE_GET_DIR(endpt), sce));
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
}
}
@@ -1014,8 +1077,8 @@ ugen_set_interface(struct ugen_softc *sc
usbd_interface_handle iface;
usb_endpoint_descriptor_t *ed;
usbd_status err;
- struct ugen_endpoint *sce;
- u_int8_t niface, nendpt, endptno, endpt;
+ struct ugen_endpoint *sce, **sce_cache;
+ u_int8_t niface, nendpt, nendpt_cache, endptno, endpt;
int dir;
DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno));
@@ -1033,30 +1096,45 @@ ugen_set_interface(struct ugen_softc *sc
if (err)
return (err);
-#if defined(__FreeBSD__)
- /* destroy the existing devices, we remake the new ones in a moment */
- ugen_destroy_devnodes(sc);
-#endif
+ /* store an array of endpoint descriptors to clear if the interface
+ * change succeeds - these aren't available afterwards */
+ sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP,
+ M_WAITOK);
+ nendpt_cache = nendpt;
- /* XXX should only do this after setting new altno has succeeded */
for (endptno = 0; endptno < nendpt; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT;
- sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
- sce->sc = 0;
- sce->edesc = 0;
- sce->iface = 0;
+ sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir];
}
/* change setting */
err = usbd_set_interface(iface, altno);
- if (err)
+ if (err) {
+ free(sce_cache, M_TEMP);
return (err);
+ }
err = usbd_endpoint_count(iface, &nendpt);
if (err)
- return (err);
+ panic("ugen_set_interface: endpoint count failed");
+
+#if defined(__FreeBSD__)
+ /* destroy the existing devices, we remake the new ones in a moment */
+ ugen_destroy_devnodes(sc);
+#endif
+
+ /* now we can clear the old interface's ugen_endpoints */
+ for (endptno = 0; endptno < nendpt_cache; endptno++) {
+ sce = sce_cache[endptno];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+ free(sce_cache, M_TEMP);
+
+ /* set the new interface's ugen_endpoints */
for (endptno = 0; endptno < nendpt; endptno++) {
ed = usbd_interface2endpoint_descriptor(iface,endptno);
endpt = ed->bEndpointAddress;
More information about the freebsd-current
mailing list