diff options
-rw-r--r-- | drivers/net/usb/usbnet.c | 44 | ||||
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 13 | ||||
-rw-r--r-- | include/linux/usb/usbnet.h | 6 |
3 files changed, 61 insertions, 2 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index af1fe4696509..7d471fca2743 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c | |||
@@ -233,6 +233,11 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) | |||
233 | { | 233 | { |
234 | int status; | 234 | int status; |
235 | 235 | ||
236 | if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { | ||
237 | skb_queue_tail(&dev->rxq_pause, skb); | ||
238 | return; | ||
239 | } | ||
240 | |||
236 | skb->protocol = eth_type_trans (skb, dev->net); | 241 | skb->protocol = eth_type_trans (skb, dev->net); |
237 | dev->net->stats.rx_packets++; | 242 | dev->net->stats.rx_packets++; |
238 | dev->net->stats.rx_bytes += skb->len; | 243 | dev->net->stats.rx_bytes += skb->len; |
@@ -526,6 +531,41 @@ static void intr_complete (struct urb *urb) | |||
526 | } | 531 | } |
527 | 532 | ||
528 | /*-------------------------------------------------------------------------*/ | 533 | /*-------------------------------------------------------------------------*/ |
534 | void usbnet_pause_rx(struct usbnet *dev) | ||
535 | { | ||
536 | set_bit(EVENT_RX_PAUSED, &dev->flags); | ||
537 | |||
538 | if (netif_msg_rx_status(dev)) | ||
539 | devdbg(dev, "paused rx queue enabled"); | ||
540 | } | ||
541 | EXPORT_SYMBOL_GPL(usbnet_pause_rx); | ||
542 | |||
543 | void usbnet_resume_rx(struct usbnet *dev) | ||
544 | { | ||
545 | struct sk_buff *skb; | ||
546 | int num = 0; | ||
547 | |||
548 | clear_bit(EVENT_RX_PAUSED, &dev->flags); | ||
549 | |||
550 | while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { | ||
551 | usbnet_skb_return(dev, skb); | ||
552 | num++; | ||
553 | } | ||
554 | |||
555 | tasklet_schedule(&dev->bh); | ||
556 | |||
557 | if (netif_msg_rx_status(dev)) | ||
558 | devdbg(dev, "paused rx queue disabled, %d skbs requeued", num); | ||
559 | } | ||
560 | EXPORT_SYMBOL_GPL(usbnet_resume_rx); | ||
561 | |||
562 | void usbnet_purge_paused_rxq(struct usbnet *dev) | ||
563 | { | ||
564 | skb_queue_purge(&dev->rxq_pause); | ||
565 | } | ||
566 | EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); | ||
567 | |||
568 | /*-------------------------------------------------------------------------*/ | ||
529 | 569 | ||
530 | // unlink pending rx/tx; completion handlers do all other cleanup | 570 | // unlink pending rx/tx; completion handlers do all other cleanup |
531 | 571 | ||
@@ -623,6 +663,8 @@ int usbnet_stop (struct net_device *net) | |||
623 | 663 | ||
624 | usb_kill_urb(dev->interrupt); | 664 | usb_kill_urb(dev->interrupt); |
625 | 665 | ||
666 | usbnet_purge_paused_rxq(dev); | ||
667 | |||
626 | /* deferred work (task, timer, softirq) must also stop. | 668 | /* deferred work (task, timer, softirq) must also stop. |
627 | * can't flush_scheduled_work() until we drop rtnl (later), | 669 | * can't flush_scheduled_work() until we drop rtnl (later), |
628 | * else workers could deadlock; so make workers a NOP. | 670 | * else workers could deadlock; so make workers a NOP. |
@@ -1113,7 +1155,6 @@ static void usbnet_bh (unsigned long param) | |||
1113 | } | 1155 | } |
1114 | 1156 | ||
1115 | 1157 | ||
1116 | |||
1117 | /*------------------------------------------------------------------------- | 1158 | /*------------------------------------------------------------------------- |
1118 | * | 1159 | * |
1119 | * USB Device Driver support | 1160 | * USB Device Driver support |
@@ -1210,6 +1251,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) | |||
1210 | skb_queue_head_init (&dev->rxq); | 1251 | skb_queue_head_init (&dev->rxq); |
1211 | skb_queue_head_init (&dev->txq); | 1252 | skb_queue_head_init (&dev->txq); |
1212 | skb_queue_head_init (&dev->done); | 1253 | skb_queue_head_init (&dev->done); |
1254 | skb_queue_head_init(&dev->rxq_pause); | ||
1213 | dev->bh.func = usbnet_bh; | 1255 | dev->bh.func = usbnet_bh; |
1214 | dev->bh.data = (unsigned long) dev; | 1256 | dev->bh.data = (unsigned long) dev; |
1215 | INIT_WORK (&dev->kevent, kevent); | 1257 | INIT_WORK (&dev->kevent, kevent); |
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 828dc1825bba..d42692dfbc67 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c | |||
@@ -1764,8 +1764,15 @@ static int rndis_iw_set_essid(struct net_device *dev, | |||
1764 | 1764 | ||
1765 | if (!wrqu->essid.flags || length == 0) | 1765 | if (!wrqu->essid.flags || length == 0) |
1766 | return disassociate(usbdev, 1); | 1766 | return disassociate(usbdev, 1); |
1767 | else | 1767 | else { |
1768 | /* Pause and purge rx queue, so we don't pass packets before | ||
1769 | * 'media connect'-indication. | ||
1770 | */ | ||
1771 | usbnet_pause_rx(usbdev); | ||
1772 | usbnet_purge_paused_rxq(usbdev); | ||
1773 | |||
1768 | return set_essid(usbdev, &ssid); | 1774 | return set_essid(usbdev, &ssid); |
1775 | } | ||
1769 | } | 1776 | } |
1770 | 1777 | ||
1771 | 1778 | ||
@@ -2328,6 +2335,8 @@ get_bssid: | |||
2328 | memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN); | 2335 | memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN); |
2329 | wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); | 2336 | wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); |
2330 | } | 2337 | } |
2338 | |||
2339 | usbnet_resume_rx(usbdev); | ||
2331 | } | 2340 | } |
2332 | 2341 | ||
2333 | if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) { | 2342 | if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) { |
@@ -2541,6 +2550,8 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) | |||
2541 | 2550 | ||
2542 | switch (msg->status) { | 2551 | switch (msg->status) { |
2543 | case RNDIS_STATUS_MEDIA_CONNECT: | 2552 | case RNDIS_STATUS_MEDIA_CONNECT: |
2553 | usbnet_pause_rx(usbdev); | ||
2554 | |||
2544 | devinfo(usbdev, "media connect"); | 2555 | devinfo(usbdev, "media connect"); |
2545 | 2556 | ||
2546 | /* queue work to avoid recursive calls into rndis_command */ | 2557 | /* queue work to avoid recursive calls into rndis_command */ |
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index de8b4b18961b..09514252d84e 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h | |||
@@ -53,6 +53,7 @@ struct usbnet { | |||
53 | struct sk_buff_head rxq; | 53 | struct sk_buff_head rxq; |
54 | struct sk_buff_head txq; | 54 | struct sk_buff_head txq; |
55 | struct sk_buff_head done; | 55 | struct sk_buff_head done; |
56 | struct sk_buff_head rxq_pause; | ||
56 | struct urb *interrupt; | 57 | struct urb *interrupt; |
57 | struct tasklet_struct bh; | 58 | struct tasklet_struct bh; |
58 | 59 | ||
@@ -63,6 +64,7 @@ struct usbnet { | |||
63 | # define EVENT_RX_MEMORY 2 | 64 | # define EVENT_RX_MEMORY 2 |
64 | # define EVENT_STS_SPLIT 3 | 65 | # define EVENT_STS_SPLIT 3 |
65 | # define EVENT_LINK_RESET 4 | 66 | # define EVENT_LINK_RESET 4 |
67 | # define EVENT_RX_PAUSED 5 | ||
66 | }; | 68 | }; |
67 | 69 | ||
68 | static inline struct usb_driver *driver_of(struct usb_interface *intf) | 70 | static inline struct usb_driver *driver_of(struct usb_interface *intf) |
@@ -190,6 +192,10 @@ extern void usbnet_defer_kevent (struct usbnet *, int); | |||
190 | extern void usbnet_skb_return (struct usbnet *, struct sk_buff *); | 192 | extern void usbnet_skb_return (struct usbnet *, struct sk_buff *); |
191 | extern void usbnet_unlink_rx_urbs(struct usbnet *); | 193 | extern void usbnet_unlink_rx_urbs(struct usbnet *); |
192 | 194 | ||
195 | extern void usbnet_pause_rx(struct usbnet *); | ||
196 | extern void usbnet_resume_rx(struct usbnet *); | ||
197 | extern void usbnet_purge_paused_rxq(struct usbnet *); | ||
198 | |||
193 | extern int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd); | 199 | extern int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd); |
194 | extern int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd); | 200 | extern int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd); |
195 | extern u32 usbnet_get_link (struct net_device *net); | 201 | extern u32 usbnet_get_link (struct net_device *net); |