diff options
author | Marc Balmer <mbalmer@cvs.openbsd.org> | 2007-07-23 16:41:16 +0000 |
---|---|---|
committer | Marc Balmer <mbalmer@cvs.openbsd.org> | 2007-07-23 16:41:16 +0000 |
commit | d4437547b4d6f249e597a360cc94565271503d3c (patch) | |
tree | 8428bef8937549febd8d4ee4b5bbd4861a3fddb2 /sys | |
parent | 42fc3749a744710d36ef56cf7bf2032a6c71bc09 (diff) |
This improves cdce(4) and makes some devices work that previously did not
(like the Sony Ericsson K610 mobile phone).
- Read hardware address from ethernet descriptor makes the Sony Ericsson
K610 work.
- Add support for status notifications (interrupt endpoint).
- Recognize if there is a union descriptori (thus remove CDCE_NO_UNION).
- Handle wrong union descriptors (master/slave values are swapped)
(Ambit NTL 250)
Diff done by Marc Winiger <mw@msys.ch>, tested by many on different
platforms and with various devices: cable modems, mobile phones, and,
zaurus as USB function.
ok jsg, claudio, kili
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/if_cdce.c | 239 | ||||
-rw-r--r-- | sys/dev/usb/if_cdcereg.h | 8 | ||||
-rw-r--r-- | sys/dev/usb/usbcdc.h | 26 |
3 files changed, 210 insertions, 63 deletions
diff --git a/sys/dev/usb/if_cdce.c b/sys/dev/usb/if_cdce.c index 9d475d90592..21fd5c66547 100644 --- a/sys/dev/usb/if_cdce.c +++ b/sys/dev/usb/if_cdce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_cdce.c,v 1.33 2007/06/14 10:11:15 mbalmer Exp $ */ +/* $OpenBSD: if_cdce.c,v 1.34 2007/07/23 16:41:15 mbalmer Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com> @@ -73,7 +73,6 @@ #include <dev/usb/if_cdcereg.h> -void *cdce_get_desc(usbd_device_handle dev, int type, int subtype); int cdce_tx_list_init(struct cdce_softc *); int cdce_rx_list_init(struct cdce_softc *); int cdce_newbuf(struct cdce_softc *, struct cdce_chain *, @@ -86,22 +85,23 @@ int cdce_ioctl(struct ifnet *, u_long, caddr_t); void cdce_init(void *); void cdce_watchdog(struct ifnet *); void cdce_stop(struct cdce_softc *); +void cdce_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); static uint32_t cdce_crc32(const void *, size_t); const struct cdce_type cdce_devs[] = { - {{ USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632 }, CDCE_NO_UNION }, - {{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501 }, CDCE_NO_UNION }, + {{ USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632 }, 0 }, + {{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501 }, 0 }, {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500 }, CDCE_ZAURUS }, - {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_A300 }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600 }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C700 }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C750 }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2 }, CDCE_ZAURUS | CDCE_NO_UNION }, - {{ USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00 }, CDCE_NO_UNION }, - {{ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET }, CDCE_NO_UNION }, - {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX }, CDCE_NO_UNION }, - {{ USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250 }, CDCE_NO_UNION }, + {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_A300 }, CDCE_ZAURUS }, + {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600 }, CDCE_ZAURUS }, + {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C700 }, CDCE_ZAURUS }, + {{ USB_VENDOR_SHARP, USB_PRODUCT_SHARP_C750 }, CDCE_ZAURUS }, + {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN }, CDCE_ZAURUS }, + {{ USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2 }, CDCE_ZAURUS }, + {{ USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00 }, 0 }, + {{ USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET }, 0 }, + {{ USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX }, 0 }, + {{ USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250 }, CDCE_SWAPUNION }, }; #define cdce_lookup(v, p) ((const struct cdce_type *)usb_lookup(cdce_devs, v, p)) @@ -158,10 +158,15 @@ cdce_attach(struct device *parent, struct device *self, void *aux) usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usb_cdc_union_descriptor_t *ud; + usb_cdc_ethernet_descriptor_t *ethd; usb_config_descriptor_t *cd; - int data_ifcno; + const usb_descriptor_t *desc; + usbd_desc_iter_t iter; + usb_string_descriptor_t eaddr_str; u_int16_t macaddr_hi; - int i, j, numalts; + int i, j, numalts, len; + int ctl_ifcno = -1; + int data_ifcno = -1; devinfop = usbd_devinfo_alloc(dev, 0); printf("\n%s: %s\n", sc->cdce_dev.dv_xname, devinfop); @@ -169,23 +174,47 @@ cdce_attach(struct device *parent, struct device *self, void *aux) sc->cdce_udev = uaa->device; sc->cdce_ctl_iface = uaa->iface; + id = usbd_get_interface_descriptor(sc->cdce_ctl_iface); + ctl_ifcno = id->bInterfaceNumber; t = cdce_lookup(uaa->vendor, uaa->product); if (t) sc->cdce_flags = t->cdce_flags; - if (sc->cdce_flags & CDCE_NO_UNION) - sc->cdce_data_iface = sc->cdce_ctl_iface; - else { - ud = cdce_get_desc(sc->cdce_udev, UDESC_CS_INTERFACE, - UDESCSUB_CDC_UNION); - if (ud == NULL) { - printf("%s: no union descriptor\n", - sc->cdce_dev.dv_xname); - return; + /* Get the data interface no. and capabilities */ + ethd = NULL; + usb_desc_iter_init(dev, &iter); + desc = usb_desc_iter_next(&iter); + while (desc) { + if (desc->bDescriptorType != UDESC_CS_INTERFACE) { + desc = usb_desc_iter_next(&iter); + continue; + } + switch(desc->bDescriptorSubtype) { + case UDESCSUB_CDC_UNION: + ud = (usb_cdc_union_descriptor_t *)desc; + if ((sc->cdce_flags & CDCE_SWAPUNION) == 0 && + ud->bMasterInterface == ctl_ifcno) + data_ifcno = ud->bSlaveInterface[0]; + if ((sc->cdce_flags & CDCE_SWAPUNION) && + ud->bSlaveInterface[0] == ctl_ifcno) + data_ifcno = ud->bMasterInterface; + break; + case UDESCSUB_CDC_ENF: + if (ethd) { + printf("%s: ", sc->cdce_dev.dv_xname); + printf("extra ethernet descriptor\n"); + return; + } + ethd = (usb_cdc_ethernet_descriptor_t *)desc; + break; } - data_ifcno = ud->bSlaveInterface[0]; + desc = usb_desc_iter_next(&iter); + } + if (data_ifcno == -1) { + sc->cdce_data_iface = sc->cdce_ctl_iface; + } else { for (i = 0; i < uaa->nifaces; i++) { if (uaa->ifaces[i] != NULL) { id = usbd_get_interface_descriptor( @@ -204,24 +233,42 @@ cdce_attach(struct device *parent, struct device *self, void *aux) return; } - /* Find endpoints. */ + id = usbd_get_interface_descriptor(sc->cdce_ctl_iface); + sc->cdce_intr_no = -1; + for (i = 0; i < id->bNumEndpoints && sc->cdce_intr_no == -1; i++) { + ed = usbd_interface2endpoint_descriptor(sc->cdce_ctl_iface, i); + if (!ed) { + printf("%s: no descriptor for interrupt endpoint %d\n", + sc->cdce_dev.dv_xname, i); + return; + } + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + printf("%s: status change notification available\n", + sc->cdce_dev.dv_xname); + sc->cdce_intr_no = ed->bEndpointAddress; + sc->cdce_intr_size = sizeof(sc->cdce_intr_buf); + } + } + id = usbd_get_interface_descriptor(sc->cdce_data_iface); cd = usbd_get_config_descriptor(sc->cdce_udev); numalts = usbd_get_no_alts(cd, id->bInterfaceNumber); for (j = 0; j < numalts; j++) { - if (usbd_set_interface(sc->cdce_data_iface, j)) { - printf("%s: setting alternate interface failed\n", - sc->cdce_dev.dv_xname); + if (usbd_set_interface(sc->cdce_data_iface, j)) { + printf("%s: interface alternate setting %d failed\n", + sc->cdce_dev.dv_xname, j); return; } + /* Find endpoints. */ id = usbd_get_interface_descriptor(sc->cdce_data_iface); sc->cdce_bulkin_no = sc->cdce_bulkout_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->cdce_data_iface, i); if (!ed) { - printf("%s: could not read endpoint descriptor\n", - sc->cdce_dev.dv_xname); + printf("%s: no descriptor for bulk endpoint %d\n", + sc->cdce_dev.dv_xname, i); return; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && @@ -232,14 +279,17 @@ cdce_attach(struct device *parent, struct device *self, void *aux) sc->cdce_bulkout_no = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { - /* XXX: CDC spec defines an interrupt pipe, but it is not - * needed for simple host-to-host applications. */ + /* XXX: CDC spec defines an interrupt pipe, but it is + * not needed for simple host-to-host applications. */ } else { - printf("%s: unexpected endpoint\n", - sc->cdce_dev.dv_xname); + printf("%s: unexpected endpoint, ep=%x attr=%x\n", + sc->cdce_dev.dv_xname, ed->bEndpointAddress, + ed->bmAttributes); } } - + if ((sc->cdce_bulkin_no != -1) && (sc->cdce_bulkout_no != -1)) { + goto found; + } } if (sc->cdce_bulkin_no == -1) { @@ -253,12 +303,32 @@ cdce_attach(struct device *parent, struct device *self, void *aux) return; } +found: s = splnet(); - macaddr_hi = htons(0x2acb); - bcopy(&macaddr_hi, &sc->cdce_arpcom.ac_enaddr[0], sizeof(u_int16_t)); - bcopy(&ticks, &sc->cdce_arpcom.ac_enaddr[2], sizeof(u_int32_t)); - sc->cdce_arpcom.ac_enaddr[5] = (u_int8_t)(sc->cdce_unit); + if (!ethd || usbd_get_string_desc(sc->cdce_udev, ethd->iMacAddress, 0, + &eaddr_str, &len)) { + macaddr_hi = htons(0x2acb); + bcopy(&macaddr_hi, &sc->cdce_arpcom.ac_enaddr[0], + sizeof(u_int16_t)); + bcopy(&ticks, &sc->cdce_arpcom.ac_enaddr[2], sizeof(u_int32_t)); + sc->cdce_arpcom.ac_enaddr[5] = (u_int8_t)(sc->cdce_unit); + } else { + for (i = 0; i < ETHER_ADDR_LEN * 2; i++) { + int c = UGETW(eaddr_str.bString[i]); + + if ('0' <= c && c <= '9') + c -= '0'; + else if ('A' <= c && c <= 'F') + c -= 'A' - 10; + else if ('a' <= c && c <= 'f') + c -= 'a' - 10; + c &= 0xf; + if (i % 2 == 0) + c <<= 4; + sc->cdce_arpcom.ac_enaddr[i / 2] |= c; + } + } printf("%s: address %s\n", sc->cdce_dev.dv_xname, ether_sprintf(sc->cdce_arpcom.ac_enaddr)); @@ -408,6 +478,18 @@ cdce_stop(struct cdce_softc *sc) sc->cdce_bulkout_pipe = NULL; } + if (sc->cdce_intr_pipe != NULL) { + err = usbd_abort_pipe(sc->cdce_intr_pipe); + if (err) + printf("%s: abort interrupt pipe failed: %s\n", + sc->cdce_dev.dv_xname, usbd_errstr(err)); + err = usbd_close_pipe(sc->cdce_intr_pipe); + if (err) + printf("%s: close interrupt pipe failed: %s\n", + sc->cdce_dev.dv_xname, usbd_errstr(err)); + sc->cdce_intr_pipe = NULL; + } + for (i = 0; i < CDCE_RX_LIST_CNT; i++) { if (sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf != NULL) { m_freem(sc->cdce_cdata.cdce_rx_chain[i].cdce_mbuf); @@ -519,6 +601,18 @@ cdce_init(void *xsc) s = splnet(); + if (sc->cdce_intr_no != -1 && sc->cdce_intr_pipe == NULL) { + err = usbd_open_pipe_intr(sc->cdce_ctl_iface, sc->cdce_intr_no, + USBD_SHORT_XFER_OK, &sc->cdce_intr_pipe, sc, + &sc->cdce_intr_buf, sc->cdce_intr_size, cdce_intr, + USBD_DEFAULT_INTERVAL); + if (err) { + printf("Failed to establish interrupt pipe: %s\n", + usbd_errstr(err)); + return; + } + } + if (cdce_tx_list_init(sc) == ENOBUFS) { printf("%s: tx list init failed\n", sc->cdce_dev.dv_xname); splx(s); @@ -775,25 +869,6 @@ cdce_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) splx(s); } -void * -cdce_get_desc(usbd_device_handle dev, 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); - - while (p < end) { - desc = (usb_descriptor_t *)p; - if (desc->bDescriptorType == type && - desc->bDescriptorSubtype == subtype) - return (desc); - p += desc->bLength; - } - - return (NULL); -} - int cdce_activate(struct device *self, enum devact act) { @@ -810,6 +885,50 @@ cdce_activate(struct device *self, enum devact act) return (0); } +void +cdce_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) +{ + struct cdce_softc *sc = addr; + usb_cdc_notification_t *buf = &sc->cdce_intr_buf; + usb_cdc_connection_speed_t *speed; + u_int32_t count; + + if (status == USBD_CANCELLED) + return; + + if (status != USBD_NORMAL_COMPLETION) { + printf("cdce_intr: status=%d\n", status); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(sc->cdce_intr_pipe); + return; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); + + if (buf->bmRequestType == UCDC_NOTIFICATION) { + switch (buf->bNotification) { + case UCDC_N_NETWORK_CONNECTION: + printf("%s: network %s\n", sc->cdce_dev.dv_xname, + UGETW(buf->wValue) ? "connected" : "disconnected"); + break; + case UCDC_N_CONNECTION_SPEED_CHANGE: + speed = (usb_cdc_connection_speed_t *)&buf->data; + printf("%s: upstream %d bps, downstream %d bps\n", + sc->cdce_dev.dv_xname, UGETDW(speed->dwUSBitRate), + UGETDW(speed->dwDSBitRate)); + break; + default: + printf("%s: bNotification 0x%x\n", + sc->cdce_dev.dv_xname, buf->bNotification); + } + } else { + printf("%s: bmRequestType=%d ", sc->cdce_dev.dv_xname, + buf->bmRequestType); + printf("wValue=%d wIndex=%d wLength=%d\n", UGETW(buf->wValue), + UGETW(buf->wIndex), UGETW(buf->wLength)); + } +} + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. diff --git a/sys/dev/usb/if_cdcereg.h b/sys/dev/usb/if_cdcereg.h index 1179a411d67..a989943f022 100644 --- a/sys/dev/usb/if_cdcereg.h +++ b/sys/dev/usb/if_cdcereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_cdcereg.h,v 1.2 2007/06/06 19:25:49 mk Exp $ */ +/* $OpenBSD: if_cdcereg.h,v 1.3 2007/07/23 16:41:15 mbalmer Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com> @@ -42,7 +42,7 @@ struct cdce_type { struct usb_devno cdce_dev; u_int16_t cdce_flags; #define CDCE_ZAURUS 1 -#define CDCE_NO_UNION 2 +#define CDCE_SWAPUNION 2 }; struct cdce_softc; @@ -71,6 +71,10 @@ struct cdce_softc { #define GET_IFP(sc) (&(sc)->cdce_arpcom.ac_if) usbd_device_handle cdce_udev; usbd_interface_handle cdce_ctl_iface; + int cdce_intr_no; + usbd_pipe_handle cdce_intr_pipe; + usb_cdc_notification_t cdce_intr_buf; + int cdce_intr_size; usbd_interface_handle cdce_data_iface; int cdce_bulkin_no; usbd_pipe_handle cdce_bulkin_pipe; diff --git a/sys/dev/usb/usbcdc.h b/sys/dev/usb/usbcdc.h index b43a96decca..0774a81ce4a 100644 --- a/sys/dev/usb/usbcdc.h +++ b/sys/dev/usb/usbcdc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbcdc.h,v 1.6 2007/06/09 11:06:53 mbalmer Exp $ */ +/* $OpenBSD: usbcdc.h,v 1.7 2007/07/23 16:41:15 mbalmer Exp $ */ /* $NetBSD: usbcdc.h,v 1.8 2001/02/16 20:15:57 kenh Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbcdc.h,v 1.7 1999/11/17 22:33:48 n_hibma Exp $ */ @@ -52,6 +52,13 @@ #define UDESCSUB_CDC_CS 7 /* Country Selection */ #define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */ #define UDESCSUB_CDC_USBT 9 /* USB Terminal */ +#define UDESCSUB_CDC_NCT 10 /* Network Channel Terminal */ +#define UDESCSUB_CDC_PUF 11 /* Protocol Unit */ +#define UDESCSUB_CDC_EUF 12 /* Extension Unit */ +#define UDESCSUB_CDC_MCMF 13 /* Multi-Channel Management */ +#define UDESCSUB_CDC_CCMF 14 /* CAPI Control Management */ +#define UDESCSUB_CDC_ENF 15 /* Ethernet Networking */ +#define UDESCSUB_CDC_ANF 16 /* ATM Networking */ typedef struct { uByte bLength; @@ -89,6 +96,17 @@ typedef struct { uByte bSlaveInterface[1]; } __packed usb_cdc_union_descriptor_t; +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte iMacAddress; + uDWord bmEthernetStatistics; + uWord wMaxSegmentSize; + uWord wNumberMCFilters; + uByte bNumberPowerFilters; +} __packed usb_cdc_ethernet_descriptor_t; + #define UCDC_SEND_ENCAPSULATED_COMMAND 0x00 #define UCDC_GET_ENCAPSULATED_RESPONSE 0x01 #define UCDC_SET_COMM_FEATURE 0x02 @@ -147,6 +165,12 @@ typedef struct { } __packed usb_cdc_notification_t; #define UCDC_NOTIFICATION_LENGTH 8 +typedef struct { + uDWord dwUSBitRate; + uDWord dwDSBitRate; +} __packed usb_cdc_connection_speed_t; +#define UCDC_CONNECTION_SPEED_LENGTH 8 + /* * Bits set in the SERIAL STATE notifcation (first byte of data) */ |