Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Oct 2009 23:19:41 +0000 (UTC)
From:      Andrew Thompson <thompsa@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r198648 - in stable/8/sys: . amd64/include/xen cddl/contrib/opensolaris contrib/dev/acpica contrib/pf dev/usb dev/usb/net dev/xen/xenpci
Message-ID:  <200910292319.n9TNJftq014755@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: thompsa
Date: Thu Oct 29 23:19:41 2009
New Revision: 198648
URL: http://svn.freebsd.org/changeset/base/198648

Log:
  MFC r197563
  
   Add basic support for USB Network Control Model (NCM) v1.0 to if_cdce.c.

Modified:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/usb/net/if_cdce.c
  stable/8/sys/dev/usb/net/if_cdcereg.h
  stable/8/sys/dev/usb/usb.h
  stable/8/sys/dev/usb/usb_cdc.h
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/usb/net/if_cdce.c
==============================================================================
--- stable/8/sys/dev/usb/net/if_cdce.c	Thu Oct 29 23:18:59 2009	(r198647)
+++ stable/8/sys/dev/usb/net/if_cdce.c	Thu Oct 29 23:19:41 2009	(r198648)
@@ -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_cal
 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_conf
 	},
 };
 
+#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_d
 
 	{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

Modified: stable/8/sys/dev/usb/net/if_cdcereg.h
==============================================================================
--- stable/8/sys/dev/usb/net/if_cdcereg.h	Thu Oct 29 23:18:59 2009	(r198647)
+++ stable/8/sys/dev/usb/net/if_cdcereg.h	Thu Oct 29 23:19:41 2009	(r198648)
@@ -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];
 };
 

Modified: stable/8/sys/dev/usb/usb.h
==============================================================================
--- stable/8/sys/dev/usb/usb.h	Thu Oct 29 23:18:59 2009	(r198647)
+++ stable/8/sys/dev/usb/usb.h	Thu Oct 29 23:19:41 2009	(r198648)
@@ -424,9 +424,9 @@ typedef struct usb_interface_assoc_descr
 #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_descr
 #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_descr
 #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

Modified: stable/8/sys/dev/usb/usb_cdc.h
==============================================================================
--- stable/8/sys/dev/usb/usb_cdc.h	Thu Oct 29 23:18:59 2009	(r198647)
+++ stable/8/sys/dev/usb/usb_cdc.h	Thu Oct 29 23:19:41 2009	(r198648)
@@ -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_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200910292319.n9TNJftq014755>