Re: [PATCH] Fix for USB ugen panics

From: Jay Cornwall <jay_at_evilrealms.net>
Date: Tue, 06 Jan 2004 23:17:40 +0000
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



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
_at__at_ -313,8 +313,8 _at__at_ 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;
_at__at_ -322,10 +322,6 _at__at_ 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.
 	 */
_at__at_ -337,37 +333,104 _at__at_ 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;
+			}
 		}
 	}
 
_at__at_ -1014,8 +1077,8 _at__at_ 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));
_at__at_ -1033,30 +1096,45 _at__at_ 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;
Received on Tue Jan 06 2004 - 14:17:45 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:37:36 UTC