Merge branch 'r8152-next'
[cascardo/linux.git] / drivers / net / usb / r8152.c
index f116335..4a9ece0 100644 (file)
@@ -461,11 +461,7 @@ enum rtl8152_flags {
 
 /* Define these values to match your device */
 #define VENDOR_ID_REALTEK              0x0bda
-#define PRODUCT_ID_RTL8152             0x8152
-#define PRODUCT_ID_RTL8153             0x8153
-
 #define VENDOR_ID_SAMSUNG              0x04e8
-#define PRODUCT_ID_SAMSUNG             0xa101
 
 #define MCU_TYPE_PLA                   0x0100
 #define MCU_TYPE_USB                   0x0000
@@ -1036,7 +1032,6 @@ static void read_bulk_callback(struct urb *urb)
        int status = urb->status;
        struct rx_agg *agg;
        struct r8152 *tp;
-       int result;
 
        agg = urb->context;
        if (!agg)
@@ -1087,16 +1082,7 @@ static void read_bulk_callback(struct urb *urb)
                break;
        }
 
-       result = r8152_submit_rx(tp, agg, GFP_ATOMIC);
-       if (result == -ENODEV) {
-               set_bit(RTL8152_UNPLUG, &tp->flags);
-               netif_device_detach(tp->netdev);
-       } else if (result) {
-               spin_lock(&tp->rx_lock);
-               list_add_tail(&agg->list, &tp->rx_done);
-               spin_unlock(&tp->rx_lock);
-               tasklet_schedule(&tp->tl);
-       }
+       r8152_submit_rx(tp, agg, GFP_ATOMIC);
 }
 
 static void write_bulk_callback(struct urb *urb)
@@ -1169,6 +1155,9 @@ static void intr_callback(struct urb *urb)
        case -ESHUTDOWN:
                netif_device_detach(tp->netdev);
        case -ENOENT:
+       case -EPROTO:
+               netif_info(tp, intr, tp->netdev,
+                          "Stop submitting intr, status %d\n", status);
                return;
        case -EOVERFLOW:
                netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n");
@@ -1256,7 +1245,6 @@ static int alloc_all_mem(struct r8152 *tp)
 
        spin_lock_init(&tp->rx_lock);
        spin_lock_init(&tp->tx_lock);
-       INIT_LIST_HEAD(&tp->rx_done);
        INIT_LIST_HEAD(&tp->tx_free);
        skb_queue_head_init(&tp->tx_queue);
 
@@ -1682,7 +1670,6 @@ static void rx_bottom(struct r8152 *tp)
                int len_used = 0;
                struct urb *urb;
                u8 *rx_data;
-               int ret;
 
                list_del_init(cursor);
 
@@ -1735,13 +1722,7 @@ find_next_rx:
                }
 
 submit:
-               ret = r8152_submit_rx(tp, agg, GFP_ATOMIC);
-               if (ret && ret != -ENODEV) {
-                       spin_lock_irqsave(&tp->rx_lock, flags);
-                       list_add_tail(&agg->list, &tp->rx_done);
-                       spin_unlock_irqrestore(&tp->rx_lock, flags);
-                       tasklet_schedule(&tp->tl);
-               }
+               r8152_submit_rx(tp, agg, GFP_ATOMIC);
        }
 }
 
@@ -1799,6 +1780,8 @@ static void bottom_half(unsigned long data)
        if (!netif_carrier_ok(tp->netdev))
                return;
 
+       clear_bit(SCHEDULE_TASKLET, &tp->flags);
+
        rx_bottom(tp);
        tx_bottom(tp);
 }
@@ -1806,11 +1789,28 @@ static void bottom_half(unsigned long data)
 static
 int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags)
 {
+       int ret;
+
        usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1),
                          agg->head, agg_buf_sz,
                          (usb_complete_t)read_bulk_callback, agg);
 
-       return usb_submit_urb(agg->urb, mem_flags);
+       ret = usb_submit_urb(agg->urb, mem_flags);
+       if (ret == -ENODEV) {
+               set_bit(RTL8152_UNPLUG, &tp->flags);
+               netif_device_detach(tp->netdev);
+       } else if (ret) {
+               struct urb *urb = agg->urb;
+               unsigned long flags;
+
+               urb->actual_length = 0;
+               spin_lock_irqsave(&tp->rx_lock, flags);
+               list_add_tail(&agg->list, &tp->rx_done);
+               spin_unlock_irqrestore(&tp->rx_lock, flags);
+               tasklet_schedule(&tp->tl);
+       }
+
+       return ret;
 }
 
 static void rtl_drop_queued_tx(struct r8152 *tp)
@@ -2001,6 +2001,25 @@ static int rtl_start_rx(struct r8152 *tp)
                        break;
        }
 
