From owner-p4-projects@FreeBSD.ORG Thu Aug 27 20:25:45 2009 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 8EE631065692; Thu, 27 Aug 2009 20:25:45 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 39586106568C for ; Thu, 27 Aug 2009 20:25:45 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id E4ED78FC3F for ; Thu, 27 Aug 2009 20:25:44 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id n7RKPi6F092178 for ; Thu, 27 Aug 2009 20:25:44 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id n7RKPiOp092176 for perforce@freebsd.org; Thu, 27 Aug 2009 20:25:44 GMT (envelope-from hselasky@FreeBSD.org) Date: Thu, 27 Aug 2009 20:25:44 GMT Message-Id: <200908272025.n7RKPiOp092176@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 167909 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 27 Aug 2009 20:25:45 -0000 http://perforce.freebsd.org/chv.cgi?CH=167909 Change 167909 by hselasky@hselasky_laptop001 on 2009/08/27 20:24:48 USB Network: - Add basic support for NCM v1.0 to if_cdce.c. - http://www.usb.org/developers/devclass_docs/NCM10.zip Affected files ... .. //depot/projects/usb/src/sys/dev/usb/net/if_cdce.c#21 edit .. //depot/projects/usb/src/sys/dev/usb/net/if_cdcereg.h#5 edit .. //depot/projects/usb/src/sys/dev/usb/usb.h#47 edit .. //depot/projects/usb/src/sys/dev/usb/usb_cdc.h#13 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/net/if_cdce.c#21 (text+ko) ==== @@ -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 __FBSDID("$FreeBSD: src/sys/dev/usb/net/if_cdce.c,v 1.16 2009/08/24 05:00:33 alfred Exp $"); @@ -89,6 +94,11 @@ 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 @@ }, }; +#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 @@ {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 @@ 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 @@ 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 @@ alloc_transfers: + pcfg = cdce_config; /* Default Configuration */ + for (i = 0; i != 32; i++) { - error = usbd_set_alt_interface_index - (uaa->device, sc->sc_ifaces_index[0], i); + 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) { - 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); + if (error == 0) + break; + } - 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 @@ { 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 ==== //depot/projects/usb/src/sys/dev/usb/net/if_cdcereg.h#5 (text+ko) ==== @@ -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 @@ 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 @@ #define CDCE_FLAG_RX_DATA 0x0010 uint8_t sc_eaddr_str_index; - uint8_t sc_data_iface_no; uint8_t sc_ifaces_index[2]; }; ==== //depot/projects/usb/src/sys/dev/usb/usb.h#47 (text+ko) ==== @@ -424,6 +424,7 @@ #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 @@ -474,7 +475,6 @@ #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 UISUBCLASS_NCM 0x0d /* Network Control Model, NCM */ #define UIPROTO_DATA_NCM 0x01 /* Network Control Model */ #define UICLASS_SMARTCARD 0x0b ==== //depot/projects/usb/src/sys/dev/usb/usb_cdc.h#13 (text+ko) ==== @@ -196,7 +196,7 @@ uWord wHeaderLength; uWord wSequence; uWord wBlockLength; - uWord wDpIndex; + uWord wDptIndex; } __packed; struct usb_ncm16_dp { @@ -216,7 +216,7 @@ uWord wHeaderLength; uWord wSequence; uDWord dwBlockLength; - uDWord dwDpIndex; + uDWord dwDptIndex; } __packed; struct usb_ncm32_dp {