Date: Fri, 28 Sep 2007 21:25:30 +0800 From: Eugene Grosbein <eugen@kuzbass.ru> To: bug-followup@freebsd.org Cc: usb@freebsd.org Subject: Re: usb/91546: [umodem] [patch] Nokia 6630 mobile phone does not work Message-ID: <20070928132530.GA1634@grosbein.pp.ru>
next in thread | raw e-mail | index | archive | help
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 &&
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20070928132530.GA1634>