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
This commit is contained in:
Andrew Thompson 2009-09-28 07:53:55 +00:00
parent a872528155
commit 4076dd2309
4 changed files with 698 additions and 30 deletions

View File

@ -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:
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);
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) {
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)
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

View File

@ -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];
};

View File

@ -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

View File

@ -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_ */