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:
parent
a872528155
commit
4076dd2309
@ -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
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user