broadcom-wl: fix crash when starting multiple virtual interfaces
[openwrt/svn-archive/archive.git] / package / kernel / broadcom-wl / patches / 008-fix_virtual_interfaces.patch
index 6cffa27810cf131314a37243c16a9eb81626eb48..23831df5ba6a9c5b8f4c4df07816b6bc2a297dcd 100644 (file)
@@ -1,6 +1,117 @@
 --- a/driver/wl_linux.c
 +++ b/driver/wl_linux.c
-@@ -1541,6 +1541,8 @@ wl_add_if(wl_info_t *wl, struct wlc_if* 
+@@ -354,6 +354,7 @@ static int wl_read_proc(char *buffer, ch
+ static int wl_dump(wl_info_t *wl, struct bcmstrbuf *b);
+ #endif /* BCMDBG */
+ struct wl_if *wl_alloc_if(wl_info_t *wl, int iftype, uint unit, struct wlc_if* wlc_if);
++static void wl_link_if(wl_info_t *wl, wl_if_t *wlif);
+ static void wl_free_if(wl_info_t *wl, wl_if_t *wlif);
+@@ -566,6 +567,9 @@ wl_attach(uint16 vendor, uint16 device,
+       wl->dev = dev;
+       wl_if_setup(dev);
++      /* add the interface to the interface linked list */
++      wl_link_if(wl, wlif);
++
+       /* map chip registers (47xx: and sprom) */
+       dev->base_addr = regs;
+@@ -1106,10 +1110,14 @@ wl_free(wl_info_t *wl)
+                       free_irq(wl->dev->irq, wl);
+       }
+-      if (wl->dev) {
+-              wl_free_if(wl, WL_DEV_IF(wl->dev));
+-              wl->dev = NULL;
++      /* free all interfaces */
++      while (wl->if_list) {
++              if ((wl->if_list->dev != wl->dev) || wl->if_list->next == NULL)
++                      wl_free_if(wl, wl->if_list);
++              else
++                      wl_free_if(wl, wl->if_list->next);
+       }
++      wl->dev = NULL;
+ #ifdef TOE
+       wl_toe_detach(wl->toei);
+@@ -1355,10 +1363,12 @@ wl_txflowcontrol(wl_info_t *wl, bool sta
+       ASSERT(prio == ALLPRIO);
+       for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) {
+-              if (state == ON)
+-                      netif_stop_queue(wlif->dev);
+-              else
+-                      netif_wake_queue(wlif->dev);
++              if (wlif->dev_registed) {
++                      if (state == ON)
++                              netif_stop_queue(wlif->dev);
++                      else
++                              netif_wake_queue(wlif->dev);
++              }
+       }
+ }
+@@ -1398,7 +1408,6 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+ {
+       struct net_device *dev;
+       wl_if_t *wlif;
+-      wl_if_t *p;
+       dev = alloc_etherdev(sizeof(wl_if_t));
+       wlif = netdev_priv(dev);
+@@ -1411,9 +1420,13 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+       wlif->wlcif = wlcif;
+       wlif->subunit = subunit;
+-      /* match current flow control state */
+-      if (iftype != WL_IFTYPE_MON && wl->dev && netif_queue_stopped(wl->dev))
+-              netif_stop_queue(dev);
++      return wlif;
++}
++
++static void
++wl_link_if(wl_info_t *wl, wl_if_t *wlif)
++{
++      wl_if_t *p;
+       /* add the interface to the interface linked list */
+       if (wl->if_list == NULL)
+@@ -1424,7 +1437,6 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+                       p = p->next;
+               p->next = wlif;
+       }
+-      return wlif;
+ }
+ static void
+@@ -1504,6 +1516,9 @@ _wl_add_if(wl_task_t *task)
+       wl_info_t *wl = wlif->wl;
+       struct net_device *dev = wlif->dev;
++      /* add the interface to the interface linked list */
++      wl_link_if(wl, wlif);
++
+       if (wlif->type == WL_IFTYPE_WDS)
+               dev->netdev_ops = &wl_wds_ops;
+@@ -1516,6 +1531,14 @@ _wl_add_if(wl_task_t *task)
+       }
+       wlif->dev_registed = TRUE;
++      /* match current flow control state */
++      if (wl->dev) {
++              if (netif_queue_stopped(wl->dev))
++                      netif_stop_queue(dev);
++              else
++                      netif_wake_queue(dev);
++      }
++
+ done:
+       MFREE(wl->osh, task, sizeof(wl_task_t));
+       atomic_dec(&wl->callbacks);
+@@ -1545,6 +1568,8 @@ wl_add_if(wl_info_t *wl, struct wlc_if*
                return NULL;
        }
  
        sprintf(wlif->dev->name, "%s%d.%d", devname, wl->pub->unit, wlif->subunit);
        if (remote)
                bcopy(remote, &wlif->remote, ETHER_ADDR_LEN);
+@@ -2778,6 +2803,9 @@ wl_add_monitor(wl_task_t *task)
+       dev = wlif->dev;
+       wl->monitor = dev;
++      /* add the interface to the interface linked list */
++      wl_link_if(wl, wlif);
++
+       /* override some fields */
+       sprintf(dev->name, "prism%d", wl->pub->unit);
+       bcopy(wl->dev->dev_addr, dev->dev_addr, ETHER_ADDR_LEN);