Module Name:    src
Committed By:   ws
Date:           Tue Feb 20 15:48:37 UTC 2018

Modified Files:
        src/sys/dev/usb: files.usb uftdi.c ugen.c

Log Message:
Attach uftdi to each interface found in the device separately.
This allows for other drivers (e.g. ugen) to attach to some of
the other interfaces.

Allow ugen to attach only to some of the interfaces found in a device.


To generate a diff of this commit:
cvs rdiff -u -r1.148 -r1.149 src/sys/dev/usb/files.usb
cvs rdiff -u -r1.66 -r1.67 src/sys/dev/usb/uftdi.c
cvs rdiff -u -r1.137 -r1.138 src/sys/dev/usb/ugen.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/files.usb
diff -u src/sys/dev/usb/files.usb:1.148 src/sys/dev/usb/files.usb:1.149
--- src/sys/dev/usb/files.usb:1.148	Sun Dec 10 17:03:07 2017
+++ src/sys/dev/usb/files.usb	Tue Feb 20 15:48:37 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files.usb,v 1.148 2017/12/10 17:03:07 bouyer Exp $
+#	$NetBSD: files.usb,v 1.149 2018/02/20 15:48:37 ws Exp $
 #
 # Config file and device description for machine-independent USB code.
 # Included by ports that need it.  Ports that use it must provide
@@ -138,6 +138,7 @@ file	dev/usb/ucom.c			ucom | ucombus		ne
 # Generic devices
 device	ugen
 attach	ugen at usbdevif
+attach	ugen at usbifif with ugenif
 file	dev/usb/ugen.c			ugen			needs-flag
 
 
@@ -387,7 +388,7 @@ file	dev/usb/umodem.c		umodem
 
 # FTDI serial driver
 device	uftdi: ucombus
-attach	uftdi at usbdevif
+attach	uftdi at usbifif
 file	dev/usb/uftdi.c			uftdi
 
 # Prolific PL2303 serial driver

Index: src/sys/dev/usb/uftdi.c
diff -u src/sys/dev/usb/uftdi.c:1.66 src/sys/dev/usb/uftdi.c:1.67
--- src/sys/dev/usb/uftdi.c:1.66	Fri Dec 22 14:18:29 2017
+++ src/sys/dev/usb/uftdi.c	Tue Feb 20 15:48:37 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: uftdi.c,v 1.66 2017/12/22 14:18:29 jakllsch Exp $	*/
+/*	$NetBSD: uftdi.c,v 1.67 2018/02/20 15:48:37 ws Exp $	*/
 
 /*
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uftdi.c,v 1.66 2017/12/22 14:18:29 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uftdi.c,v 1.67 2018/02/20 15:48:37 ws Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -62,9 +62,7 @@ int uftdidebug = 0;
 #define DPRINTFN(n,x)
 #endif
 
-#define UFTDI_CONFIG_INDEX	0
-#define UFTDI_IFACE_INDEX	0
-#define UFTDI_MAX_PORTS		4
+#define UFTDI_CONFIG_NO		1
 
 /*
  * These are the default number of bytes transferred per frame if the
@@ -83,17 +81,17 @@ int uftdidebug = 0;
 struct uftdi_softc {
 	device_t		sc_dev;		/* base device */
 	struct usbd_device *	sc_udev;	/* device */
-	struct usbd_interface *	sc_iface[UFTDI_MAX_PORTS];	/* interface */
+	struct usbd_interface *	sc_iface;	/* interface */
+	int			sc_iface_no;
 
 	enum uftdi_type		sc_type;
 	u_int			sc_hdrlen;
-	u_int			sc_numports;
 	u_int			sc_chiptype;
 
 	u_char			sc_msr;
 	u_char			sc_lsr;
 
-	device_t		sc_subdev[UFTDI_MAX_PORTS];
+	device_t		sc_subdev;
 
 	u_char			sc_dying;
 
