From owner-freebsd-usb@FreeBSD.ORG Fri Sep 28 13:30:08 2007 Return-Path: Delivered-To: freebsd-usb@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4F0DC16A419 for ; Fri, 28 Sep 2007 13:30:08 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 3C2FC13C43E for ; Fri, 28 Sep 2007 13:30:08 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.1/8.14.1) with ESMTP id l8SDU7H4098743 for ; Fri, 28 Sep 2007 13:30:07 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.1/8.14.1/Submit) id l8SDU7oH098742; Fri, 28 Sep 2007 13:30:07 GMT (envelope-from gnats) Date: Fri, 28 Sep 2007 13:30:07 GMT Message-Id: <200709281330.l8SDU7oH098742@freefall.freebsd.org> To: freebsd-usb@FreeBSD.org From: Eugene Grosbein Cc: Subject: Re: usb/91546: [umodem] [patch] Nokia 6630 mobile phone does not work X-BeenThere: freebsd-usb@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Eugene Grosbein List-Id: FreeBSD support for USB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 28 Sep 2007 13:30:08 -0000 The following reply was made to PR usb/91546; it has been noted by GNATS. From: Eugene Grosbein To: bug-followup@freebsd.org Cc: usb@freebsd.org, "M. Warner Losh" Subject: Re: usb/91546: [umodem] [patch] Nokia 6630 mobile phone does not work Date: Fri, 28 Sep 2007 21:25:30 +0800 Hi! Here is the patch allowing to work with Nokia E50 (and hopefully others) as USB modem through device /dev/cuaU0 or like. This is basically the same code from NetBSD with one addition. The code from NetBSD allows to use UNION interface descriptor instead of CM descriptor but takes the first UNION descriptor it finds. That does not work for modern devices having lots of UNION descriptors, where needed is not first. The code uses umodem_get_desc() function to fine UNION, so I made it restartable with additional parameter. To start search from the beginning, the caller passes NULL, or it may pass the descriptor returned by umodem_get_desc() and it continues the search from the next descriptor. The function umodem_get_caps() obtains additional parameters and one of them is device control interface. It ignores any UNION which master interface is not device control interface and that has not at lease two endpoints (we need bulk in and out). So, it successfully finds needed descriptor in my case, attaches the device and it works. --- sys/dev/usb/umodem.c.orig 2007-09-26 21:22:37.000000000 +0800 +++ sys/dev/usb/umodem.c 2007-09-28 21:01:06.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); @@ -261,10 +262,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; @@ -276,7 +274,6 @@ usbd_device_handle dev = uaa->device; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; - usb_cdc_cm_descriptor_t *cmd; char *devinfo = NULL; const char *devname; usbd_status err; @@ -304,15 +301,14 @@ 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. */ - cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); - if (cmd == NULL) { - printf("%s: no CM descriptor\n", devname); + 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) { + printf("%s: no pointer to data interface\n", devname); goto bad; } - sc->sc_data_iface_no = data_ifcno = cmd->bDataInterface; printf("%s: data interface %d, has %sCM over data, has %sbreak\n", devname, data_ifcno, @@ -550,27 +546,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; } - *acm = cad->bmCapabilities; + if (cud == NULL) { + DPRINTF(("umodem_get_caps: no UNION desc\n")); + } + + return cmd ? cmd->bDataInterface : cud ? iface_no : -1; } void @@ -586,6 +605,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) { @@ -776,14 +812,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 &&