+       if (ret && ++i < RTL8152_MAX_RX) {
+               struct list_head rx_queue;
+               unsigned long flags;
+
+               INIT_LIST_HEAD(&rx_queue);
+
+               do {
+                       struct rx_agg *agg = &tp->rx_info[i++];
+                       struct urb *urb = agg->urb;
+
+                       urb->actual_length = 0;
+                       list_add_tail(&agg->list, &rx_queue);
+               } while (i < RTL8152_MAX_RX);
+
+               spin_lock_irqsave(&tp->rx_lock, flags);
+               list_splice_tail(&rx_queue, &tp->rx_done);
+               spin_unlock_irqrestore(&tp->rx_lock, flags);
+       }
+
        return ret;
 }
 
@@ -2857,15 +2876,18 @@ static void rtl_work_func_t(struct work_struct *work)
 {
        struct r8152 *tp = container_of(work, struct r8152, schedule.work);
 
+       /* If the device is unplugged or !netif_running(), the workqueue
+        * doesn't need to wake the device, and could return directly.
+        */
+       if (test_bit(RTL8152_UNPLUG, &tp->flags) || !netif_running(tp->netdev))
+               return;
+
        if (usb_autopm_get_interface(tp->intf) < 0)
                return;
 
        if (!test_bit(WORK_ENABLE, &tp->flags))
                goto out1;
 
-       if (test_bit(RTL8152_UNPLUG, &tp->flags))
-               goto out1;
-
        if (!mutex_trylock(&tp->control)) {
                schedule_delayed_work(&tp->schedule, 0);
                goto out1;
@@ -2901,6 +2923,9 @@ static int rtl8152_open(struct net_device *netdev)
        if (res)
                goto out;
 
+       /* set speed to 0 to avoid autoresume try to submit rx */
+       tp->speed = 0;
+
        res = usb_autopm_get_interface(tp->intf);
        if (res < 0) {
                free_all_mem(tp);
@@ -2914,6 +2939,8 @@ static int rtl8152_open(struct net_device *netdev)
                clear_bit(WORK_ENABLE, &tp->flags);
                usb_kill_urb(tp->intr_urb);
                cancel_delayed_work_sync(&tp->schedule);
+
+               /* disable the tx/rx, if the workqueue has enabled them. */
                if (tp->speed & LINK_STATUS)
                        tp->rtl_ops.disable(tp);
        }
@@ -2935,6 +2962,8 @@ static int rtl8152_open(struct net_device *netdev)
                netif_warn(tp, ifup, netdev, "intr_urb submit failed: %d\n",
                           res);
                free_all_mem(tp);
+       } else {
+               tasklet_enable(&tp->tl);
        }
 
        mutex_unlock(&tp->control);
@@ -2950,6 +2979,7 @@ static int rtl8152_close(struct net_device *netdev)
        struct r8152 *tp = netdev_priv(netdev);
        int res = 0;
 
+       tasklet_disable(&tp->tl);
        clear_bit(WORK_ENABLE, &tp->flags);
        usb_kill_urb(tp->intr_urb);
        cancel_delayed_work_sync(&tp->schedule);
@@ -2965,14 +2995,9 @@ static int rtl8152_close(struct net_device *netdev)
                 * be disable when autoresume occurs, because the
                 * netif_running() would be false.
                 */
-               if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
-                       rtl_runtime_suspend_enable(tp, false);
-                       clear_bit(SELECTIVE_SUSPEND, &tp->flags);
-               }
+               rtl_runtime_suspend_enable(tp, false);
 
-               tasklet_disable(&tp->tl);
                tp->rtl_ops.down(tp);
-               tasklet_enable(&tp->tl);
 
                mutex_unlock(&tp->control);
 
@@ -3215,7 +3240,7 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
                netif_device_detach(netdev);
        }
 
-       if (netif_running(netdev)) {
+       if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
                clear_bit(WORK_ENABLE, &tp->flags);
                usb_kill_urb(tp->intr_urb);
                tasklet_disable(&tp->tl);
@@ -3263,6 +3288,8 @@ static int rtl8152_resume(struct usb_interface *intf)
                        set_bit(WORK_ENABLE, &tp->flags);
                }
                usb_submit_urb(tp->intr_urb, GFP_KERNEL);
+       } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+               clear_bit(SELECTIVE_SUSPEND, &tp->flags);
        }
 
        mutex_unlock(&tp->control);
@@ -3734,66 +3761,43 @@ static void rtl8153_unload(struct r8152 *tp)
        r8153_power_cut_en(tp, false);
 }
 
