diff options
author | Brian Niebuhr <bniebuhr@efjohnson.com> | 2009-08-14 11:04:22 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:35 -0400 |
commit | 9b39e9ddedeef48569f8aac60a7b4c1fbb127c7d (patch) | |
tree | d2cc583190e18fa03298e84093c3346e1646007c /drivers/usb/gadget/u_ether.c | |
parent | 877accca79b706afe5d78b9a92cf4f22919fb2b0 (diff) |
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>
Diffstat (limited to 'drivers/usb/gadget/u_ether.c')
-rw-r--r-- | drivers/usb/gadget/u_ether.c | 85 |
1 files changed, 57 insertions, 28 deletions
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index c66521953917..f8751ff863cd 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c | |||
@@ -37,8 +37,9 @@ | |||
37 | * one (!) network link through the USB gadget stack, normally "usb0". | 37 | * one (!) network link through the USB gadget stack, normally "usb0". |
38 | * | 38 | * |
39 | * The control and data models are handled by the function driver which | 39 | * The control and data models are handled by the function driver which |
40 | * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. | 40 | * connects to this code; such as CDC Ethernet (ECM or EEM), |
41 | * That includes all descriptor and endpoint management. | 41 | * "CDC Subset", or RNDIS. That includes all descriptor and endpoint |
42 | * management. | ||
42 | * | 43 | * |
43 | * Link level addressing is handled by this component using module | 44 | * Link level addressing is handled by this component using module |
44 | * parameters; if no such parameters are provided, random link level | 45 | * parameters; if no such parameters are provided, random link level |
@@ -68,9 +69,13 @@ struct eth_dev { | |||
68 | struct list_head tx_reqs, rx_reqs; | 69 | struct list_head tx_reqs, rx_reqs; |
69 | atomic_t tx_qlen; | 70 | atomic_t tx_qlen; |
70 | 71 | ||
72 | struct sk_buff_head rx_frames; | ||
73 | |||
71 | unsigned header_len; | 74 | unsigned header_len; |
72 | struct sk_buff *(*wrap)(struct sk_buff *skb); | 75 | struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); |
73 | int (*unwrap)(struct sk_buff *skb); | 76 | int (*unwrap)(struct gether *, |
77 | struct sk_buff *skb, | ||
78 | struct sk_buff_head *list); | ||
74 | 79 | ||
75 | struct work_struct work; | 80 | struct work_struct work; |
76 | 81 | ||
@@ -269,7 +274,7 @@ enomem: | |||
269 | 274 | ||
270 | static void rx_complete(struct usb_ep *ep, struct usb_request *req) | 275 | static void rx_complete(struct usb_ep *ep, struct usb_request *req) |
271 | { | 276 | { |
272 | struct sk_buff *skb = req->context; | 277 | struct sk_buff *skb = req->context, *skb2; |
273 | struct eth_dev *dev = ep->driver_data; | 278 | struct eth_dev *dev = ep->driver_data; |
274 | int status = req->status; | 279 | int status = req->status; |
275 | 280 | ||
@@ -278,26 +283,47 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) | |||
278 | /* normal completion */ | 283 | /* normal completion */ |
279 | case 0: | 284 | case 0: |
280 | skb_put(skb, req->actual); | 285 | skb_put(skb, req->actual); |
281 | if (dev->unwrap) | ||
282 | status = dev->unwrap(skb); | ||
283 | if (status < 0 | ||
284 | || ETH_HLEN > skb->len | ||
285 | || skb->len > ETH_FRAME_LEN) { | ||
286 | dev->net->stats.rx_errors++; | ||
287 | dev->net->stats.rx_length_errors++; | ||
288 | DBG(dev, "rx length %d\n", skb->len); | ||
289 | break; | ||
290 | } | ||
291 | 286 | ||
292 | skb->protocol = eth_type_trans(skb, dev->net); | 287 | if (dev->unwrap) { |
293 | dev->net->stats.rx_packets++; | 288 | unsigned long flags; |
294 | dev->net->stats.rx_bytes += skb->len; | ||
295 | 289 | ||
296 | /* no buffer copies needed, unless hardware can't | 290 | spin_lock_irqsave(&dev->lock, flags); |
297 | * use skb buffers. | 291 | if (dev->port_usb) { |
298 | */ | 292 | status = dev->unwrap(dev->port_usb, |
299 | status = netif_rx(skb); | 293 | skb, |
294 | &dev->rx_frames); | ||
295 | } else { | ||
296 | dev_kfree_skb_any(skb); | ||
297 | status = -ENOTCONN; | ||
298 | } | ||
299 | spin_unlock_irqrestore(&dev->lock, flags); | ||
300 | } else { | ||
301 | skb_queue_tail(&dev->rx_frames, skb); | ||
302 | } | ||
300 | skb = NULL; | 303 | skb = NULL; |
304 | |||
305 | skb2 = skb_dequeue(&dev->rx_frames); | ||
306 | while (skb2) { | ||
307 | if (status < 0 | ||
308 | || ETH_HLEN > skb2->len | ||
309 | || skb2->len > ETH_FRAME_LEN) { | ||
310 | dev->net->stats.rx_errors++; | ||
311 | dev->net->stats.rx_length_errors++; | ||
312 | DBG(dev, "rx length %d\n", skb2->len); | ||
313 | dev_kfree_skb_any(skb2); | ||
314 | goto next_frame; | ||
315 | } | ||
316 | skb2->protocol = eth_type_trans(skb2, dev->net); | ||
317 | dev->net->stats.rx_packets++; | ||
318 | dev->net->stats.rx_bytes += skb2->len; | ||
319 | |||
320 | /* no buffer copies needed, unless hardware can't | ||
321 | * use skb buffers. | ||
322 | */ | ||
323 | status = netif_rx(skb2); | ||
324 | next_frame: | ||
325 | skb2 = skb_dequeue(&dev->rx_frames); | ||
326 | } | ||
301 | break; | 327 | break; |
302 | 328 | ||
303 | /* software-driven interface shutdown */ | 329 | /* software-driven interface shutdown */ |
@@ -537,14 +563,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
537 | * or there's not enough space for extra headers we need | 563 | * or there's not enough space for extra headers we need |
538 | */ | 564 | */ |
539 | if (dev->wrap) { | 565 | if (dev->wrap) { |
540 | struct sk_buff *skb_new; | 566 | unsigned long flags; |
541 | 567 | ||
542 | skb_new = dev->wrap(skb); | 568 | spin_lock_irqsave(&dev->lock, flags); |
543 | if (!skb_new) | 569 | if (dev->port_usb) |
570 | skb = dev->wrap(dev->port_usb, skb); | ||
571 | spin_unlock_irqrestore(&dev->lock, flags); | ||
572 | if (!skb) | ||
544 | goto drop; | 573 | goto drop; |
545 | 574 | ||
546 | dev_kfree_skb_any(skb); | ||
547 | skb = skb_new; | ||
548 | length = skb->len; | 575 | length = skb->len; |
549 | } | 576 | } |
550 | req->buf = skb->data; | 577 | req->buf = skb->data; |
@@ -578,9 +605,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
578 | } | 605 | } |
579 | 606 | ||
580 | if (retval) { | 607 | if (retval) { |
608 | dev_kfree_skb_any(skb); | ||
581 | drop: | 609 | drop: |
582 | dev->net->stats.tx_dropped++; | 610 | dev->net->stats.tx_dropped++; |
583 | dev_kfree_skb_any(skb); | ||
584 | spin_lock_irqsave(&dev->req_lock, flags); | 611 | spin_lock_irqsave(&dev->req_lock, flags); |
585 | if (list_empty(&dev->tx_reqs)) | 612 | if (list_empty(&dev->tx_reqs)) |
586 | netif_start_queue(net); | 613 | netif_start_queue(net); |
@@ -753,6 +780,8 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) | |||
753 | INIT_LIST_HEAD(&dev->tx_reqs); | 780 | INIT_LIST_HEAD(&dev->tx_reqs); |
754 | INIT_LIST_HEAD(&dev->rx_reqs); | 781 | INIT_LIST_HEAD(&dev->rx_reqs); |
755 | 782 | ||
783 | skb_queue_head_init(&dev->rx_frames); | ||
784 | |||
756 | /* network device setup */ | 785 | /* network device setup */ |
757 | dev->net = net; | 786 | dev->net = net; |
758 | strcpy(net->name, "usb%d"); | 787 | strcpy(net->name, "usb%d"); |