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 <bniebuhr@efjohnson.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
877accca79
commit
9b39e9dded
@@ -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;
|
||||
|
||||
if (dev->unwrap) {
|
||||
unsigned long flags;
|
||||
|
||||
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->protocol = eth_type_trans(skb, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb->len;
|
||||
|
||||
/* no buffer copies needed, unless hardware can't
|
||||
* use skb buffers.
|
||||
*/
|
||||
status = netif_rx(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");
|
||||
|
||||
Reference in New Issue
Block a user