Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 28 Dec 2007 21:56:20 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 131914 for review
Message-ID:  <200712282156.lBSLuKKw005582@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=131914

Change 131914 by hselasky@hselasky_laptop001 on 2007/12/28 21:55:58

	
	This commit introduces multi sub-framing and zero copy
	to the USB CDC Ethernet driver in Host Mode and Device
	Mode. The implementation is backwards compatible with
	existing CDC implementations, only that you will not 
	get any acceleration on the downlink unless the USB
	CDC Ethernet adapters supports it by sending number
	of frames ahead messages.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/if_cdce.c#40 edit
.. //depot/projects/usb/src/sys/dev/usb/if_cdcereg.h#15 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/if_cdce.c#40 (text+ko) ====

@@ -113,14 +113,15 @@
 		.type = UE_BULK,
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_OUT,
-		.bufsize = (MCLBYTES + 4),
+		.frames = CDCE_ETH_FRAMES_MAX,
+		.bufsize = (MCLBYTES * CDCE_ETH_FRAMES_MAX),
 		.if_index = 0,
 		/* Host Mode */
-		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
 		.mh.callback = &cdce_bulk_write_callback,
 		.mh.timeout = 10000,	/* 10 seconds */
 		/* Device Mode */
-		.md.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.md.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1,.short_frames_ok = 1,},
 		.md.callback = &cdce_bulk_read_callback,
 		.md.timeout = 0,	/* no timeout */
 	},
@@ -129,14 +130,15 @@
 		.type = UE_BULK,
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_IN,
-		.bufsize = (MCLBYTES + 4),
+		.frames = CDCE_ETH_FRAMES_MAX,
+		.bufsize = (MCLBYTES * CDCE_ETH_FRAMES_MAX),
 		.if_index = 0,
 		/* Host Mode */
-		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1,.short_frames_ok = 1,},
 		.mh.callback = &cdce_bulk_read_callback,
 		.mh.timeout = 0,	/* no timeout */
 		/* Device Mode */
-		.md.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
 		.md.callback = &cdce_bulk_write_callback,
 		.md.timeout = 10000,	/* 10 seconds */
 	},
@@ -442,8 +444,8 @@
 	ifp->if_start = cdce_start_cb;
 	ifp->if_init = cdce_init_cb;
 	ifp->if_baudrate = 11000000;
-	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
-	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+	IFQ_SET_MAXLEN(&ifp->if_snd, CDCE_IFQ_MAXLEN);
+	ifp->if_snd.ifq_drv_maxlen = CDCE_IFQ_MAXLEN;
 	IFQ_SET_READY(&ifp->if_snd);
 
 	/* no IFM type for 11Mbps USB, so go with 10baseT */
@@ -456,6 +458,11 @@
 
 	/* start the interrupt transfer, if any */
 	mtx_lock(&(sc->sc_mtx));
+#ifdef CDCE_DO_BENCHMARK
+	usbd_transfer_start(sc->sc_xfer[0]);
+	usbd_transfer_start(sc->sc_xfer[1]);
+	device_printf(dev, "benchmarking enabled\n");
+#endif
 	usbd_transfer_start(sc->sc_xfer[4]);
 	mtx_unlock(&(sc->sc_mtx));
 
@@ -539,16 +546,43 @@
 }
 
 static void
