/* * cdc_ncm.c * * Copyright (C) ST-Ericsson 2010-2012 * Contact: Alexey Orishko <alexey.orishko@stericsson.com> * Original author: Hans Petter Selasky <hans.petter.selasky@stericsson.com> * * USB Host Driver for Network Control Model (NCM) * http://www.usb.org/developers/docs/devclass_docs/NCM10_012011.zip * * The NCM encoding, decoding and initialization logic * derives from FreeBSD 8.x. if_cdce.c and if_cdcereg.h * * This software is available to you under a choice of one of two * licenses. You may choose this file to be licensed under the terms * of the GNU General Public License (GPL) Version 2 or the 2-clause * BSD license listed below: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE.
*/
switch (stringset) { case ETH_SS_STATS: for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
}
}
/* clamp new_rx to sane values */
min = USB_CDC_NCM_NTB_MIN_IN_SIZE;
max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
/* dwNtbInMaxSize spec violation? Use MIN size for both limits */ if (max < min) {
dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n",
le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min);
max = min;
}
val = clamp_t(u32, new_rx, min, max); if (val != new_rx)
dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max);
/* clamp new_tx to sane values */ if (ctx->is_ndp16)
min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); else
min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32);
if (le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) == 0)
max = CDC_NCM_NTB_MAX_SIZE_TX; /* dwNtbOutMaxSize not set */ else
max = clamp_t(u32, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize),
USB_CDC_NCM_NTB_MIN_OUT_SIZE,
CDC_NCM_NTB_MAX_SIZE_TX);
/* some devices set dwNtbOutMaxSize too low for the above default */
min = min(min, max);
val = clamp_t(u32, new_tx, min, max); if (val != new_tx)
dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max);
/* tell device to use new size */ if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
USB_TYPE_CLASS | USB_DIR_OUT
| USB_RECIP_INTERFACE,
0, iface_no, &dwNtbInMaxSize, 4) < 0)
dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); else
ctx->rx_max = val;
}
/* usbnet use these values for sizing rx queues */ if (dev->rx_urb_size != ctx->rx_max) {
dev->rx_urb_size = ctx->rx_max; if (netif_running(dev->net))
usbnet_unlink_rx_urbs(dev);
}
val = cdc_ncm_check_tx_max(dev, new_tx); if (val != ctx->tx_max)
dev_info(&dev->intf->dev, "setting tx_max = %u\n", val);
/* Adding a pad byte here if necessary simplifies the handling * in cdc_ncm_fill_tx_frame, making tx_max always represent * the real skb max size. * * We cannot use dev->maxpacket here because this is called from * .bind which is called before usbnet sets up dev->maxpacket
*/ if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
val % usb_maxpacket(dev->udev, dev->out) == 0)
val++;
/* we might need to flush any pending tx buffers if running */ if (netif_running(dev->net) && val > ctx->tx_max) {
netif_tx_lock_bh(dev->net);
usbnet_start_xmit(NULL, dev->net); /* make sure tx_curr_skb is reallocated if it was empty */ if (ctx->tx_curr_skb) {
dev_kfree_skb_any(ctx->tx_curr_skb);
ctx->tx_curr_skb = NULL;
}
ctx->tx_max = val;
netif_tx_unlock_bh(dev->net);
} else {
ctx->tx_max = val;
}
dev->hard_mtu = ctx->tx_max;
/* max qlen depend on hard_mtu and rx_urb_size */
usbnet_update_max_qlen(dev);
/* never pad more than 3 full USB packets per transfer */
ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out),
CDC_NCM_MIN_TX_PKT, ctx->tx_max);
}
if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc) return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); if (ctx->ether_desc) return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); return CDC_NCM_MAX_DATAGRAM_SIZE;
}
/* initial one-time device setup. MUST be called with the data interface * in altsetting 0
*/ staticint cdc_ncm_init(struct usbnet *dev)
{ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; int err;
out: /* set MTU to max supported by the device if necessary */
dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev));
/* do not exceed operator preferred MTU */ if (ctx->mbim_extended_desc) {
mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU); if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
dev->net->mtu = mbim_mtu;
}
}
/* * verify that the structure alignment is: * - power of two * - not greater than the maximum transmit length * - not less than four bytes
*/
val = ctx->tx_ndp_modulus;
/* * verify that the payload alignment is: * - power of two * - not greater than the maximum transmit length * - not less than four bytes
*/
val = ctx->tx_modulus;
/* be conservative when selecting initial buffer size to * increase the number of hosts this will work for
*/
def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX,
le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX,
le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
/* clamp rx_max and tx_max and inform device */
cdc_ncm_update_rxtx_max(dev, def_rx, def_tx);
/* sanitize the modulus and remainder values */
cdc_ncm_fix_modulus(dev);
/* set max datagram size */
cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev)); return 0;
}
for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) {
e = intf->cur_altsetting->endpoint + ep;
/* ignore endpoints which cannot transfer data */ if (!usb_endpoint_maxp(&e->desc)) continue;
switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_INT: if (usb_endpoint_dir_in(&e->desc)) { if (!dev->status)
dev->status = e;
} break;
case USB_ENDPOINT_XFER_BULK: if (usb_endpoint_dir_in(&e->desc)) { if (!in)
in = e;
} else { if (!out)
out = e;
} break;
default: break;
}
} if (in && !dev->in)
dev->in = usb_rcvbulkpipe(dev->udev,
in->desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK); if (out && !dev->out)
dev->out = usb_sndbulkpipe(dev->udev,
out->desc.bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK);
}
staticvoid cdc_ncm_free(struct cdc_ncm_ctx *ctx)
{ if (ctx == NULL) return;
if (ctx->tx_rem_skb != NULL) {
dev_kfree_skb_any(ctx->tx_rem_skb);
ctx->tx_rem_skb = NULL;
}
if (ctx->tx_curr_skb != NULL) {
dev_kfree_skb_any(ctx->tx_curr_skb);
ctx->tx_curr_skb = NULL;
}
if (ctx->is_ndp16)
kfree(ctx->delayed_ndp16); else
kfree(ctx->delayed_ndp32);
kfree(ctx);
}
/* we need to override the usbnet change_mtu ndo for two reasons: * - respect the negotiated maximum datagram size * - avoid unwanted changes to rx and tx buffers
*/ int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
{ struct usbnet *dev = netdev_priv(net);
/* some buggy devices have an IAD but no CDC Union */ if (!hdr.usb_cdc_union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
}
/* check if we got everything */ if (!ctx->data) {
dev_err(&intf->dev, "CDC Union missing and no IAD found\n"); goto error;
} if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) { if (!ctx->mbim_desc) {
dev_err(&intf->dev, "MBIM functional descriptor missing\n"); goto error;
}
} else { if (!ctx->ether_desc || !ctx->func_desc) {
dev_err(&intf->dev, "NCM or ECM functional descriptors missing\n"); goto error;
}
}
/* claim data interface, if different from control */ if (ctx->data != ctx->control) {
temp = usb_driver_claim_interface(driver, ctx->data, dev); if (temp) {
dev_err(&intf->dev, "failed to claim data intf\n"); goto error;
}
}
if (ctx->func_desc)
ctx->filtering_supported = !!(ctx->func_desc->bmNetworkCapabilities
& USB_CDC_NCM_NCAP_ETH_FILTER);
/* Reset data interface. Some devices will not reset properly * unless they are configured first. Toggle the altsetting to * force a reset. * Some other devices do not work properly with this procedure * that can be avoided using quirk CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE
*/ if (!(ctx->drvflags & CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE))
usb_set_interface(dev->udev, iface_no, data_altsetting);
/* initialize basic device settings */ if (cdc_ncm_init(dev)) goto error2;
/* Some firmwares need a pause here or they will silently fail * to set up the interface properly. This value was decided * empirically on a Sierra Wireless MC7455 running 02.08.02.00 * firmware.
*/
usleep_range(10000, 20000);
/* configure data interface */
temp = usb_set_interface(dev->udev, iface_no, data_altsetting); if (temp) {
dev_dbg(&intf->dev, "set interface failed\n"); goto error2;
}
if (ctx->ether_desc) {
temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); if (temp) {
dev_err(&intf->dev, "failed to get mac address\n"); goto error2;
}
dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
}
/* finish setting up the device specific data */
cdc_ncm_setup(dev);
/* Allocate the delayed NDP if needed. */ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { if (ctx->is_ndp16) {
ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); if (!ctx->delayed_ndp16) goto error2;
} else {
ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); if (!ctx->delayed_ndp32) goto error2;
}
dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
}
/* Return the number of the MBIM control interface altsetting iff it * is preferred and available,
*/
u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
{ struct usb_host_interface *alt;
/* The MBIM spec defines a NCM compatible default altsetting, * which we may have matched: * * "Functions that implement both NCM 1.0 and MBIM (an * “NCM/MBIM function”) according to this recommendation * shall provide two alternate settings for the * Communication Interface. Alternate setting 0, and the * associated class and endpoint descriptors, shall be * constructed according to the rules given for the * Communication Interface in section 5 of [USBNCM10]. * Alternate setting 1, and the associated class and * endpoint descriptors, shall be constructed according to * the rules given in section 6 (USB Device Model) of this * specification."
*/ if (intf->num_altsetting < 2) return intf->cur_altsetting->desc.bAlternateSetting;
if (prefer_mbim) {
alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); if (alt && cdc_ncm_comm_intf_is_mbim(alt)) return CDC_NCM_COMM_ALTSETTING_MBIM;
} return CDC_NCM_COMM_ALTSETTING_NCM;
}
EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
/* The NCM data altsetting is fixed, so we hard-coded it. * Additionally, generic NCM devices are assumed to accept arbitrarily * placed NDP.
*/ return cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
}
if (skb->len + align > max)
align = max - skb->len; if (align && skb_tailroom(skb) >= align)
skb_put_zero(skb, align);
}
/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly * allocating a new one within skb
*/ staticstruct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
{ struct usb_cdc_ncm_ndp16 *ndp16 = NULL; struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
/* If NDP should be moved to the end of the NCM package, we can't follow the * NTH16 header as we would normally do. NDP isn't written to the SKB yet, and * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
*/ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { if (ctx->delayed_ndp16->dwSignature == sign) return ctx->delayed_ndp16;
/* We can only push a single NDP to the end. Return * NULL to send what we've already got and queue this * skb for later.
*/ elseif (ctx->delayed_ndp16->dwSignature) return NULL;
}
/* follow the chain of NDPs, looking for a match */ while (ndpoffset) {
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); if (ndp16->dwSignature == sign) return ndp16;
ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
}
/* align new NDP */ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
/* verify that there is room for the NDP and the datagram (reserve) */ if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size) return NULL;
/* link to it */ if (ndp16)
ndp16->wNextNdpIndex = cpu_to_le16(skb->len); else
nth16->wNdpIndex = cpu_to_le16(skb->len);
/* push a new empty NDP */ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
ndp16 = skb_put_zero(skb, ctx->max_ndp_size); else
ndp16 = ctx->delayed_ndp16;
/* If NDP should be moved to the end of the NCM package, we can't follow the * NTH32 header as we would normally do. NDP isn't written to the SKB yet, and * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
*/ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { if (ctx->delayed_ndp32->dwSignature == sign) return ctx->delayed_ndp32;
/* We can only push a single NDP to the end. Return * NULL to send what we've already got and queue this * skb for later.
*/ elseif (ctx->delayed_ndp32->dwSignature) return NULL;
}
/* follow the chain of NDPs, looking for a match */ while (ndpoffset) {
ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb->data + ndpoffset); if (ndp32->dwSignature == sign) return ndp32;
ndpoffset = le32_to_cpu(ndp32->dwNextNdpIndex);
}
/* align new NDP */ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size);
/* verify that there is room for the NDP and the datagram (reserve) */ if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size) return NULL;
/* link to it */ if (ndp32)
ndp32->dwNextNdpIndex = cpu_to_le32(skb->len); else
nth32->dwNdpIndex = cpu_to_le32(skb->len);
/* push a new empty NDP */ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
ndp32 = skb_put_zero(skb, ctx->max_ndp_size); else
ndp32 = ctx->delayed_ndp32;
/* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated * accordingly. Otherwise, we should check here.
*/ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
delayed_ndp_size = ctx->max_ndp_size +
max_t(u32,
ctx->tx_ndp_modulus,
ctx->tx_modulus + ctx->tx_remainder) - 1; else
delayed_ndp_size = 0;
/* if there is a remaining skb, it gets priority */ if (skb != NULL) {
swap(skb, ctx->tx_rem_skb);
swap(sign, ctx->tx_rem_sign);
} else {
ready2send = 1;
}
/* check if we are resuming an OUT skb */
skb_out = ctx->tx_curr_skb;
/* allocate a new OUT skb */ if (!skb_out) { if (ctx->tx_low_mem_val == 0) {
ctx->tx_curr_size = ctx->tx_max;
skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); /* If the memory allocation fails we will wait longer * each time before attempting another full size * allocation again to not overload the system * further.
*/ if (skb_out == NULL) { /* If even the smallest allocation fails, abort. */ if (ctx->tx_curr_size == USB_CDC_NCM_NTB_MIN_OUT_SIZE) goto alloc_failed;
ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1,
(unsigned)CDC_NCM_LOW_MEM_MAX_CNT);
ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt;
}
} if (skb_out == NULL) { /* See if a very small allocation is possible. * We will send this packet immediately and hope * that there is more memory available later.
*/ if (skb)
ctx->tx_curr_size = max(skb->len,
(u32)USB_CDC_NCM_NTB_MIN_OUT_SIZE); else
ctx->tx_curr_size = USB_CDC_NCM_NTB_MIN_OUT_SIZE;
skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC);
/* No allocation possible so we will abort */ if (!skb_out) goto alloc_failed;
ctx->tx_low_mem_val--;
} if (ctx->is_ndp16) { /* fill out the initial 16-bit NTB header */
nth.nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16));
nth.nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
nth.nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
nth.nth16->wSequence = cpu_to_le16(ctx->tx_seq++);
} else { /* fill out the initial 32-bit NTB header */
nth.nth32 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth32));
nth.nth32->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH32_SIGN);
nth.nth32->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth32));
nth.nth32->wSequence = cpu_to_le16(ctx->tx_seq++);
}
/* count total number of frames in this NTB */
ctx->tx_curr_frame_num = 0;
/* recent payload counter for this skb_out */
ctx->tx_curr_frame_payload = 0;
}
for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { /* send any remaining skb first */ if (skb == NULL) {
skb = ctx->tx_rem_skb;
sign = ctx->tx_rem_sign;
ctx->tx_rem_skb = NULL;
/* check for end of skb */ if (skb == NULL) break;
}
/* get the appropriate NDP for this skb */ if (ctx->is_ndp16)
ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); else
ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);
/* align beginning of next frame */
cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size);
/* check if we had enough room left for both NDP and frame */ if ((ctx->is_ndp16 && !ndp.ndp16) || (!ctx->is_ndp16 && !ndp.ndp32) ||
skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { if (n == 0) { /* won't fit, MTU problem? */
dev_kfree_skb_any(skb);
skb = NULL;
dev->net->stats.tx_dropped++;
} else { /* no room for skb - store for later */ if (ctx->tx_rem_skb != NULL) {
dev_kfree_skb_any(ctx->tx_rem_skb);
dev->net->stats.tx_dropped++;
}
ctx->tx_rem_skb = skb;
ctx->tx_rem_sign = sign;
skb = NULL;
ready2send = 1;
ctx->tx_reason_ntb_full++; /* count reason for transmitting */
} break;
}
/* calculate frame number within this NDP */ if (ctx->is_ndp16) {
ndplen = le16_to_cpu(ndp.ndp16->wLength);
index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;
/* send now if this NDP is full */ if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
ready2send = 1;
ctx->tx_reason_ndp_full++; /* count reason for transmitting */ break;
}
}
/* free up any dangling skb */ if (skb != NULL) {
dev_kfree_skb_any(skb);
skb = NULL;
dev->net->stats.tx_dropped++;
}
ctx->tx_curr_frame_num = n;
if (n == 0) { /* wait for more frames */ /* push variables */
ctx->tx_curr_skb = skb_out; goto exit_no_skb;
} elseif ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) { /* wait for more frames */ /* push variables */
ctx->tx_curr_skb = skb_out; /* set the pending count */ if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT; goto exit_no_skb;
} else { if (n == ctx->tx_max_datagrams)
ctx->tx_reason_max_datagram++; /* count reason for transmitting */ /* frame goes out */ /* variables will be reset at next call */
}
/* If requested, put NDP at end of frame. */ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { if (ctx->is_ndp16) {
nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size);
nth.nth16->wNdpIndex = cpu_to_le16(skb_out->len);
skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size);
/* If collected data size is less or equal ctx->min_tx_pkt * bytes, we send buffers as it is. If we get more data, it * would be more efficient for USB HS mobile device with DMA * engine to receive a full size NTB, than canceling DMA * transfer and receiving a short packet. * * This optimization support is pointless if we end up sending * a ZLP after full sized NTBs.
*/ if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
skb_out->len > ctx->min_tx_pkt) {
padding_count = ctx->tx_curr_size - skb_out->len; if (!WARN_ON(padding_count > ctx->tx_curr_size))
skb_put_zero(skb_out, padding_count);
} elseif (skb_out->len < ctx->tx_curr_size &&
(skb_out->len % dev->maxpacket) == 0) {
skb_put_u8(skb_out, 0); /* force short packet */
}
/* set final frame length */ if (ctx->is_ndp16) {
nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
nth.nth16->wBlockLength = cpu_to_le16(skb_out->len);
} else {
nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data;
nth.nth32->dwBlockLength = cpu_to_le32(skb_out->len);
}
/* return skb */
ctx->tx_curr_skb = NULL;
/* keep private stats: framing overhead and number of NTBs */
ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
ctx->tx_ntbs++;
/* usbnet will count all the framing overhead by default. * Adjust the stats so that the tx_bytes counter show real * payload data instead.
*/
usbnet_set_skb_tx_stats(skb_out, n,
(long)ctx->tx_curr_frame_payload - skb_out->len);
return skb_out;
alloc_failed: if (skb) {
dev_kfree_skb_any(skb);
dev->net->stats.tx_dropped++;
}
exit_no_skb: /* Start timer, if there is a remaining non-empty skb */ if (ctx->tx_curr_skb != NULL && n > 0)
cdc_ncm_tx_timeout_start(ctx); return NULL;
}
EXPORT_SYMBOL_GPL(cdc_ncm_fill_tx_frame);
staticvoid cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
{ /* start timer, if not already started */ if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
hrtimer_start(&ctx->tx_timer,
ctx->timer_interval,
HRTIMER_MODE_REL);
}
/* * The Ethernet API we are using does not support transmitting * multiple Ethernet frames in a single call. This driver will * accumulate multiple Ethernet frames and send out a larger * USB frame when the USB buffer is full or when a single jiffies * timeout happens.
*/ if (ctx == NULL) goto error;
/* verify NTB header and return offset of first NDP, or negative error */ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in)
{ struct usbnet *dev = netdev_priv(skb_in->dev); struct usb_cdc_ncm_nth16 *nth16; int len; int ret = -EINVAL;
if (ctx == NULL) goto error;
if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16))) {
netif_dbg(dev, rx_err, dev->net, "frame too short\n"); goto error;
}
ret = le32_to_cpu(nth32->dwNdpIndex);
error: return ret;
}
EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth32);
/* verify NDP header and return number of datagrams, or negative error */ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset)
{ struct usbnet *dev = netdev_priv(skb_in->dev); struct usb_cdc_ncm_ndp16 *ndp16; int ret = -EINVAL;
ret = ((le16_to_cpu(ndp16->wLength) - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16));
ret--; /* we process NDP entries except for the last one */
if ((sizeof(struct usb_cdc_ncm_ndp16) +
ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) {
netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
ret = -EINVAL;
}
/* verify NDP header and return number of datagrams, or negative error */ int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset)
{ struct usbnet *dev = netdev_priv(skb_in->dev); struct usb_cdc_ncm_ndp32 *ndp32; int ret = -EINVAL;
ret = ((le16_to_cpu(ndp32->wLength) - sizeof(struct usb_cdc_ncm_ndp32)) / sizeof(struct usb_cdc_ncm_dpe32));
ret--; /* we process NDP entries except for the last one */
if ((sizeof(struct usb_cdc_ncm_ndp32) +
ret * (sizeof(struct usb_cdc_ncm_dpe32))) > skb_in->len) {
netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret);
ret = -EINVAL;
}
for (x = 0; x < nframes; x++) { if (ctx->is_ndp16) {
offset = le16_to_cpu(dpe.dpe16->wDatagramIndex);
len = le16_to_cpu(dpe.dpe16->wDatagramLength);
} else {
offset = le32_to_cpu(dpe.dpe32->dwDatagramIndex);
len = le32_to_cpu(dpe.dpe32->dwDatagramLength);
}
/* * CDC NCM ch. 3.7 * All entries after first NULL entry are to be ignored
*/ if ((offset == 0) || (len == 0)) { if (!x) goto err_ndp; /* empty NTB */ break;
}
} else { /* create a fresh copy to reduce truesize */
skb = netdev_alloc_skb_ip_align(dev->net, len); if (!skb) goto error;
skb_put_data(skb, skb_in->data + offset, len);
usbnet_skb_return(dev, skb);
payload += len; /* count payload bytes in this NTB */
}
if (ctx->is_ndp16)
dpe.dpe16++; else
dpe.dpe32++;
}
err_ndp: /* are there more NDPs to process? */ if (ctx->is_ndp16)
ndpoffset = le16_to_cpu(ndp.ndp16->wNextNdpIndex); else
ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex);
/* test for split data in 8-byte chunks */ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
cdc_ncm_speed_change(dev,
(struct usb_cdc_speed_change *)urb->transfer_buffer); return;
}
event = urb->transfer_buffer;
switch (event->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: /* * According to the CDC NCM specification ch.7.1 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
*/ /* RTL8156 shipped before 2021 sends notification about * every 32ms. Don't forward notification if state is same.
*/ if (netif_carrier_ok(dev->net) != !!event->wValue)
usbnet_link_change(dev, !!event->wValue, 0); break;
case USB_CDC_NOTIFY_SPEED_CHANGE: if (urb->actual_length < (sizeof(*event) + sizeof(struct usb_cdc_speed_change)))
set_bit(EVENT_STS_SPLIT, &dev->flags); else
cdc_ncm_speed_change(dev,
(struct usb_cdc_speed_change *)&event[1]); break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.