summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-09-28 07:53:55 +0000
committerthompsa <thompsa@FreeBSD.org>2009-09-28 07:53:55 +0000
commitfe6300971a702031259ab5ba25bbe3a7424cf78f (patch)
tree28cb94845c971c3365137621c5761d003857e3ab /sys/dev
parentf23c472dc2b4fb09b14c469c1a5ae414e74a1285 (diff)
downloadFreeBSD-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.c590
-rw-r--r--sys/dev/usb/net/if_cdcereg.h28
-rw-r--r--sys/dev/usb/usb.h5
-rw-r--r--sys/dev/usb/usb_cdc.h103
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_ */
OpenPOWER on IntegriCloud