+cdce_free_mbufs(struct mbuf **ppm)
+{
+	struct mbuf *m;
+	uint16_t x;
+
+	/* free all previous mbufs */
+	for (x = 0; x != CDCE_ETH_FRAMES_MAX; x++) {
+		m = ppm[x];
+		if (m) {
+			m_freem(m);
+			ppm[x] = NULL;
+		} else {
+			if (x != 0) {
+				break;
+			}
+		}
+	}
+	return;
+}
+
+static void
 cdce_bulk_write_callback(struct usbd_xfer *xfer)
 {
 	struct cdce_softc *sc = xfer->priv_sc;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct mbuf *m;
 	uint32_t crc;
+	uint32_t x;
+
+	/* free all previous mbufs */
+	cdce_free_mbufs(sc->sc_tx_mbufs);
 
 	switch (USBD_GET_STATE(xfer)) {
 	case USBD_ST_TRANSFERRED:
-		DPRINTF(sc, 10, "transfer complete\n");
+		DPRINTF(sc, 10, "transfer complete: "
+		    "%u bytes in %u frames\n", xfer->actlen, 
+		    xfer->aframes);
 
 		ifp->if_opackets++;
 
@@ -559,36 +593,100 @@
 			usbd_transfer_start(sc->sc_xfer[2]);
 			goto done;
 		}
+
+#ifdef CDCE_MF_ENABLE
+			x = 1;
+#else
+			x = 0;
+#endif
+		while (x != CDCE_ETH_FRAMES_MAX) {
+
 		IFQ_DRV_DEQUEUE(&(ifp->if_snd), m);
 
 		if (m == NULL) {
-			goto done;
+#ifdef CDCE_DO_BENCHMARK
+			/* send dummy ethernet frames */
+			usbd_set_frame_data(xfer, &(sc->sc_rx_dump), x);
+			xfer->frlengths[x] = (sizeof(sc->sc_rx_dump) / 2) + 
+			  (x & 63) - 1;
+			x++;
+			continue;
+#else
+			break;
+#endif
+		}
+
+		if (m->m_pkthdr.len < sizeof(struct ether_header)) {
+		  /* frames of this size have special meaning - filter away */
+		  m_freem(m);
+		  ifp->if_oerrors++;
+		  continue;
+		}
+
+		if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
+			/* Zaurus wants a 32-bit CRC appended to every frame */
+
+			crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
+			crc = htole32(crc);
+
+			if (!m_append(m, 4, (void *)&crc)) {
+			  m_freem(m);
+			  ifp->if_oerrors++;
+			  continue;
+			}
+
+			m->m_pkthdr.len += 4;
 		}
+
 		if (m->m_pkthdr.len > MCLBYTES) {
 			m->m_pkthdr.len = MCLBYTES;
 		}
-		xfer->frlengths[0] = m->m_pkthdr.len;
 
-		usbd_m_copy_in(xfer->frbuffers + 0, 0,
-		    m, 0, m->m_pkthdr.len);
+		m = m_pullup(m, m->m_pkthdr.len);
+		if (m == NULL) {
+		ifp->if_oerrors++;
+		continue;
+		}
 
-		if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
-			/* Zaurus wants a 32-bit CRC appended to every frame */
+		sc->sc_tx_mbufs[x] = m;
 
-			crc = htole32(cdce_m_crc32(m, 0, m->m_pkthdr.len));
+		xfer->frlengths[x] = m->m_len;
 
-			usbd_copy_in(xfer->frbuffers + 0,
-			    m->m_pkthdr.len, &crc, 4);
+		usbd_set_frame_data(xfer, m->m_data, x);
 
-			xfer->frlengths[0] += 4;
-		}
 		/*
 		 * if there's a BPF listener, bounce a copy
 		 * of this frame to him:
 		 */
 		BPF_MTAP(ifp, m);
 
-		m_freem(m);
+		x++;
+		}
+
+		xfer->nframes = x;
+
+#ifdef CDCE_MF_ENABLE
+		  if (x == 1) {
+		    /* nothing to do */
+		    goto done;
+		  }
+		  /* fill out the Multi Frame header */
+		  usbd_set_frame_data(xfer, &(sc->sc_tx_eth.hdr), 0);
+		  xfer->frlengths[0] = sizeof(sc->sc_tx_eth.hdr);
+		  sc->sc_tx_eth.hdr.bSig0[0] = 'M';
+		  sc->sc_tx_eth.hdr.bSig0[1] = 'F';
+		  x--;
+		  /* tell the peer how many frames are coming */
+		  x += ifp->if_snd.ifq_drv_len;
+		  USETDW(sc->sc_tx_eth.hdr.dwFramesAhead, x);
+		  x = ~x;
+		  USETDW(sc->sc_tx_eth.hdr.dwFramesAheadInverse, x);
+#else
+		  if (x == 0) {
+		    /* nothing to do */
+		    goto done;
+		  }
+#endif
 
 		usbd_start_hardware(xfer);
 
