]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/usb/usbnet.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / usb / usbnet.c
index c04d49e31f814fe11d2f1b730dcba014afeb324c..95c41d56631ceb1e13fe9c23da8f29bf9d17441f 100644 (file)
@@ -391,14 +391,19 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
                goto error;
        // else network stack removes extra byte if we forced a short packet
 
-       if (skb->len)
-               usbnet_skb_return (dev, skb);
-       else {
-               netif_dbg(dev, rx_err, dev->net, "drop\n");
-error:
-               dev->net->stats.rx_errors++;
-               skb_queue_tail (&dev->done, skb);
+       if (skb->len) {
+               /* all data was already cloned from skb inside the driver */
+               if (dev->driver_info->flags & FLAG_MULTI_PACKET)
+                       dev_kfree_skb_any(skb);
+               else
+                       usbnet_skb_return(dev, skb);
+               return;
        }
+
+       netif_dbg(dev, rx_err, dev->net, "drop\n");
+error:
+       dev->net->stats.rx_errors++;
+       skb_queue_tail(&dev->done, skb);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -926,8 +931,10 @@ fail_halt:
                if (urb != NULL) {
                        clear_bit (EVENT_RX_MEMORY, &dev->flags);
                        status = usb_autopm_get_interface(dev->intf);
-                       if (status < 0)
+                       if (status < 0) {
+                               usb_free_urb(urb);
                                goto fail_lowmem;
+                       }
                        if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)
                                resched = 0;
                        usb_autopm_put_interface(dev->intf);
@@ -971,7 +978,8 @@ static void tx_complete (struct urb *urb)
        struct usbnet           *dev = entry->dev;
 
        if (urb->status == 0) {
-               dev->net->stats.tx_packets++;
+               if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
+                       dev->net->stats.tx_packets++;
                dev->net->stats.tx_bytes += entry->length;
        } else {
                dev->net->stats.tx_errors++;
@@ -1044,8 +1052,13 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        if (info->tx_fixup) {
                skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
                if (!skb) {
-                       netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
-                       goto drop;
+                       if (netif_msg_tx_err(dev)) {
+                               netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
+                               goto drop;
+                       } else {
+                               /* cdc_ncm collected packet; waits for more */
+                               goto not_drop;
+                       }
                }
        }
        length = skb->len;
@@ -1067,13 +1080,18 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        /* don't assume the hardware handles USB_ZERO_PACKET
         * NOTE:  strictly conforming cdc-ether devices should expect
         * the ZLP here, but ignore the one-byte packet.
+        * NOTE2: CDC NCM specification is different from CDC ECM when
+        * handling ZLP/short packets, so cdc_ncm driver will make short
+        * packet itself if needed.
         */
        if (length % dev->maxpacket == 0) {
                if (!(info->flags & FLAG_SEND_ZLP)) {
-                       urb->transfer_buffer_length++;
-                       if (skb_tailroom(skb)) {
-                               skb->data[skb->len] = 0;
-                               __skb_put(skb, 1);
+                       if (!(info->flags & FLAG_MULTI_PACKET)) {
+                               urb->transfer_buffer_length++;
+                               if (skb_tailroom(skb)) {
+                                       skb->data[skb->len] = 0;
+                                       __skb_put(skb, 1);
+                               }
                        }
                } else
                        urb->transfer_flags |= URB_ZERO_PACKET;
@@ -1122,6 +1140,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
                netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval);
 drop:
                dev->net->stats.tx_dropped++;
+not_drop:
                if (skb)
                        dev_kfree_skb_any (skb);
                usb_free_urb (urb);
@@ -1231,8 +1250,7 @@ void usbnet_disconnect (struct usb_interface *intf)
        net = dev->net;
        unregister_netdev (net);
 
-       /* we don't hold rtnl here ... */
-       flush_scheduled_work ();
+       cancel_work_sync(&dev->kevent);
 
        if (dev->driver_info->unbind)
                dev->driver_info->unbind (dev, intf);