From 9b39e9ddedeef48569f8aac60a7b4c1fbb127c7d Mon Sep 17 00:00:00 2001 From: Brian Niebuhr Date: Fri, 14 Aug 2009 10:04:22 -0500 Subject: USB: gadget: Add EEM gadget driver This patch adds a CDC EEM ethernet gadget driver. CDC EEM is a newer USB ethernet specification that uses a simpler interface than the older CDC ECM. This makes CDC EEM usable by a wider set of USB hardware. By default the ethernet gadget will still use CDC ECM/Subset, but kernel configuration and/or a module parameter will allow alternative use of the CDC EEM protocol. Changes since last version: - Brought in missing RNDIS changes that caused compile error - Modified 'sentinel CRC' checking to match EEM host driver Signed-off-by: Brian Niebuhr Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/u_ether.c | 85 +++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 28 deletions(-) (limited to 'drivers/usb/gadget/u_ether.c') diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index c6652195391..f8751ff863c 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -37,8 +37,9 @@ * one (!) network link through the USB gadget stack, normally "usb0". * * The control and data models are handled by the function driver which - * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. - * That includes all descriptor and endpoint management. + * connects to this code; such as CDC Ethernet (ECM or EEM), + * "CDC Subset", or RNDIS. That includes all descriptor and endpoint + * management. * * Link level addressing is handled by this component using module * parameters; if no such parameters are provided, random link level @@ -68,9 +69,13 @@ struct eth_dev { struct list_head tx_reqs, rx_reqs; atomic_t tx_qlen; + struct sk_buff_head rx_frames; + unsigned header_len; - struct sk_buff *(*wrap)(struct sk_buff *skb); - int (*unwrap)(struct sk_buff *skb); + struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); + int (*unwrap)(struct gether *, + struct sk_buff *skb, + struct sk_buff_head *list); struct work_struct work; @@ -269,7 +274,7 @@ enomem: static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context; + struct sk_buff *skb = req->context, *skb2; struct eth_dev *dev = ep->driver_data; int status = req->status; @@ -278,26 +283,47 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) /* normal completion */ case 0: skb_put(skb, req->actual); - if (dev->unwrap) - status = dev->unwrap(skb); - if (status < 0 - || ETH_HLEN > skb->len - || skb->len > ETH_FRAME_LEN) { - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - DBG(dev, "rx length %d\n", skb->len); - break; - } - skb->protocol = eth_type_trans(skb, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb->len; + if (dev->unwrap) { + unsigned long flags; - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb); + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + status = dev->unwrap(dev->port_usb, + skb, + &dev->rx_frames); + } else { + dev_kfree_skb_any(skb); + status = -ENOTCONN; + } + spin_unlock_irqrestore(&dev->lock, flags); + } else { + skb_queue_tail(&dev->rx_frames, skb); + } skb = NULL; + + skb2 = skb_dequeue(&dev->rx_frames); + while (skb2) { + if (status < 0 + || ETH_HLEN > skb2->len + || skb2->len > ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb2->len); + dev_kfree_skb_any(skb2); + goto next_frame; + } + skb2->protocol = eth_type_trans(skb2, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb2->len; + + /* no buffer copies needed, unless hardware can't + * use skb buffers. + */ + status = netif_rx(skb2); +next_frame: + skb2 = skb_dequeue(&dev->rx_frames); + } break; /* software-driven interface shutdown */ @@ -537,14 +563,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, * or there's not enough space for extra headers we need */ if (dev->wrap) { - struct sk_buff *skb_new; + unsigned long flags; - skb_new = dev->wrap(skb); - if (!skb_new) + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + skb = dev->wrap(dev->port_usb, skb); + spin_unlock_irqrestore(&dev->lock, flags); + if (!skb) goto drop; - dev_kfree_skb_any(skb); - skb = skb_new; length = skb->len; } req->buf = skb->data; @@ -578,9 +605,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } if (retval) { + dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; - dev_kfree_skb_any(skb); spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); @@ -753,6 +780,8 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->rx_reqs); + skb_queue_head_init(&dev->rx_frames); + /* network device setup */ dev->net = net; strcpy(net->name, "usb%d"); -- cgit v1.2.3 From 2bcd57ab61e7cabed626226a3771617981c11ce1 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 24 Sep 2009 04:22:25 +0400 Subject: headers: utsname.h redux * remove asm/atomic.h inclusion from linux/utsname.h -- not needed after kref conversion * remove linux/utsname.h inclusion from files which do not need it NOTE: it looks like fs/binfmt_elf.c do not need utsname.h, however due to some personality stuff it _is_ needed -- cowardly leave ELF-related headers and files alone. Signed-off-by: Alexey Dobriyan Signed-off-by: Linus Torvalds --- drivers/usb/gadget/u_ether.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb/gadget/u_ether.c') diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index f8751ff863c..2fc02bd9584 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -23,7 +23,6 @@ /* #define VERBOSE_DEBUG */ #include -#include #include #include #include -- cgit v1.2.3