diff options
author | thompsa <thompsa@FreeBSD.org> | 2009-09-28 07:53:55 +0000 |
---|---|---|
committer | thompsa <thompsa@FreeBSD.org> | 2009-09-28 07:53:55 +0000 |
commit | fe6300971a702031259ab5ba25bbe3a7424cf78f (patch) | |
tree | 28cb94845c971c3365137621c5761d003857e3ab /sys/dev | |
parent | f23c472dc2b4fb09b14c469c1a5ae414e74a1285 (diff) | |
download | FreeBSD-src-fe6300971a702031259ab5ba25bbe3a7424cf78f.zip FreeBSD-src-fe6300971a702031259ab5ba25bbe3a7424cf78f.tar.gz |
Add basic support for USB Network Control Model (NCM) v1.0 to if_cdce.c.
http://www.usb.org/developers/devclass_docs/NCM10.zip
Submitted by: Hans Petter Selasky
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/net/if_cdce.c | 590 | ||||
-rw-r--r-- | sys/dev/usb/net/if_cdcereg.h | 28 | ||||
-rw-r--r-- | sys/dev/usb/usb.h | 5 | ||||
-rw-r--r-- | sys/dev/usb/usb_cdc.h | 103 |
4 files changed, 697 insertions, 29 deletions
diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c index 52ea206..86f7a9b 100644 --- a/sys/dev/usb/net/if_cdce.c +++ b/sys/dev/usb/net/if_cdce.c @@ -40,6 +40,11 @@ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf */ +/* + * USB Network Control Model (NCM) + * http://www.usb.org/developers/devclass_docs/NCM10.zip + */ + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -89,6 +94,11 @@ static usb_callback_t cdce_bulk_read_callback; static usb_callback_t cdce_intr_read_callback; static usb_callback_t cdce_intr_write_callback; +#if CDCE_HAVE_NCM +static usb_callback_t cdce_ncm_bulk_write_callback; +static usb_callback_t cdce_ncm_bulk_read_callback; +#endif + static uether_fn_t cdce_attach_post; static uether_fn_t cdce_init; static uether_fn_t cdce_stop; @@ -159,6 +169,61 @@ static const struct usb_config cdce_config[CDCE_N_TRANSFER] = { }, }; +#if CDCE_HAVE_NCM +static const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = { + + [CDCE_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 0, + .frames = CDCE_NCM_RX_FRAMES_MAX, + .bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN), + .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,}, + .callback = cdce_ncm_bulk_read_callback, + .timeout = 0, /* no timeout */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 0, + .frames = CDCE_NCM_TX_FRAMES_MAX, + .bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = cdce_ncm_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_INTR_RX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_read_callback, + .timeout = 0, + .usb_mode = USB_MODE_HOST, + }, + + [CDCE_INTR_TX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DEVICE, + }, +}; +#endif + static device_method_t cdce_methods[] = { /* USB interface */ DEVMETHOD(usb_handle_request, cdce_handle_request), @@ -213,8 +278,151 @@ static const struct usb_device_id cdce_devs[] = { {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)}, {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)}, + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)}, }; +#if CDCE_HAVE_NCM +/*------------------------------------------------------------------------* + * cdce_ncm_init + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +cdce_ncm_init(struct cdce_softc *sc) +{ + struct usb_ncm_parameters temp; + struct usb_device_request req; + uDWord value; + int err; + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, sizeof(temp)); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + &temp, 0, NULL, 1000 /* ms */); + if (err) + return (1); + + /* Read correct set of parameters according to device mode */ + + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + sc->sc_ncm.rx_max = UGETW(temp.dwNtbInMaxSize); + sc->sc_ncm.tx_max = UGETW(temp.dwNtbOutMaxSize); + sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder); + sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor); + sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment); + } else { + sc->sc_ncm.rx_max = UGETW(temp.dwNtbOutMaxSize); + sc->sc_ncm.tx_max = UGETW(temp.dwNtbInMaxSize); + sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder); + sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor); + sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment); + } + + /* Verify maximum receive length */ + + if (err || (sc->sc_ncm.rx_max < 32) || + (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) { + DPRINTFN(1, "Using default maximum receive length\n"); + sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN; + } + + /* Verify maximum transmit length */ + + if (err || (sc->sc_ncm.tx_max < 32) || + (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) { + DPRINTFN(1, "Using default maximum transmit length\n"); + sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN; + } + + /* + * Verify that the structure alignment is: + * - power of two + * - not greater than the maximum transmit length + * - not less than four bytes + */ + if (err || (sc->sc_ncm.tx_struct_align < 4) || + (sc->sc_ncm.tx_struct_align != + ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) || + (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) { + DPRINTFN(1, "Using default other alignment: 4 bytes\n"); + sc->sc_ncm.tx_struct_align = 4; + } + + /* + * Verify that the payload alignment is: + * - power of two + * - not greater than the maximum transmit length + * - not less than four bytes + */ + if (err || (sc->sc_ncm.tx_modulus < 4) || + (sc->sc_ncm.tx_modulus != + ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) || + (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) { + DPRINTFN(1, "Using default transmit modulus: 4 bytes\n"); + sc->sc_ncm.tx_modulus = 4; + } + + /* Verify that the payload remainder */ + + if (err || (sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) { + DPRINTFN(1, "Using default transmit remainder: 0 bytes\n"); + sc->sc_ncm.tx_remainder = 0; + } + + /* Additional configuration, will fail in device side mode, which is OK. */ + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, 4); + USETDW(value, sc->sc_ncm.rx_max); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + &value, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting input size " + "to %u failed.\n", sc->sc_ncm.rx_max); + } + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_CRC_MODE; + USETW(req.wValue, 0); /* no CRC */ + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + NULL, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting CRC mode to off failed.\n"); + } + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_NTB_FORMAT; + USETW(req.wValue, 0); /* NTB-16 */ + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + NULL, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting NTB format to 16-bit failed.\n"); + } + + return (0); /* success */ +} +#endif + static int cdce_probe(device_t dev) { @@ -240,31 +448,31 @@ cdce_attach(device_t dev) const struct usb_cdc_union_descriptor *ud; const struct usb_interface_descriptor *id; const struct usb_cdc_ethernet_descriptor *ued; + const struct usb_config *pcfg; int error; uint8_t i; + uint8_t data_iface_no; char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + sc->sc_ue.ue_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - if (sc->sc_flags & CDCE_FLAG_NO_UNION) { - sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; - sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; - sc->sc_data_iface_no = 0; /* not used */ - goto alloc_transfers; - } ud = usbd_find_descriptor (uaa->device, NULL, uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1); - if ((ud == NULL) || (ud->bLength < sizeof(*ud))) { - device_printf(dev, "no union descriptor!\n"); - goto detach; + if ((ud == NULL) || (ud->bLength < sizeof(*ud)) || + (sc->sc_flags & CDCE_FLAG_NO_UNION)) { + DPRINTFN(1, "No union descriptor!\n"); + sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; + sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; + goto alloc_transfers; } - sc->sc_data_iface_no = ud->bSlaveInterface[0]; + data_iface_no = ud->bSlaveInterface[0]; for (i = 0;; i++) { @@ -274,8 +482,7 @@ cdce_attach(device_t dev) id = usbd_get_interface_descriptor(iface); - if (id && (id->bInterfaceNumber == - sc->sc_data_iface_no)) { + if (id && (id->bInterfaceNumber == data_iface_no)) { sc->sc_ifaces_index[0] = i; sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); @@ -312,24 +519,30 @@ cdce_attach(device_t dev) alloc_transfers: - for (i = 0; i != 32; i++) { + pcfg = cdce_config; /* Default Configuration */ - error = usbd_set_alt_interface_index - (uaa->device, sc->sc_ifaces_index[0], i); + for (i = 0; i != 32; i++) { - if (error) { - device_printf(dev, "no valid alternate " - "setting found!\n"); - goto detach; - } - error = usbd_transfer_setup - (uaa->device, sc->sc_ifaces_index, - sc->sc_xfer, cdce_config, CDCE_N_TRANSFER, - sc, &sc->sc_mtx); + error = usbd_set_alt_interface_index(uaa->device, + sc->sc_ifaces_index[0], i); + if (error) + break; +#if CDCE_HAVE_NCM + if ((i == 0) && (cdce_ncm_init(sc) == 0)) + pcfg = cdce_ncm_config; +#endif + error = usbd_transfer_setup(uaa->device, + sc->sc_ifaces_index, sc->sc_xfer, + pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx); - if (error == 0) { + if (error == 0) break; - } + } + + if (error || (i == 32)) { + device_printf(dev, "No valid alternate " + "setting found!\n"); + goto detach; } ued = usbd_find_descriptor @@ -768,3 +981,328 @@ cdce_handle_request(device_t dev, { return (ENXIO); /* use builtin handler */ } + +#if CDCE_HAVE_NCM +static uint8_t +cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index); + struct mbuf *m; + uint32_t rem; + uint32_t offset; + uint32_t last_offset; + uint32_t n; + + usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index); + + offset = sizeof(sc->sc_ncm.hdr) + + sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp); + + /* Store last valid offset before alignment */ + last_offset = offset; + + /* Align offset correctly */ + offset = sc->sc_ncm.tx_remainder - + ((0UL - offset) & (0UL - sc->sc_ncm.tx_modulus)); + + for (n = 0; n != CDCE_NCM_SUBFRAMES_MAX; n++) { + + /* check if end of transmit buffer is reached */ + + if (offset >= sc->sc_ncm.tx_max) + break; + + /* compute maximum buffer size */ + + rem = sc->sc_ncm.tx_max - offset; + + IFQ_DRV_DEQUEUE(&(ifp->if_snd), m); + + if (m == NULL) + break; + + if (m->m_pkthdr.len > rem) { + if (n == 0) { + /* The frame won't fit in our buffer */ + DPRINTFN(1, "Frame too big to be transmitted!\n"); + m_freem(m); + ifp->if_oerrors++; + n--; + continue; + } + /* Wait till next buffer becomes ready */ + IFQ_DRV_PREPEND(&(ifp->if_snd), m); + break; + } + usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len); + + USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len); + USETW(sc->sc_ncm.dp[n].wFrameIndex, offset); + + /* Update offset */ + offset += m->m_pkthdr.len; + + /* Store last valid offset before alignment */ + last_offset = offset; + + /* Align offset correctly */ + offset = sc->sc_ncm.tx_remainder - + ((0UL - offset) & (0UL - sc->sc_ncm.tx_modulus)); + + /* + * If there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + /* Free mbuf */ + + m_freem(m); + + /* Pre-increment interface counter */ + + ifp->if_opackets++; + } + + if (n == 0) + return (1); + + rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4); + + USETW(sc->sc_ncm.dpt.wLength, rem); + + /* zero the rest of the data pointer entries */ + for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) { + USETW(sc->sc_ncm.dp[n].wFrameLength, 0); + USETW(sc->sc_ncm.dp[n].wFrameIndex, 0); + } + + /* set frame length */ + usbd_xfer_set_frame_len(xfer, index, last_offset); + + /* Fill out 16-bit header */ + sc->sc_ncm.hdr.dwSignature[0] = 'N'; + sc->sc_ncm.hdr.dwSignature[1] = 'C'; + sc->sc_ncm.hdr.dwSignature[2] = 'M'; + sc->sc_ncm.hdr.dwSignature[3] = 'H'; + USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr)); + USETW(sc->sc_ncm.hdr.wBlockLength, offset); + USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq); + USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr)); + + sc->sc_ncm.tx_seq++; + + /* Fill out 16-bit frame table header */ + sc->sc_ncm.dpt.dwSignature[0] = 'N'; + sc->sc_ncm.dpt.dwSignature[1] = 'C'; + sc->sc_ncm.dpt.dwSignature[2] = 'M'; + sc->sc_ncm.dpt.dwSignature[3] = 'x'; + USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0); /* reserved */ + + usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr)); + usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt), + sizeof(sc->sc_ncm.dpt)); + usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt), + &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp)); + return (0); +} + +static void +cdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + uint16_t x; + int actlen; + int aframes; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + DPRINTFN(10, "transfer complete: " + "%u bytes in %u frames\n", actlen, aframes); + + case USB_ST_SETUP: + for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) { + if (cdce_ncm_fill_tx_frames(xfer, x)) + break; + } + + if (x != 0) { + usbd_xfer_set_frames(xfer, x); + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + DPRINTFN(10, "Transfer error: %s\n", + usbd_errstr(error)); + + /* update error counter */ + ifp->if_oerrors += 1; + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + } + break; + } +} + +static void +cdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct mbuf *m; + int sumdata; + int sumlen; + int actlen; + int aframes; + int temp; + int nframes; + int x; + int offset; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL); + + DPRINTFN(1, "received %u bytes in %u frames\n", + actlen, aframes); + + if (actlen < (sizeof(sc->sc_ncm.hdr) + + sizeof(sc->sc_ncm.dpt))) { + DPRINTFN(1, "frame too short\n"); + goto tr_stall; + } + usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr), + sizeof(sc->sc_ncm.hdr)); + + if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') || + (sc->sc_ncm.hdr.dwSignature[1] != 'C') || + (sc->sc_ncm.hdr.dwSignature[2] != 'M') || + (sc->sc_ncm.hdr.dwSignature[3] != 'H')) { + DPRINTFN(1, "invalid HDR signature\n"); + goto tr_stall; + } + temp = UGETW(sc->sc_ncm.hdr.wBlockLength); + if (temp > sumlen) { + DPRINTFN(1, "unsupported block length %u/%u\n", + temp, sumlen); + goto tr_stall; + } + temp = UGETW(sc->sc_ncm.hdr.wDptIndex); + if ((temp + sizeof(sc->sc_ncm.dpt)) > actlen) { + DPRINTFN(1, "invalid DPT index\n"); + goto tr_stall; + } + usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt), + sizeof(sc->sc_ncm.dpt)); + + if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') || + (sc->sc_ncm.dpt.dwSignature[1] != 'C') || + (sc->sc_ncm.dpt.dwSignature[2] != 'M') || + (sc->sc_ncm.dpt.dwSignature[3] != 'x')) { + DPRINTFN(1, "invalid DPT signature\n"); + goto tr_stall; + } + nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4; + + /* Subtract size of header and last zero padded entry */ + if (nframes >= (2 + 1)) + nframes -= (2 + 1); + else + nframes = 0; + + DPRINTFN(1, "nframes = %u\n", nframes); + + temp += sizeof(sc->sc_ncm.dpt); + + if ((temp + (4 * nframes)) > actlen) + goto tr_stall; + + if (nframes > CDCE_NCM_SUBFRAMES_MAX) { + DPRINTFN(1, "Truncating number of frames from %u to %u\n", + nframes, CDCE_NCM_SUBFRAMES_MAX); + nframes = CDCE_NCM_SUBFRAMES_MAX; + } + usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes)); + + sumdata = 0; + + for (x = 0; x != nframes; x++) { + + offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex); + temp = UGETW(sc->sc_ncm.dp[x].wFrameLength); + if ((offset + temp) > actlen) { + DPRINTFN(1, "invalid frame detected (ignored)\n"); + m = NULL; + + } else if (temp >= sizeof(struct ether_header)) { + /* + * allocate a suitable memory buffer, if + * possible + */ + if (temp > (MCLBYTES - ETHER_ALIGN)) { + m = NULL; + continue; + } if (temp > (MHLEN - ETHER_ALIGN)) { + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + } else { + m = m_gethdr(M_DONTWAIT, MT_DATA); + } + } else { + m = NULL; /* dump it */ + } + + DPRINTFN(16, "frame %u, offset = %u, length = %u \n", + x, offset, temp); + + /* check if we have a buffer */ + if (m) { + m_adj(m, ETHER_ALIGN); + + usbd_copy_out(pc, offset, m->m_data, temp); + + /* enqueue */ + uether_rxmbuf(&sc->sc_ue, m, temp); + + sumdata += temp; + } else { + ifp->if_ierrors++; + } + } + + DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen); + + case USB_ST_SETUP: + usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + uether_rxflush(&sc->sc_ue); /* must be last */ + break; + + default: /* Error */ + DPRINTFN(1, "error = %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { +tr_stall: + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + } + break; + } +} +#endif diff --git a/sys/dev/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h index 367e167..5593169 100644 --- a/sys/dev/usb/net/if_cdcereg.h +++ b/sys/dev/usb/net/if_cdcereg.h @@ -38,6 +38,18 @@ #define CDCE_FRAMES_MAX 8 /* units */ #define CDCE_IND_SIZE_MAX 32 /* bytes */ +#define CDCE_NCM_TX_MAXLEN 2048UL /* bytes */ +#define CDCE_NCM_TX_FRAMES_MAX 8 /* units */ + +#define CDCE_NCM_RX_MAXLEN (1UL << 14) /* bytes */ +#define CDCE_NCM_RX_FRAMES_MAX 1 /* units */ + +#define CDCE_NCM_SUBFRAMES_MAX 32 /* units */ + +#ifndef CDCE_HAVE_NCM +#define CDCE_HAVE_NCM 1 +#endif + enum { CDCE_BULK_RX, CDCE_BULK_TX, @@ -46,9 +58,24 @@ enum { CDCE_N_TRANSFER, }; +struct cdce_ncm { + struct usb_ncm16_hdr hdr; + struct usb_ncm16_dpt dpt; + struct usb_ncm16_dp dp[CDCE_NCM_SUBFRAMES_MAX]; + uint32_t rx_max; + uint32_t tx_max; + uint16_t tx_remainder; + uint16_t tx_modulus; + uint16_t tx_struct_align; + uint16_t tx_seq; +}; + struct cdce_softc { struct usb_ether sc_ue; struct mtx sc_mtx; +#if CDCE_HAVE_NCM + struct cdce_ncm sc_ncm; +#endif struct usb_xfer *sc_xfer[CDCE_N_TRANSFER]; struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX]; struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX]; @@ -59,7 +86,6 @@ struct cdce_softc { #define CDCE_FLAG_RX_DATA 0x0010 uint8_t sc_eaddr_str_index; - uint8_t sc_data_iface_no; uint8_t sc_ifaces_index[2]; }; diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h index 1198398..8ebaeec 100644 --- a/sys/dev/usb/usb.h +++ b/sys/dev/usb/usb.h @@ -424,9 +424,9 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t; #define UISUBCLASS_MOBILE_DIRECT_LINE_MODEL 10 #define UISUBCLASS_OBEX 11 #define UISUBCLASS_ETHERNET_EMULATION_MODEL 12 +#define UISUBCLASS_NETWORK_CONTROL_MODEL 13 #define UIPROTO_CDC_AT 1 -#define UIPROTO_CDC_ETH_512X4 0x76 /* FreeBSD specific */ #define UICLASS_HID 0x03 #define UISUBCLASS_BOOT 1 @@ -461,7 +461,7 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t; #define UIPROTO_HSHUBMTT 1 #define UICLASS_CDC_DATA 0x0a -#define UISUBCLASS_DATA 0 +#define UISUBCLASS_DATA 0x00 #define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ #define UIPROTO_DATA_HDLC 0x31 /* HDLC */ #define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ @@ -475,6 +475,7 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t; #define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ #define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc. */ #define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ +#define UIPROTO_DATA_NCM 0x01 /* Network Control Model */ #define UICLASS_SMARTCARD 0x0b #define UICLASS_FIRM_UPD 0x0c diff --git a/sys/dev/usb/usb_cdc.h b/sys/dev/usb/usb_cdc.h index f6e312c..7032bf4 100644 --- a/sys/dev/usb/usb_cdc.h +++ b/sys/dev/usb/usb_cdc.h @@ -188,4 +188,107 @@ struct usb_cdc_notification { #define UCDC_MDM_PARITY_ERR 0x20 #define UCDC_MDM_OVERRUN_ERR 0x40 +/* + * Network Control Model, NCM16 + NCM32, protocol definitions + */ +struct usb_ncm16_hdr { + uDWord dwSignature; + uWord wHeaderLength; + uWord wSequence; + uWord wBlockLength; + uWord wDptIndex; +} __packed; + +struct usb_ncm16_dp { + uWord wFrameIndex; + uWord wFrameLength; +} __packed; + +struct usb_ncm16_dpt { + uDWord dwSignature; + uWord wLength; + uWord wNextNdpIndex; + struct usb_ncm16_dp dp[0]; +} __packed; + +struct usb_ncm32_hdr { + uDWord dwSignature; + uWord wHeaderLength; + uWord wSequence; + uDWord dwBlockLength; + uDWord dwDptIndex; +} __packed; + +struct usb_ncm32_dp { + uDWord dwFrameIndex; + uDWord dwFrameLength; +} __packed; + +struct usb_ncm32_dpt { + uDWord dwSignature; + uWord wLength; + uWord wReserved6; + uDWord dwNextNdpIndex; + uDWord dwReserved12; + struct usb_ncm32_dp dp[0]; +} __packed; + +/* Communications interface class specific descriptors */ + +#define UCDC_NCM_FUNC_DESC_SUBTYPE 0x1A + +struct usb_ncm_func_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bcdNcmVersion[2]; + uByte bmNetworkCapabilities; +#define UCDC_NCM_CAP_FILTER 0x01 +#define UCDC_NCM_CAP_MAC_ADDR 0x02 +#define UCDC_NCM_CAP_ENCAP 0x04 +#define UCDC_NCM_CAP_MAX_DATA 0x08 +#define UCDC_NCM_CAP_CRCMODE 0x10 +} __packed; + +/* Communications interface specific class request codes */ + +#define UCDC_NCM_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define UCDC_NCM_SET_ETHERNET_POWER_MGMT_PATTERN_FILTER 0x41 +#define UCDC_NCM_GET_ETHERNET_POWER_MGMT_PATTERN_FILTER 0x42 +#define UCDC_NCM_SET_ETHERNET_PACKET_FILTER 0x43 +#define UCDC_NCM_GET_ETHERNET_STATISTIC 0x44 +#define UCDC_NCM_GET_NTB_PARAMETERS 0x80 +#define UCDC_NCM_GET_NET_ADDRESS 0x81 +#define UCDC_NCM_SET_NET_ADDRESS 0x82 +#define UCDC_NCM_GET_NTB_FORMAT 0x83 +#define UCDC_NCM_SET_NTB_FORMAT 0x84 +#define UCDC_NCM_GET_NTB_INPUT_SIZE 0x85 +#define UCDC_NCM_SET_NTB_INPUT_SIZE 0x86 +#define UCDC_NCM_GET_MAX_DATAGRAM_SIZE 0x87 +#define UCDC_NCM_SET_MAX_DATAGRAM_SIZE 0x88 +#define UCDC_NCM_GET_CRC_MODE 0x89 +#define UCDC_NCM_SET_CRC_MODE 0x8A + +struct usb_ncm_parameters { + uWord wLength; + uWord bmNtbFormatsSupported; +#define UCDC_NCM_FORMAT_NTB16 0x0001 +#define UCDC_NCM_FORMAT_NTB32 0x0002 + uDWord dwNtbInMaxSize; + uWord wNdpInDivisor; + uWord wNdpInPayloadRemainder; + uWord wNdpInAlignment; + uWord wReserved14; + uDWord dwNtbOutMaxSize; + uWord wNdpOutDivisor; + uWord wNdpOutPayloadRemainder; + uWord wNdpOutAlignment; + uWord wReserved26; +} __packed; + +/* Communications interface specific class notification codes */ +#define UCDC_NCM_NOTIF_NETWORK_CONNECTION 0x00 +#define UCDC_NCM_NOTIF_RESPONSE_AVAILABLE 0x01 +#define UCDC_NCM_NOTIF_CONNECTION_SPEED_CHANGE 0x2A + #endif /* _USB_CDC_H_ */ |