-static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id)
+static int rtl_ops_init(struct r8152 *tp)
 {
        struct rtl_ops *ops = &tp->rtl_ops;
-       int ret = -ENODEV;
-
-       switch (id->idVendor) {
-       case VENDOR_ID_REALTEK:
-               switch (id->idProduct) {
-               case PRODUCT_ID_RTL8152:
-                       ops->init               = r8152b_init;
-                       ops->enable             = rtl8152_enable;
-                       ops->disable            = rtl8152_disable;
-                       ops->up                 = rtl8152_up;
-                       ops->down               = rtl8152_down;
-                       ops->unload             = rtl8152_unload;
-                       ops->eee_get            = r8152_get_eee;
-                       ops->eee_set            = r8152_set_eee;
-                       ret = 0;
-                       break;
-               case PRODUCT_ID_RTL8153:
-                       ops->init               = r8153_init;
-                       ops->enable             = rtl8153_enable;
-                       ops->disable            = rtl8153_disable;
-                       ops->up                 = rtl8153_up;
-                       ops->down               = rtl8153_down;
-                       ops->unload             = rtl8153_unload;
-                       ops->eee_get            = r8153_get_eee;
-                       ops->eee_set            = r8153_set_eee;
-                       ret = 0;
-                       break;
-               default:
-                       break;
-               }
+       int ret = 0;
+
+       switch (tp->version) {
+       case RTL_VER_01:
+       case RTL_VER_02:
+               ops->init               = r8152b_init;
+               ops->enable             = rtl8152_enable;
+               ops->disable            = rtl8152_disable;
+               ops->up                 = rtl8152_up;
+               ops->down               = rtl8152_down;
+               ops->unload             = rtl8152_unload;
+               ops->eee_get            = r8152_get_eee;
+               ops->eee_set            = r8152_set_eee;
                break;
 
-       case VENDOR_ID_SAMSUNG:
-               switch (id->idProduct) {
-               case PRODUCT_ID_SAMSUNG:
-                       ops->init               = r8153_init;
-                       ops->enable             = rtl8153_enable;
-                       ops->disable            = rtl8153_disable;
-                       ops->up                 = rtl8153_up;
-                       ops->down               = rtl8153_down;
-                       ops->unload             = rtl8153_unload;
-                       ops->eee_get            = r8153_get_eee;
-                       ops->eee_set            = r8153_set_eee;
-                       ret = 0;
-                       break;
-               default:
-                       break;
-               }
+       case RTL_VER_03:
+       case RTL_VER_04:
+       case RTL_VER_05:
+               ops->init               = r8153_init;
+               ops->enable             = rtl8153_enable;
+               ops->disable            = rtl8153_disable;
+               ops->up                 = rtl8153_up;
+               ops->down               = rtl8153_down;
+               ops->unload             = rtl8153_unload;
+               ops->eee_get            = r8153_get_eee;
+               ops->eee_set            = r8153_set_eee;
                break;
 
        default:
+               ret = -ENODEV;
+               netif_err(tp, probe, tp->netdev, "Unknown Device\n");
                break;
        }
 
-       if (ret)
-               netif_err(tp, probe, tp->netdev, "Unknown Device\n");
-
        return ret;
 }
 
@@ -3825,7 +3829,8 @@ static int rtl8152_probe(struct usb_interface *intf,
        tp->netdev = netdev;
        tp->intf = intf;
 
-       ret = rtl_ops_init(tp, id);
+       r8152b_get_version(tp);
+       ret = rtl_ops_init(tp);
        if (ret)
                goto out;
 
@@ -3858,11 +3863,9 @@ static int rtl8152_probe(struct usb_interface *intf,
        tp->mii.phy_id_mask = 0x3f;
        tp->mii.reg_num_mask = 0x1f;
        tp->mii.phy_id = R8152_PHY_ID;
-       tp->mii.supports_gmii = 0;
 
        intf->needs_remote_wakeup = 1;
 
-       r8152b_get_version(tp);
        tp->rtl_ops.init(tp);
        set_ethernet_addr(tp);
 
@@ -3880,12 +3883,15 @@ static int rtl8152_probe(struct usb_interface *intf,
        else
                device_set_wakeup_enable(&udev->dev, false);
 
+       tasklet_disable(&tp->tl);
+
        netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION);
 
        return 0;
 
 out1:
        usb_set_intfdata(intf, NULL);
+       tasklet_kill(&tp->tl);
 out:
        free_netdev(netdev);
        return ret;
@@ -3911,9 +3917,9 @@ static void rtl8152_disconnect(struct usb_interface *intf)
 
 /* table of devices that work with this driver */
 static struct usb_device_id rtl8152_table[] = {
-       {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)},
-       {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8153)},
-       {USB_DEVICE(VENDOR_ID_SAMSUNG, PRODUCT_ID_SAMSUNG)},
+       {USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
+       {USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
+       {USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
        {}
 };