usb/117185: [umodem] Add support for UNION interface descriptor

Eugene Grosbein eugen at grosbein.pp.ru
Sun Oct 14 08:10:02 PDT 2007


>Number:         117185
>Category:       usb
>Synopsis:       [umodem] Add support for UNION interface descriptor
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-usb
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Oct 14 15:10:01 UTC 2007
>Closed-Date:
>Last-Modified:
>Originator:     Eugene Grosbein
>Release:        FreeBSD 7.0-PRERELEASE i386
>Organization:
Svyaz-Service
>Environment:
System: FreeBSD grosbein.pp.ru 7.0-PRERELEASE FreeBSD 7.0-PRERELEASE #1: Sun Oct 14 20:41:31 KRAST 2007 eu at grosbein.pp.ru:/usr/local/obj/usr/local/src/sys/DADV i386

>Description:
	Currently, umodem(4) does not attach USB modems not having CM interface
	descriptor but having UNION interface descriptor instead.
	Examples are Nokia smartphones like E50, N73 etc.

>How-To-Repeat:
	Attach Nokia E50 using its USB data cable (choose 'PC Suite mode')
	and see that it's detected as 'ugen' device.

>Fix:

	The following patch teaches umodem(4) to use UNION descriptor,
	it creates /dev/cuaU0 device node that works just fine
	for PPP over GPRS.

	This obsoletes usb/91546 containing incomplete patch
	by PR originator and version of this patch for 6.2-STABLE by me.

--- sys/dev/usb/umodem.c.orig	2007-10-14 20:51:58.000000000 +0800
+++ sys/dev/usb/umodem.c	2007-10-14 21:03:58.000000000 +0800
@@ -172,13 +172,14 @@
 	struct task		sc_task;
 };
 
-static void	*umodem_get_desc(usbd_device_handle dev, int type, int subtype);
+static void	*umodem_get_desc(usbd_device_handle dev, usb_descriptor_t *, int type, int subtype);
+static usbd_interface_handle umodem_get_interface(struct usb_attach_arg *uaa, int ifcno);
 static usbd_status umodem_set_comm_feature(struct umodem_softc *sc,
 					   int feature, int state);
 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 int	umodem_get_caps(struct usb_attach_arg *, int, int *, int *);
 
 static void	umodem_get_status(void *, int portno, u_char *lsr, u_char *msr);
 static void	umodem_set(void *, int, int, int);
@@ -262,10 +263,7 @@
 	if (ret == UMATCH_NONE)
 		return (ret);
 
-	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))
+	if (umodem_get_caps(uaa, -1, &cm, &acm) == -1)
 		return (UMATCH_NONE);
 
 	return ret;
@@ -279,7 +277,6 @@
 	usbd_device_handle dev = uaa->device;
 	usb_interface_descriptor_t *id;
 	usb_endpoint_descriptor_t *ed;
-	usb_cdc_cm_descriptor_t *cmd;
 	int data_ifcno;
 	int i;
 	struct ucom_softc *ucom;
@@ -297,15 +294,14 @@
 	device_printf(self, "iclass %d/%d\n", id->bInterfaceClass,
 	  id->bInterfaceSubClass);
 
-	umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap);
-
 	/* Get the data interface no. */
-	cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
-	if (cmd == NULL) {
-		device_printf(sc->sc_dev, "no CM descriptor\n");
+ 	sc->sc_data_iface_no = data_ifcno =
+ 	    umodem_get_caps(uaa, sc->sc_ctl_iface_no, &sc->sc_cm_cap, &sc->sc_acm_cap);
+ 
+ 	if (data_ifcno == -1) {
+		device_printf(sc->sc_dev,"%s: no pointer to data interface\n");
 		goto bad;
 	}
-	sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface;
 
 	device_printf(sc->sc_dev,
 	    "data interface %d, has %sCM over data, has %sbreak\n",
@@ -530,27 +526,50 @@
 	ucom_status_change(&sc->sc_ucom);
 }
 
-void
-umodem_get_caps(usbd_device_handle dev, int *cm, int *acm)
+static int
+umodem_get_caps(struct usb_attach_arg *uaa, int ctl_iface_no, int *cm, int *acm)
 {
 	usb_cdc_cm_descriptor_t *cmd;
 	usb_cdc_acm_descriptor_t *cad;
+	usb_cdc_union_descriptor_t *cud;
+	usbd_device_handle dev = uaa->device;
+	usbd_interface_handle iface;
+	int iface_no = 0;
 
 	*cm = *acm = 0;
 
-	cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+	cmd = umodem_get_desc(dev, NULL, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
 	if (cmd == NULL) {
 		DPRINTF(("umodem_get_desc: no CM desc\n"));
-		return;
+	} else {
+		*cm = cmd->bmCapabilities;
 	}
-	*cm = cmd->bmCapabilities;
 
-	cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+	cad = umodem_get_desc(dev, NULL, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
 	if (cad == NULL) {
 		DPRINTF(("umodem_get_desc: no ACM desc\n"));
-		return;
+	} else {
+		*acm = cad->bmCapabilities;
+	}
+	
+	cud = NULL;
+	while ((cud = umodem_get_desc(dev, (usb_descriptor_t *)cud,
+			UDESC_CS_INTERFACE, UDESCSUB_CDC_UNION)))
+	{
+		iface_no = cud->bSlaveInterface[0];
+		if (ctl_iface_no == -1)
+			break;
+
+		iface = umodem_get_interface(uaa,iface_no);
+		if (ctl_iface_no == cud->bMasterInterface &&
+		    usbd_get_interface_descriptor(iface)->bNumEndpoints >= 2)
+			break;
+	}
+	if (cud == NULL) {
+		DPRINTF(("umodem_get_caps: no UNION desc\n"));
 	}
-	*acm = cad->bmCapabilities;
+	
+	return cmd ? cmd->bDataInterface : cud ? iface_no : -1;
 }
 
 void
@@ -566,6 +585,23 @@
 		*msr = sc->sc_msr;
 }
 
+static usbd_interface_handle
+umodem_get_interface(struct usb_attach_arg *uaa, int ifcno)
+{
+	int i;
+	usb_interface_descriptor_t *id;
+
+	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 == ifcno) {
+				return uaa->ifaces[i];
+			}
+		}
+	}
+	return NULL;
+}
+
 int
 umodem_param(void *addr, int portno, struct termios *t)
 {
@@ -756,14 +792,17 @@
 	return (USBD_NORMAL_COMPLETION);
 }
 
-void *
-umodem_get_desc(usbd_device_handle dev, int type, int subtype)
+static void *
+umodem_get_desc(usbd_device_handle dev, usb_descriptor_t *restart, int type, int subtype)
 {
 	usb_descriptor_t *desc;
 	usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev);
         uByte *p = (uByte *)cd;
         uByte *end = p + UGETW(cd->wTotalLength);
 
+	if (restart)
+		p = (uByte *)(restart) + restart->bLength;
+
 	while (p < end) {
 		desc = (usb_descriptor_t *)p;
 		if (desc->bDescriptorType == type &&
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-usb mailing list