@@ -190,29 +188,30 @@ CFATTACH_DECL2_NEW(uftdi, sizeof(struct 
 int
 uftdi_match(device_t parent, cfdata_t match, void *aux)
 {
-	struct usb_attach_arg *uaa = aux;
+	struct usbif_attach_arg *uiaa = aux;
 
 	DPRINTFN(20,("uftdi: vendor=0x%x, product=0x%x\n",
-		     uaa->uaa_vendor, uaa->uaa_product));
+		     uiaa->uiaa_vendor, uiaa->uiaa_product));
 
-	return uftdi_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ?
-		UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+	if (uiaa->uiaa_configno != UFTDI_CONFIG_NO)
+		return UMATCH_NONE;
+
+	return uftdi_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL ?
+		UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE;
 }
 
 void
 uftdi_attach(device_t parent, device_t self, void *aux)
 {
 	struct uftdi_softc *sc = device_private(self);
-	struct usb_attach_arg *uaa = aux;
-	struct usbd_device *dev = uaa->uaa_device;
-	struct usbd_interface *iface;
+	struct usbif_attach_arg *uiaa = aux;
+	struct usbd_device *dev = uiaa->uiaa_device;
+	struct usbd_interface *iface = uiaa->uiaa_iface;
 	usb_device_descriptor_t *ddesc;
 	usb_interface_descriptor_t *id;
 	usb_endpoint_descriptor_t *ed;
 	char *devinfop;
-	const char *devname = device_xname(self);
-	int i,idx;
-	usbd_status err;
+	int i;
 	struct ucom_attach_args ucaa;
 
 	DPRINTFN(10,("\nuftdi_attach: sc=%p\n", sc));
@@ -224,122 +223,86 @@ uftdi_attach(device_t parent, device_t s
 	aprint_normal_dev(self, "%s\n", devinfop);
 	usbd_devinfo_free(devinfop);
 
-	/* Move the device into the configured state. */
-	err = usbd_set_config_index(dev, UFTDI_CONFIG_INDEX, 1);
-	if (err) {
-		aprint_error("\n%s: failed to set configuration, err=%s\n",
-		       devname, usbd_errstr(err));
-		goto bad;
-	}
-
 	sc->sc_dev = self;
 	sc->sc_udev = dev;
-	sc->sc_numports = 1;
+	sc->sc_iface_no = uiaa->uiaa_ifaceno;
 	sc->sc_type = UFTDI_TYPE_8U232AM; /* most devices are post-8U232AM */
 	sc->sc_hdrlen = 0;
-	if (uaa->uaa_vendor == USB_VENDOR_FTDI
-	    && uaa->uaa_product == USB_PRODUCT_FTDI_SERIAL_8U100AX) {
+	if (uiaa->uiaa_vendor == USB_VENDOR_FTDI
+	    && uiaa->uiaa_product == USB_PRODUCT_FTDI_SERIAL_8U100AX) {
 		sc->sc_type = UFTDI_TYPE_SIO;
 		sc->sc_hdrlen = 1;
 	}
 
 	ddesc = usbd_get_device_descriptor(dev);
 	sc->sc_chiptype = UGETW(ddesc->bcdDevice);
-	switch (sc->sc_chiptype) {
-	case 0x500: /* 2232D */
-	case 0x700: /* 2232H */
-		sc->sc_numports = 2;
-		break;
-	case 0x800: /* 4232H */
-		sc->sc_numports = 4;
-		break;
-	case 0x200: /* 232/245AM */
-	case 0x400: /* 232/245BL */
-	case 0x600: /* 232/245R */
-	default:
-		break;
-	}
-
-	for (idx = UFTDI_IFACE_INDEX; idx < sc->sc_numports; idx++) {
-		err = usbd_device2interface_handle(dev, idx, &iface);
-		if (err) {
-			aprint_error(
-			    "\n%s: failed to get interface idx=%d, err=%s\n",
-			    devname, idx, usbd_errstr(err));
-			goto bad;
-		}
 
-		id = usbd_get_interface_descriptor(iface);
+	id = usbd_get_interface_descriptor(iface);
 
-		sc->sc_iface[idx] = iface;
+	sc->sc_iface = iface;
 
-		ucaa.ucaa_bulkin = ucaa.ucaa_bulkout = -1;
-		ucaa.ucaa_ibufsize = ucaa.ucaa_obufsize = 0;
-		for (i = 0; i < id->bNumEndpoints; i++) {
-			int addr, dir, attr;
-			ed = usbd_interface2endpoint_descriptor(iface, i);
-			if (ed == NULL) {
-				aprint_error_dev(self,
-				    "could not read endpoint descriptor: %s\n",
-				    usbd_errstr(err));
-				goto bad;
-			}
-
-			addr = ed->bEndpointAddress;
-			dir = UE_GET_DIR(ed->bEndpointAddress);
-			attr = ed->bmAttributes & UE_XFERTYPE;
-			if (dir == UE_DIR_IN && attr == UE_BULK) {
-				ucaa.ucaa_bulkin = addr;
-				ucaa.ucaa_ibufsize = UGETW(ed->wMaxPacketSize);
-				if (ucaa.ucaa_ibufsize >= UFTDI_MAX_IBUFSIZE)
-					ucaa.ucaa_ibufsize = UFTDI_MAX_IBUFSIZE;
-			} else if (dir == UE_DIR_OUT && attr == UE_BULK) {
-				ucaa.ucaa_bulkout = addr;
-				ucaa.ucaa_obufsize = UGETW(ed->wMaxPacketSize)
-				    - sc->sc_hdrlen;
-				if (ucaa.ucaa_obufsize >= UFTDI_MAX_OBUFSIZE)
-					ucaa.ucaa_obufsize = UFTDI_MAX_OBUFSIZE;
-				/* Limit length if we have a 6-bit header.  */
-				if ((sc->sc_hdrlen > 0) &&
-				    (ucaa.ucaa_obufsize > UFTDIOBUFSIZE))
-					ucaa.ucaa_obufsize = UFTDIOBUFSIZE;
-			} else {
-				aprint_error_dev(self,
-				    "unexpected endpoint\n");
-				goto bad;
-			}
-		}
-		if (ucaa.ucaa_bulkin == -1) {
+	ucaa.ucaa_bulkin = ucaa.ucaa_bulkout = -1;
+	ucaa.ucaa_ibufsize = ucaa.ucaa_obufsize = 0;
+	for (i = 0; i < id->bNumEndpoints; i++) {
+		int addr, dir, attr;
+		ed = usbd_interface2endpoint_descriptor(iface, i);
+		if (ed == NULL) {
 			aprint_error_dev(self,
-			    "Could not find data bulk in\n");
+			    "could not read endpoint descriptor\n");
 			goto bad;
 		}
-		if (ucaa.ucaa_bulkout == -1) {
-			aprint_error_dev(self,
-			    "Could not find data bulk out\n");
+
+		addr = ed->bEndpointAddress;
+		dir = UE_GET_DIR(ed->bEndpointAddress);
+		attr = ed->bmAttributes & UE_XFERTYPE;
+		if (dir == UE_DIR_IN && attr == UE_BULK) {
+			ucaa.ucaa_bulkin = addr;
+			ucaa.ucaa_ibufsize = UGETW(ed->wMaxPacketSize);
+			if (ucaa.ucaa_ibufsize >= UFTDI_MAX_IBUFSIZE)
+				ucaa.ucaa_ibufsize = UFTDI_MAX_IBUFSIZE;
+		} else if (dir == UE_DIR_OUT && attr == UE_BULK) {
+			ucaa.ucaa_bulkout = addr;
+			ucaa.ucaa_obufsize = UGETW(ed->wMaxPacketSize)
+			    - sc->sc_hdrlen;
+			if (ucaa.ucaa_obufsize >= UFTDI_MAX_OBUFSIZE)
+				ucaa.ucaa_obufsize = UFTDI_MAX_OBUFSIZE;
+			/* Limit length if we have a 6-bit header.  */
+			if ((sc->sc_hdrlen > 0) &&
+			    (ucaa.ucaa_obufsize > UFTDIOBUFSIZE))
+				ucaa.ucaa_obufsize = UFTDIOBUFSIZE;
+		} else {
+			aprint_error_dev(self, "unexpected endpoint\n");
 			goto bad;
 		}
-
-		ucaa.ucaa_portno = FTDI_PIT_SIOA + idx;
-		/* ucaa_bulkin, ucaa_bulkout set above */
-		if (ucaa.ucaa_ibufsize == 0)
-			ucaa.ucaa_ibufsize = UFTDIIBUFSIZE;
-		ucaa.ucaa_ibufsizepad = ucaa.ucaa_ibufsize;
-		if (ucaa.ucaa_obufsize == 0)
-			ucaa.ucaa_obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen;
-		ucaa.ucaa_opkthdrlen = sc->sc_hdrlen;
-		ucaa.ucaa_device = dev;
-		ucaa.ucaa_iface = iface;
-		ucaa.ucaa_methods = &uftdi_methods;
-		ucaa.ucaa_arg = sc;
-		ucaa.ucaa_info = NULL;
-
-		DPRINTF(("uftdi: in=0x%x out=0x%x isize=0x%x osize=0x%x\n",
-			ucaa.ucaa_bulkin, ucaa.ucaa_bulkout,
-			ucaa.ucaa_ibufsize, ucaa.ucaa_obufsize));
-		sc->sc_subdev[idx] = config_found_sm_loc(self, "ucombus", NULL,
-		    &ucaa, ucomprint, ucomsubmatch);
 	}
+	if (ucaa.ucaa_bulkin == -1) {
+		aprint_error_dev(self, "Could not find data bulk in\n");
+		goto bad;
+	}
+	if (ucaa.ucaa_bulkout == -1) {
+		aprint_error_dev(self, "Could not find data bulk out\n");
+		goto bad;
+	}
+
+	ucaa.ucaa_portno = FTDI_PIT_SIOA + sc->sc_iface_no;
+	/* ucaa_bulkin, ucaa_bulkout set above */
+	if (ucaa.ucaa_ibufsize == 0)
+		ucaa.ucaa_ibufsize = UFTDIIBUFSIZE;
+	ucaa.ucaa_ibufsizepad = ucaa.ucaa_ibufsize;
+	if (ucaa.ucaa_obufsize == 0)
+		ucaa.ucaa_obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen;
+	ucaa.ucaa_opkthdrlen = sc->sc_hdrlen;
+	ucaa.ucaa_device = dev;
+	ucaa.ucaa_iface = iface;
+	ucaa.ucaa_methods = &uftdi_methods;
+	ucaa.ucaa_arg = sc;
+	ucaa.ucaa_info = NULL;
+
+	DPRINTF(("uftdi: in=0x%x out=0x%x isize=0x%x osize=0x%x\n",
+		ucaa.ucaa_bulkin, ucaa.ucaa_bulkout,
+		ucaa.ucaa_ibufsize, ucaa.ucaa_obufsize));
+	sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL,
+	    &ucaa, ucomprint, ucomsubmatch);
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
 
@@ -371,29 +334,21 @@ uftdi_activate(device_t self, enum devac
 void
 uftdi_childdet(device_t self, device_t child)
 {
-	int i;
 	struct uftdi_softc *sc = device_private(self);
 
-	for (i = 0; i < sc->sc_numports; i++) {
-		if (sc->sc_subdev[i] == child)
-			break;
-	}
-	KASSERT(i < sc->sc_numports);
-	sc->sc_subdev[i] = NULL;
+	KASSERT(child == sc->sc_subdev);
+	sc->sc_subdev = NULL;
 }
 
 int
 uftdi_detach(device_t self, int flags)
 {
 	struct uftdi_softc *sc = device_private(self);
-	int i;
 
 	DPRINTF(("uftdi_detach: sc=%p flags=%d\n", sc, flags));
 	sc->sc_dying = 1;
-	for (i=0; i < sc->sc_numports; i++) {
-		if (sc->sc_subdev[i] != NULL)
-			config_detach(sc->sc_subdev[i], flags);
-	}
+	if (sc->sc_subdev != NULL)
+		config_detach(sc->sc_subdev, flags);
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
 
@@ -466,7 +421,7 @@ uftdi_read(void *vsc, int portno, u_char
 			 lsr, sc->sc_lsr));
 		sc->sc_msr = msr;
 		sc->sc_lsr = lsr;
-		ucom_status_change(device_private(sc->sc_subdev[portno-1]));
+		ucom_status_change(device_private(sc->sc_subdev));
 	}
 
 	/* Adjust buffer pointer to skip status prefix */

Index: src/sys/dev/usb/ugen.c
diff -u src/sys/dev/usb/ugen.c:1.137 src/sys/dev/usb/ugen.c:1.138
--- src/sys/dev/usb/ugen.c:1.137	Sun Jan 21 13:57:12 2018
+++ src/sys/dev/usb/ugen.c	Tue Feb 20 15:48:37 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ugen.c,v 1.137 2018/01/21 13:57:12 skrll Exp $	*/
+/*	$NetBSD: ugen.c,v 1.138 2018/02/20 15:48:37 ws Exp $	*/
 
 /*
  * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.137 2018/01/21 13:57:12 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.138 2018/02/20 15:48:37 ws Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -170,7 +170,7 @@ Static int ugen_do_read(struct ugen_soft
 Static int ugen_do_write(struct ugen_softc *, int, struct uio *, int);
 Static int ugen_do_ioctl(struct ugen_softc *, int, u_long,
 			 void *, int, struct lwp *);
-Static int ugen_set_config(struct ugen_softc *, int);
+Static int ugen_set_config(struct ugen_softc *, int, int);
 Static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *,
 					       int, int *);
 Static usbd_status ugen_set_interface(struct ugen_softc *, int, int);
@@ -181,13 +181,17 @@ Static void ugen_clear_endpoints(struct 
 #define UGENENDPOINT(n) (minor(n) & 0xf)
 #define UGENDEV(u, e) (makedev(0, ((u) << 4) | (e)))
 
+int	ugenif_match(device_t, cfdata_t, void *);
+void	ugenif_attach(device_t, device_t, void *);
 int	ugen_match(device_t, cfdata_t, void *);
 void	ugen_attach(device_t, device_t, void *);
 int	ugen_detach(device_t, int);
 int	ugen_activate(device_t, enum devact);
 extern struct cfdriver ugen_cd;
-CFATTACH_DECL_NEW(ugen, sizeof(struct ugen_softc), ugen_match, ugen_attach,
-    ugen_detach, ugen_activate);
+CFATTACH_DECL_NEW(ugen, sizeof(struct ugen_softc), ugen_match,
+    ugen_attach, ugen_detach, ugen_activate);
+CFATTACH_DECL_NEW(ugenif, sizeof(struct ugen_softc), ugenif_match,
+    ugenif_attach, ugen_detach, ugen_activate);
 
 /* toggle to control attach priority. -1 means "let autoconf decide" */
 int ugen_override = -1;
@@ -211,11 +215,38 @@ ugen_match(device_t parent, cfdata_t mat
 		return UMATCH_NONE;
 }
 
+int
+ugenif_match(device_t parent, cfdata_t match, void *aux)
+{
+	if (match->cf_flags & 1)
+		return UMATCH_HIGHEST;
+	else
+		return UMATCH_NONE;
+}
+
 void
 ugen_attach(device_t parent, device_t self, void *aux)
 {
-	struct ugen_softc *sc = device_private(self);
 	struct usb_attach_arg *uaa = aux;
+	struct usbif_attach_arg uiaa;
+
+	memset(&uiaa, 0, sizeof uiaa);
+	uiaa.uiaa_port = uaa->uaa_port;
+	uiaa.uiaa_vendor = uaa->uaa_vendor;
+	uiaa.uiaa_product = uaa->uaa_product;
+	uiaa.uiaa_release = uaa->uaa_release;
+	uiaa.uiaa_device = uaa->uaa_device;
+	uiaa.uiaa_configno = -1;
+	uiaa.uiaa_ifaceno = -1;
+
+	ugenif_attach(parent, self, &uiaa);
+}
+
+void
+ugenif_attach(device_t parent, device_t self, void *aux)
+{
+	struct ugen_softc *sc = device_private(self);
+	struct usbif_attach_arg *uiaa = aux;
 	struct usbd_device *udev;
 	char *devinfop;
 	usbd_status err;
@@ -227,12 +258,12 @@ ugen_attach(device_t parent, device_t se
 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
 	cv_init(&sc->sc_detach_cv, "ugendet");
 
-	devinfop = usbd_devinfo_alloc(uaa->uaa_device, 0);
+	devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0);
 	aprint_normal_dev(self, "%s\n", devinfop);
 	usbd_devinfo_free(devinfop);
 
 	sc->sc_dev = self;
-	sc->sc_udev = udev = uaa->uaa_device;
+	sc->sc_udev = udev = uiaa->uiaa_device;
 
 	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
 		for (dir = OUT; dir <= IN; dir++) {
@@ -244,18 +275,25 @@ ugen_attach(device_t parent, device_t se
 		}
 	}
 
-	/* First set configuration index 0, the default one for ugen. */
-	err = usbd_set_config_index(udev, 0, 0);
-	if (err) {
-		aprint_error_dev(self,
-		    "setting configuration index 0 failed\n");
-		sc->sc_dying = 1;
-		return;
+	if (uiaa->uiaa_ifaceno < 0) {
+		/*
+		 * If we attach the whole device,
+		 * set configuration index 0, the default one.
+		 */
+		err = usbd_set_config_index(udev, 0, 0);
+		if (err) {
+			aprint_error_dev(self,
+			    "setting configuration index 0 failed\n");
+			sc->sc_dying = 1;
+			return;
+		}
 	}
+
+	/* Get current configuration */
 	conf = usbd_get_config_descriptor(udev)->bConfigurationValue;
 
 	/* Set up all the local state for this configuration. */
-	err = ugen_set_config(sc, conf);
+	err = ugen_set_config(sc, conf, uiaa->uiaa_ifaceno < 0);
 	if (err) {
 		aprint_error_dev(self, "setting configuration %d failed\n",
 		    conf);
@@ -268,7 +306,6 @@ ugen_attach(device_t parent, device_t se
 	if (!pmf_device_register(self, NULL, NULL))
 		aprint_error_dev(self, "couldn't establish power handler\n");
 
-	return;
 }
 
 Static void
@@ -285,7 +322,7 @@ ugen_clear_endpoints(struct ugen_softc *
 }
 
 Static int
-ugen_set_config(struct ugen_softc *sc, int configno)
+ugen_set_config(struct ugen_softc *sc, int configno, int chkopen)
 {
 	struct usbd_device *dev = sc->sc_udev;
 	usb_config_descriptor_t *cdesc;
@@ -300,17 +337,19 @@ ugen_set_config(struct ugen_softc *sc, i
 	DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
 		    device_xname(sc->sc_dev), configno, sc));
 
-	/*
-	 * We start at 1, not 0, because we don't care whether the
-	 * control endpoint is open or not. It is always present.
-	 */
-	for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++)
-		if (sc->sc_is_open[endptno]) {
-			DPRINTFN(1,
-			     ("ugen_set_config: %s - endpoint %d is open\n",
-			      device_xname(sc->sc_dev), endptno));
-			return USBD_IN_USE;
-		}
+	if (chkopen) {
+		/*
+		 * We start at 1, not 0, because we don't care whether the
+		 * control endpoint is open or not. It is always present.
+		 */
+		for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++)
+			if (sc->sc_is_open[endptno]) {
+				DPRINTFN(1,
+				     ("ugen_set_config: %s - endpoint %d is open\n",
+				      device_xname(sc->sc_dev), endptno));
+				return USBD_IN_USE;
+			}
+	}
 
 	/* Avoid setting the current value. */
 	cdesc = usbd_get_config_descriptor(dev);
@@ -320,12 +359,12 @@ ugen_set_config(struct ugen_softc *sc, i
 			return err;
 	}
 
+	ugen_clear_endpoints(sc);
+
 	err = usbd_interface_count(dev, &niface);
 	if (err)
 		return err;
 
-	ugen_clear_endpoints(sc);
-
 	for (ifaceno = 0; ifaceno < niface; ifaceno++) {
 		DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
 		err = usbd_device2interface_handle(dev, ifaceno, &iface);
@@ -1601,7 +1640,7 @@ ugen_do_ioctl(struct ugen_softc *sc, int
 	case USB_SET_CONFIG:
 		if (!(flag & FWRITE))
 			return EPERM;
-		err = ugen_set_config(sc, *(int *)addr);
+		err = ugen_set_config(sc, *(int *)addr, 1);
 		switch (err) {
 		case USBD_NORMAL_COMPLETION:
 			break;

Reply via email to