@@ -753,63 +851,172 @@
 static void
 cdce_bulk_read_callback(struct usbd_xfer *xfer)
 {
+	struct cdce_mq mq = { NULL, NULL, 0 };
 	struct cdce_softc *sc = xfer->priv_sc;
 	struct ifnet *ifp = sc->sc_ifp;
-	struct mbuf *m = NULL;
+	struct mbuf *m;
+	usb_cdc_mf_eth_header_t *mf_hdr;
+	uint32_t x;
+	uint32_t ta;
+	uint32_t tb;
 
 	switch (USBD_GET_STATE(xfer)) {
 	case USBD_ST_TRANSFERRED:
 
-		DPRINTF(sc, 0, "received %u bytes\n", xfer->actlen);
+		DPRINTF(sc, 0, "received %u bytes in %u frames\n",
+			xfer->actlen, xfer->aframes);
+
+		for (x = 0; x != xfer->nframes; x++) {
 
 		if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
 
 			/* Strip off CRC added by Zaurus */
-			if (xfer->actlen >= 4) {
-				xfer->actlen -= 4;
+			if (xfer->frlengths[x] >= MAX(4,14)) {
+				xfer->frlengths[x] -= 4;
 			}
 		}
-		if (xfer->actlen < sizeof(struct ether_header)) {
-			ifp->if_ierrors++;
-			goto tr_setup;
-		}
-		m = usbd_ether_get_mbuf();
 
+		m = sc->sc_rx_mbufs[x];
+		sc->sc_rx_mbufs[x] = NULL;
 		if (m == NULL) {
-			ifp->if_ierrors++;
-			goto tr_setup;
+			continue;
 		}
-		xfer->actlen = min(xfer->actlen, m->m_len);
+
+		if (xfer->frlengths[x] < sizeof(struct ether_header)) {
+
+#ifdef CDCE_MF_ENABLE
+			if (xfer->frlengths[x] >= CDC_MF_ETH_HEADER_SIZE) {
+
+			mf_hdr = (void *)(m->m_data);
+
+			/* decode and verify multi frame header */
+
+			ta = UGETDW(mf_hdr->dwFramesAhead);
+			tb = UGETDW(mf_hdr->dwFramesAheadInverse);
+			tb ^= ta;
+
+		    DPRINTF(sc, 0, "ta = 0x%08x, tb = 0x%08x\n", ta, tb);
+
+			/* check if we have a multi frame header */
+			if ((tb == 0xFFFFFFFF) &&
+			    (mf_hdr->bSig0[0] == 'M') &&
+			    (mf_hdr->bSig0[1] == 'F')) {
+
+			    DPRINTF(sc, 0, "frames ahead "
+				    "= %u\n", ta);
+
+			    sc->sc_rx_frames_ahead = ta;
+			}
+			}
+#endif
 
-		usbd_copy_out(xfer->frbuffers + 0, 0, m->m_data, xfer->actlen);
+			m_freem(m);
+			continue;
+		} else {
+		  /* we received a frame - decrement frames ahead */
+		  if (sc->sc_rx_frames_ahead) {
+		    sc->sc_rx_frames_ahead--;
+		  }
+		}
 
 		ifp->if_ipackets++;
 		m->m_pkthdr.rcvif = ifp;
-		m->m_pkthdr.len = m->m_len = xfer->actlen;
+		m->m_pkthdr.len = m->m_len = xfer->frlengths[x];
+
+		/* enqueue */
+		_IF_ENQUEUE(&(mq), m);
+		}
 
 	case USBD_ST_SETUP:
 tr_setup:
-		if (xfer->flags.stall_pipe &&
-		    (xfer->flags_int.usb_mode == USB_MODE_HOST)) {
+		if (xfer->flags.stall_pipe) {
+
+			/* reset number of frames ahead */
+			sc->sc_rx_frames_ahead = 0;
+
+		  if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
 			usbd_transfer_start(sc->sc_xfer[3]);
-		} else {
-			xfer->frlengths[0] = xfer->max_data_length;
+			/* 
+			 * In case the "stall_pipe" flag was set
+			 * while transferring data, we need to go
+			 * to the "tr_if_input" afterwards!
+			 */
+			goto tr_if_input;
+		  }
+		} 
+
+		/* setup a new USB transfer chain */
+
+		  ta = sc->sc_rx_frames_ahead;
+
+		  if (ta == 0) {
+		    /* always receive at least one frame */
+		    ta = 1;
+		  }
+
+		  if (ta > CDCE_ETH_FRAMES_MAX) {
+		      ta = CDCE_ETH_FRAMES_MAX;
+		  }
+
+		  for (x = 0; x != ta; x++) {
+			m = usbd_ether_get_mbuf();
+			if (m == NULL) {
+			  break;
+			}
+
+			usbd_set_frame_data(xfer, m->m_data, x);
+
+			xfer->frlengths[x] = m->m_len;
+			sc->sc_rx_mbufs[x] = m;
+		  }
+
+		  for ( ; x != ta; x++) {
+
+			/* 
+			 * We are out of mbufs and need to dump all
+			 * the received data !
+			 */
+			usbd_set_frame_data(xfer, &(sc->sc_rx_dump), x);
+			xfer->frlengths[x] = sizeof(sc->sc_rx_dump);
+		  }
+			xfer->nframes = ta;
 			usbd_start_hardware(xfer);
-		}
 
 		/*
 		 * At the end of a USB callback it is always safe to unlock
 		 * the private mutex of a device! That is why we do the
 		 * "if_input" here, and not some lines up!
+		 *
+		 * By safe we mean that if "usbd_transfer_stop()" is
+		 * called, we will get a callback having the error
+		 * code USBD_CANCELLED.
 		 */
-		if (m) {
+tr_if_input:
+		if (mq.ifq_head) {
+
 			mtx_unlock(&(sc->sc_mtx));
-			(ifp->if_input) (ifp, m);
+
+			while (1) {
+
+				_IF_DEQUEUE(&(mq), m);
+
+				if (m == NULL)
+					break;
+
+				(ifp->if_input) (ifp, m);
+			}
+
 			mtx_lock(&(sc->sc_mtx));
 		}
 		return;
 
 	default:			/* Error */
+		/* free all received data, if any */
+		cdce_free_mbufs(sc->sc_rx_mbufs);
+
+		/* reset number of frames ahead */
+		sc->sc_rx_frames_ahead = 0;
+
 		if (xfer->error != USBD_CANCELLED) {
 			/* try to clear stall first */
 			xfer->flags.stall_pipe = 1;

==== //depot/projects/usb/src/sys/dev/usb/if_cdcereg.h#15 (text+ko) ====

@@ -35,26 +35,48 @@
 #ifndef _USB_IF_CDCEREG_H_
 #define	_USB_IF_CDCEREG_H_
 
-#define	CDCE_N_TRANSFER	6
+#define	CDCE_N_TRANSFER	6		/* units */
 #define	CDCE_IND_SIZE_MAX 32		/* bytes */
+#define	CDCE_ETH_FRAMES_MAX 64		/* USB ethernet acceleration factor */
+#define	CDCE_IFQ_MAXLEN MAX((2*CDCE_ETH_FRAMES_MAX), IFQ_MAXLEN)
+#define	CDCE_MF_ENABLE			/* enable multi framing */
 
 struct cdce_type {
 	struct usb_devno cdce_dev;
 	uint16_t cdce_flags;
 };
 
+struct cdce_eth_hdr {			/* multiframe header */
+	usb_cdc_mf_eth_header_t hdr;
+} __aligned(USB_HOST_ALIGN);
+
+struct cdce_eth_dump {			/* dump buffer */
+	uint8_t	data[MCLBYTES];
+} __aligned(USB_HOST_ALIGN);
+
+struct cdce_mq {			/* mini-queue */
+	struct mbuf *ifq_head;
+	struct mbuf *ifq_tail;
+	uint16_t ifq_len;
+};
+
 struct cdce_softc {
 	void   *sc_evilhack;		/* XXX this pointer must be first */
 
 	struct ifmedia sc_ifmedia;
 	struct mtx sc_mtx;
+	struct cdce_eth_hdr sc_tx_eth;
+	struct cdce_eth_dump sc_rx_dump;
 
 	struct ifnet *sc_ifp;
 	struct usbd_xfer *sc_xfer[CDCE_N_TRANSFER];
 	struct usbd_device *sc_udev;
 	device_t sc_dev;
+	struct mbuf *sc_rx_mbufs[CDCE_ETH_FRAMES_MAX];
+	struct mbuf *sc_tx_mbufs[CDCE_ETH_FRAMES_MAX];
 
 	uint32_t sc_unit;
+	uint32_t sc_rx_frames_ahead;
 
 	uint16_t sc_flags;
 #define	CDCE_FLAG_ZAURUS	0x0001



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