Subject: kern/29754: umodem doesn't support devices without CM or ACM
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <rix@estpak.ee>
List: netbsd-bugs
Date: 03/21/2005 14:38:00
>Number:         29754
>Category:       kern
>Synopsis:       umodem doesn't support devices without CM or ACM
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Mar 21 14:38:00 +0000 2005
>Originator:     rivo nurges
>Release:        NetBSD 2.99.16
>Organization:
>Environment:
System: NetBSD anarchy.estpak.ee 2.99.16 NetBSD 2.99.16 (ANARCHY) #56: Mon Mar 21 14:03:29 UTC 2005 rix@anarchy.estpak.ee:/export/src/sys/arch/i386/compile/ANARCHY i386
Architecture: i386
Machine: i386
>Description:
	Nokia 9300 with DKU-2 usb cable doesn't have CM and ACM descriptors but 
	has UNION descriptor pointing to data interface. NetBSD's current
	driver doesn't handle this.

>How-To-Repeat:
>Fix:
	Here is a patch.

Index: sys/dev/usb/umodem.c
===================================================================
RCS file: /NetBSD/src/sys/dev/usb/umodem.c,v
retrieving revision 1.49
diff -u -3 -p -r1.49 umodem.c
--- sys/dev/usb/umodem.c	23 Oct 2004 13:28:26 -0000	1.49
+++ sys/dev/usb/umodem.c	21 Mar 2005 14:10:51 -0000
@@ -129,8 +129,6 @@ Static usbd_status umodem_set_comm_featu
 Static usbd_status umodem_set_line_coding(struct umodem_softc *sc,
 					  usb_cdc_line_state_t *state);
 
-Static void	umodem_get_caps(usbd_device_handle, int *, int *);
-
 Static void	umodem_get_status(void *, int portno, u_char *lsr, u_char *msr);
 Static void	umodem_set(void *, int, int, int);
 Static void	umodem_dtr(struct umodem_softc *, int);
@@ -160,7 +158,6 @@ USB_MATCH(umodem)
 {
 	USB_MATCH_START(umodem, uaa);
 	usb_interface_descriptor_t *id;
-	int cm, acm;
 
 	if (uaa->iface == NULL)
 		return (UMATCH_NONE);
@@ -172,12 +169,6 @@ USB_MATCH(umodem)
 	    id->bInterfaceProtocol != UIPROTO_CDC_AT)
 		return (UMATCH_NONE);
 
-	umodem_get_caps(uaa->device, &cm, &acm);
-	if (!(cm & USB_CDC_CM_DOES_CM) ||
-	    !(cm & USB_CDC_CM_OVER_DATA) ||
-	    !(acm & USB_CDC_ACM_HAS_LINE))
-		return (UMATCH_NONE);
-
 	return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
 }
 
@@ -188,9 +179,12 @@ USB_ATTACH(umodem)
 	usb_interface_descriptor_t *id;
 	usb_endpoint_descriptor_t *ed;
 	const usb_cdc_cm_descriptor_t *cmd;
+	const usb_cdc_acm_descriptor_t *acmd;
+	const usb_cdc_union_descriptor_t *uniond;
+	const usb_descriptor_t *desc;
+	usbd_desc_iter_t iter;
 	char devinfo[1024];
 	usbd_status err;
-	int data_ifcno;
 	int i;
 	struct ucom_attach_args uca;
 
@@ -205,19 +199,38 @@ USB_ATTACH(umodem)
 	       devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
 	sc->sc_ctl_iface_no = id->bInterfaceNumber;
 
-	umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
+	/* Get the data interface no. and capabilities*/
+	usb_desc_iter_init(dev, &iter);
+	desc = usb_desc_iter_next(&iter);
+	while(desc) {
+		if(desc->bDescriptorType == UDESC_CS_INTERFACE) {
+			switch(desc->bDescriptorSubtype) {
+			case UDESCSUB_CDC_CM:
+				cmd = (usb_cdc_cm_descriptor_t *)desc;
+				sc->sc_cm_cap = cmd->bmCapabilities;
+				sc->sc_data_iface_no = cmd->bDataInterface;
+			break;
+			case UDESCSUB_CDC_ACM:
+				acmd = (usb_cdc_acm_descriptor_t *)desc;
+				sc->sc_acm_cap = acmd->bmCapabilities;
+			break;
+			case UDESCSUB_CDC_UNION:
+				uniond = (usb_cdc_union_descriptor_t *)desc;
+				sc->sc_data_iface_no = uniond->bSlaveInterface[0];
+			break;
+			}
+		}
+	desc = usb_desc_iter_next(&iter);
+	}
 
-	/* Get the data interface no. */
-	cmd = (usb_cdc_cm_descriptor_t *)usb_find_desc(dev, UDESC_CS_INTERFACE,
-						       UDESCSUB_CDC_CM);
-	if (cmd == NULL) {
-		printf("%s: no CM descriptor\n", USBDEVNAME(sc->sc_dev));
+	if (sc->sc_data_iface_no == 0) {
+		printf("%s: no pointer to data interface\n",
+		       USBDEVNAME(sc->sc_dev));
 		goto bad;
 	}
-	sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface;
 
 	printf("%s: data interface %d, has %sCM over data, has %sbreak\n",
-	       USBDEVNAME(sc->sc_dev), data_ifcno,
+	       USBDEVNAME(sc->sc_dev), sc->sc_data_iface_no,
 	       sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
 	       sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
 
@@ -225,7 +238,8 @@ USB_ATTACH(umodem)
 	for (i = 0; i < uaa->nifaces; i++) {
 		if (uaa->ifaces[i] != NULL) {
 			id = usbd_get_interface_descriptor(uaa->ifaces[i]);
-			if (id != NULL && id->bInterfaceNumber == data_ifcno) {
+			if (id != NULL &&
+                            id->bInterfaceNumber == sc->sc_data_iface_no) {
 				sc->sc_data_iface = uaa->ifaces[i];
 				uaa->ifaces[i] = NULL;
 			}
@@ -443,31 +457,6 @@ umodem_intr(usbd_xfer_handle xfer, usbd_
 			 sc->sc_notify_buf.bNotification));
 		break;
 	}
-}
-
-void
-umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
-{
-	const usb_cdc_cm_descriptor_t *cmd;
-	const usb_cdc_acm_descriptor_t *cad;
-
-	*cm = *acm = 0;
-
-	cmd = (usb_cdc_cm_descriptor_t *)usb_find_desc(dev, UDESC_CS_INTERFACE,
-						       UDESCSUB_CDC_CM);
-	if (cmd == NULL) {
-		DPRINTF(("umodem_get_desc: no CM desc\n"));
-		return;
-	}
-	*cm = cmd->bmCapabilities;
-
-	cad = (usb_cdc_acm_descriptor_t *)usb_find_desc(dev, UDESC_CS_INTERFACE,
-							UDESCSUB_CDC_ACM);
-	if (cad == NULL) {
-		DPRINTF(("umodem_get_desc: no ACM desc\n"));
-		return;
-	}
-	*acm = cad->bmCapabilities;
 }
 
 void