USB network (NCM driver):

- correct the ethernet payload remainder which
must be post-offseted by -14 bytes instead of
0 bytes. This is not very clearly defined in the
NCM specification.
- add development feature about limiting the
maximum datagram count in each NCM payload.
- zero-pad alignment data
- add TX-interval tuning sysctl

Approved by:    thompsa (mentor)
This commit is contained in:
Hans Petter Selasky 2010-10-13 22:04:55 +00:00
parent d4f5240abd
commit 7e05daae8f
3 changed files with 127 additions and 26 deletions

View File

@ -110,10 +110,13 @@ static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
#ifdef USB_DEBUG
static int cdce_debug = 0;
static int cdce_tx_interval = 0;
SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0,
"Debug level");
SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RW, &cdce_tx_interval, 0,
"NCM transmit interval in ms");
#endif
static const struct usb_config cdce_config[CDCE_N_TRANSFER] = {
@ -192,7 +195,7 @@ static const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = {
.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,},
.flags = {.pipe_bof = 1,},
.callback = cdce_ncm_bulk_write_callback,
.timeout = 10000, /* 10 seconds */
.usb_mode = USB_MODE_DUAL, /* both modes */
@ -294,9 +297,22 @@ cdce_ncm_init(struct cdce_softc *sc)
{
struct usb_ncm_parameters temp;
struct usb_device_request req;
uDWord value;
struct usb_ncm_func_descriptor *ufd;
uint8_t value[8];
int err;
ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL,
sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0 - 1,
UCDC_NCM_FUNC_DESC_SUBTYPE, 0 - 1);
/* verify length of NCM functional descriptor */
if (ufd != NULL) {
if (ufd->bLength < sizeof(*ufd))
ufd = NULL;
else
DPRINTFN(1, "Found NCM functional descriptor.\n");
}
req.bmRequestType = UT_READ_CLASS_INTERFACE;
req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS;
USETW(req.wValue, 0);
@ -317,17 +333,19 @@ cdce_ncm_init(struct cdce_softc *sc)
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);
sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
} else {
sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize);
sc->sc_ncm.tx_max = UGETDW(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);
sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
}
/* Verify maximum receive length */
if (err || (sc->sc_ncm.rx_max < 32) ||
if ((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;
@ -335,7 +353,7 @@ cdce_ncm_init(struct cdce_softc *sc)
/* Verify maximum transmit length */
if (err || (sc->sc_ncm.tx_max < 32) ||
if ((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;
@ -347,7 +365,7 @@ cdce_ncm_init(struct cdce_softc *sc)
* - not greater than the maximum transmit length
* - not less than four bytes
*/
if (err || (sc->sc_ncm.tx_struct_align < 4) ||
if ((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)) {
@ -361,7 +379,7 @@ cdce_ncm_init(struct cdce_softc *sc)
* - not greater than the maximum transmit length
* - not less than four bytes
*/
if (err || (sc->sc_ncm.tx_modulus < 4) ||
if ((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)) {
@ -371,11 +389,30 @@ cdce_ncm_init(struct cdce_softc *sc)
/* Verify that the payload remainder */
if (err || (sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) {
if ((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;
}
/*
* Offset the TX remainder so that IP packet payload starts at
* the tx_modulus. This is not too clear in the specification.
*/
sc->sc_ncm.tx_remainder =
(sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) &
(sc->sc_ncm.tx_modulus - 1);
/* Verify max datagrams */
if (sc->sc_ncm.tx_nframe == 0 ||
sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) {
DPRINTFN(1, "Using default max "
"subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1);
/* need to reserve one entry for zero padding */
sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1);
}
/* Additional configuration, will fail in device side mode, which is OK. */
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
@ -383,8 +420,17 @@ cdce_ncm_init(struct cdce_softc *sc)
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);
if (ufd != NULL &&
(ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) {
USETW(req.wLength, 8);
USETDW(value, sc->sc_ncm.rx_max);
USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1));
USETW(value + 6, 0);
} else {
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 */);
@ -567,7 +613,7 @@ cdce_attach(device_t dev)
} else {
bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr));
memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr));
for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {
@ -983,6 +1029,18 @@ cdce_handle_request(device_t dev,
}
#if CDCE_HAVE_NCM
static void
cdce_ncm_tx_zero(struct usb_page_cache *pc,
uint32_t start, uint32_t end)
{
if (start >= CDCE_NCM_TX_MAXLEN)
return;
if (end > CDCE_NCM_TX_MAXLEN)
end = CDCE_NCM_TX_MAXLEN;
usbd_frame_zero(pc, start, end - start);
}
static uint8_t
cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
{
@ -993,7 +1051,8 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
uint32_t rem;
uint32_t offset;
uint32_t last_offset;
uint32_t n;
uint16_t n;
uint8_t retval;
usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index);
@ -1003,11 +1062,17 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
/* 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));
/* Align offset */
offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
offset, sc->sc_ncm.tx_modulus);
for (n = 0; n != CDCE_NCM_SUBFRAMES_MAX; n++) {
/* Zero pad */
cdce_ncm_tx_zero(pc, last_offset, offset);
/* buffer full */
retval = 2;
for (n = 0; n != sc->sc_ncm.tx_nframe; n++) {
/* check if end of transmit buffer is reached */
@ -1020,8 +1085,11 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
IFQ_DRV_DEQUEUE(&(ifp->if_snd), m);
if (m == NULL)
if (m == NULL) {
/* buffer not full */
retval = 1;
break;
}
if (m->m_pkthdr.len > rem) {
if (n == 0) {
@ -1047,9 +1115,12 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
/* 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));
/* Align offset */
offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
offset, sc->sc_ncm.tx_modulus);
/* Zero pad */
cdce_ncm_tx_zero(pc, last_offset, offset);
/*
* If there's a BPF listener, bounce a copy
@ -1067,7 +1138,7 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
}
if (n == 0)
return (1);
return (0);
rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4);
@ -1079,8 +1150,22 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
USETW(sc->sc_ncm.dp[n].wFrameIndex, 0);
}
offset = last_offset;
/* Align offset */
offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN);
/* Optimise, save bandwidth and force short termination */
if (offset >= sc->sc_ncm.tx_max)
offset = sc->sc_ncm.tx_max;
else
offset ++;
/* Zero pad */
cdce_ncm_tx_zero(pc, last_offset, offset);
/* set frame length */
usbd_xfer_set_frame_len(xfer, index, last_offset);
usbd_xfer_set_frame_len(xfer, index, offset);
/* Fill out 16-bit header */
sc->sc_ncm.hdr.dwSignature[0] = 'N';
@ -1088,7 +1173,7 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
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, last_offset);
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));
@ -1106,7 +1191,7 @@ cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
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);
return (retval);
}
static void
@ -1115,6 +1200,7 @@ 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;
uint8_t temp;
int actlen;
int aframes;
@ -1128,11 +1214,19 @@ cdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) {
if (cdce_ncm_fill_tx_frames(xfer, x))
temp = cdce_ncm_fill_tx_frames(xfer, x);
if (temp == 0)
break;
if (temp == 1) {
x++;
break;
}
}
if (x != 0) {
#ifdef USB_DEBUG
usbd_xfer_set_interval(xfer, cdce_tx_interval);
#endif
usbd_xfer_set_frames(xfer, x);
usbd_transfer_submit(xfer);
}

View File

@ -38,7 +38,8 @@
#define CDCE_FRAMES_MAX 8 /* units */
#define CDCE_IND_SIZE_MAX 32 /* bytes */
#define CDCE_NCM_TX_MAXLEN 2048UL /* bytes */
#define CDCE_NCM_TX_MINLEN 512 /* bytes, must be power of two */
#define CDCE_NCM_TX_MAXLEN (1UL << 14) /* bytes */
#define CDCE_NCM_TX_FRAMES_MAX 8 /* units */
#define CDCE_NCM_RX_MAXLEN (1UL << 14) /* bytes */
@ -46,6 +47,10 @@
#define CDCE_NCM_SUBFRAMES_MAX 32 /* units */
#define CDCE_NCM_ALIGN(rem,off,mod) \
((uint32_t)(((uint32_t)(rem)) - \
((uint32_t)((-(uint32_t)(off)) & (-(uint32_t)(mod))))))
#ifndef CDCE_HAVE_NCM
#define CDCE_HAVE_NCM 1
#endif
@ -68,6 +73,7 @@ struct cdce_ncm {
uint16_t tx_modulus;
uint16_t tx_struct_align;
uint16_t tx_seq;
uint16_t tx_nframe;
};
struct cdce_softc {

View File

@ -241,6 +241,7 @@ struct usb_ncm_func_descriptor {
#define UCDC_NCM_CAP_ENCAP 0x04
#define UCDC_NCM_CAP_MAX_DATA 0x08
#define UCDC_NCM_CAP_CRCMODE 0x10
#define UCDC_NCM_CAP_MAX_DGRAM 0x20
} __packed;
/* Communications interface specific class request codes */
@ -276,7 +277,7 @@ struct usb_ncm_parameters {
uWord wNdpOutDivisor;
uWord wNdpOutPayloadRemainder;
uWord wNdpOutAlignment;
uWord wReserved26;
uWord wNtbOutMaxDatagrams;
} __packed;
/* Communications interface specific class notification codes */