mac80211: add pending brcmfmac patches fixing multiple interfaces
authorrmilecki <rmilecki@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 26 Aug 2015 22:10:14 +0000 (22:10 +0000)
committerrmilecki <rmilecki@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Wed, 26 Aug 2015 22:10:14 +0000 (22:10 +0000)
So far support for multiple interface was somehow broken in brcmfmac.
Driver couldn't correctly match firmware and system interfaces resulting
in not working APs and WARNINGs. This pending patches fixes that :)

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46734 3c298f89-4303-0410-b956-a3cf2f4a3e73

12 files changed:
package/kernel/mac80211/patches/319-brcmfmac-consolidate-ifp-lookup-in-driver-core.patch [new file with mode: 0644]
package/kernel/mac80211/patches/320-brcmfmac-make-brcmf_proto_hdrpull-return-struct-brcm.patch [new file with mode: 0644]
package/kernel/mac80211/patches/321-brcmfmac-change-parameters-for-brcmf_remove_interfac.patch [new file with mode: 0644]
package/kernel/mac80211/patches/322-brcmfmac-only-call-brcmf_cfg80211_detach-when-attach.patch [new file with mode: 0644]
package/kernel/mac80211/patches/323-brcmfmac-correct-detection-of-p2pdev-interface-event.patch [new file with mode: 0644]
package/kernel/mac80211/patches/324-brcmfmac-use-brcmf_get_ifp-to-map-ifidx-to-struct-br.patch [new file with mode: 0644]
package/kernel/mac80211/patches/325-brcmfmac-pass-struct-brcmf_if-instance-in-brcmf_txfi.patch [new file with mode: 0644]
package/kernel/mac80211/patches/326-brcmfmac-add-mapping-for-interface-index-to-bsscfg-i.patch [new file with mode: 0644]
package/kernel/mac80211/patches/327-brcmfmac-add-dedicated-debug-level-for-firmware-cons.patch [new file with mode: 0644]
package/kernel/mac80211/patches/328-brcmfmac-remove-ifidx-parameter-from-brcmf_fws_txsta.patch [new file with mode: 0644]
package/kernel/mac80211/patches/329-brcmfmac-change-prototype-for-brcmf_fws_hdrpull.patch [new file with mode: 0644]
package/kernel/mac80211/patches/330-brcmfmac-introduce-brcmf_net_detach-function.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/319-brcmfmac-consolidate-ifp-lookup-in-driver-core.patch b/package/kernel/mac80211/patches/319-brcmfmac-consolidate-ifp-lookup-in-driver-core.patch
new file mode 100644 (file)
index 0000000..97444b3
--- /dev/null
@@ -0,0 +1,138 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:53 +0200
+Subject: [PATCH] brcmfmac: consolidate ifp lookup in driver core
+
+In rx path the firmware provide an interface index which is used to
+map to a struct brcmf_if instance. However, this involves some trick
+that is done in two places. This is changed by having driver core
+providing brcmf_get_ifp() function.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -276,6 +276,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+                        struct sk_buff *pktbuf)
+ {
+       struct brcmf_proto_bcdc_header *h;
++      struct brcmf_if *ifp;
+       brcmf_dbg(BCDC, "Enter\n");
+@@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       trace_brcmf_bcdchdr(pktbuf->data);
+       h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+-      *ifidx = BCDC_GET_IF_IDX(h);
+-      if (*ifidx >= BRCMF_MAX_IFS) {
+-              brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
++      ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
++      if (IS_ERR_OR_NULL(ifp)) {
++              brcmf_dbg(INFO, "no matching ifp found\n");
+               return -EBADE;
+       }
+-      /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+-       * events this is easy because it contains the bssidx which maps
+-       * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+-       * bssidx 1 is used for p2p0 and no data can be received or
+-       * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+-       */
+-      if (*ifidx)
+-              (*ifidx)++;
+-
+       if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+           BCDC_PROTO_VER) {
+               brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+-                        brcmf_ifname(drvr, *ifidx), h->flags);
++                        brcmf_ifname(drvr, ifp->ifidx), h->flags);
+               return -EBADE;
+       }
+       if (h->flags & BCDC_FLAG_SUM_GOOD) {
+               brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+-                        brcmf_ifname(drvr, *ifidx), h->flags);
++                        brcmf_ifname(drvr, ifp->ifidx), h->flags);
+               pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+@@ -320,12 +312,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       skb_pull(pktbuf, BCDC_HEADER_LEN);
+       if (do_fws)
+-              brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
++              brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
++                                pktbuf);
+       else
+               skb_pull(pktbuf, h->data_offset << 2);
+       if (pktbuf->len == 0)
+               return -ENODATA;
++
++      *ifidx = ifp->ifidx;
+       return 0;
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -83,6 +83,25 @@ char *brcmf_ifname(struct brcmf_pub *drv
+       return "<if_none>";
+ }
++struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
++{
++      if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
++              brcmf_err("ifidx %d out of range\n", ifidx);
++              return ERR_PTR(-ERANGE);
++      }
++
++      /* The ifidx is the idx to map to matching netdev/ifp. When receiving
++       * events this is easy because it contains the bssidx which maps
++       * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
++       * bssidx 1 is used for p2p0 and no data can be received or
++       * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
++       */
++      if (ifidx)
++              ifidx++;
++
++      return drvr->iflist[ifidx];
++}
++
+ static void _brcmf_set_multicast_list(struct work_struct *work)
+ {
+       struct brcmf_if *ifp;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -202,7 +202,7 @@ int brcmf_netdev_wait_pend8021x(struct b
+ /* Return pointer to interface name */
+ char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+-
++struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+                             char *name, u8 *mac_addr);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -1081,16 +1081,8 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
+ {
+       struct brcmf_if *ifp;
+-      /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+-       * events this is easy because it contains the bssidx which maps
+-       * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+-       * bssidx 1 is used for p2p0 and no data can be received or
+-       * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+-       */
+-      if (ifidx)
+-              (ifidx)++;
+-      ifp = msgbuf->drvr->iflist[ifidx];
+-      if (!ifp || !ifp->ndev) {
++      ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
++      if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
+               brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+               brcmu_pkt_buf_free_skb(skb);
+               return;
diff --git a/package/kernel/mac80211/patches/320-brcmfmac-make-brcmf_proto_hdrpull-return-struct-brcm.patch b/package/kernel/mac80211/patches/320-brcmfmac-make-brcmf_proto_hdrpull-return-struct-brcm.patch
new file mode 100644 (file)
index 0000000..632714c
--- /dev/null
@@ -0,0 +1,222 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:54 +0200
+Subject: [PATCH] brcmfmac: make brcmf_proto_hdrpull() return struct
+ brcmf_if instance
+
+Avoid spreading the ifidx in the driver, but have it return the
+struct brcmf_if instance.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -272,11 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pu
+ }
+ static int
+-brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
+-                       struct sk_buff *pktbuf)
++brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
++                       struct sk_buff *pktbuf, struct brcmf_if **ifp)
+ {
+       struct brcmf_proto_bcdc_header *h;
+-      struct brcmf_if *ifp;
++      struct brcmf_if *tmp_if;
+       brcmf_dbg(BCDC, "Enter\n");
+@@ -290,21 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       trace_brcmf_bcdchdr(pktbuf->data);
+       h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+-      ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
+-      if (IS_ERR_OR_NULL(ifp)) {
++      tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
++      if (!tmp_if) {
+               brcmf_dbg(INFO, "no matching ifp found\n");
+               return -EBADE;
+       }
+       if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+           BCDC_PROTO_VER) {
+               brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+-                        brcmf_ifname(drvr, ifp->ifidx), h->flags);
++                        brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+               return -EBADE;
+       }
+       if (h->flags & BCDC_FLAG_SUM_GOOD) {
+               brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+-                        brcmf_ifname(drvr, ifp->ifidx), h->flags);
++                        brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+               pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+@@ -312,7 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       skb_pull(pktbuf, BCDC_HEADER_LEN);
+       if (do_fws)
+-              brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
++              brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
+                                 pktbuf);
+       else
+               skb_pull(pktbuf, h->data_offset << 2);
+@@ -320,7 +320,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       if (pktbuf->len == 0)
+               return -ENODATA;
+-      *ifidx = ifp->ifidx;
++      *ifp = tmp_if;
+       return 0;
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -87,7 +87,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
+ {
+       if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+               brcmf_err("ifidx %d out of range\n", ifidx);
+-              return ERR_PTR(-ERANGE);
++              return NULL;
+       }
+       /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+@@ -539,17 +539,15 @@ void brcmf_rx_frame(struct device *dev,
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pub *drvr = bus_if->drvr;
+       struct brcmf_skb_reorder_data *rd;
+-      u8 ifidx;
+       int ret;
+       brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+       /* process and remove protocol-specific header */
+-      ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
+-      ifp = drvr->iflist[ifidx];
++      ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
+       if (ret || !ifp || !ifp->ndev) {
+-              if ((ret != -ENODATA) && ifp)
++              if (ret != -ENODATA && ifp)
+                       ifp->stats.rx_errors++;
+               brcmu_pkt_buf_free_skb(skb);
+               return;
+@@ -592,17 +590,17 @@ void brcmf_txcomplete(struct device *dev
+ {
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pub *drvr = bus_if->drvr;
+-      u8 ifidx;
++      struct brcmf_if *ifp;
+       /* await txstatus signal for firmware if active */
+       if (brcmf_fws_fc_active(drvr->fws)) {
+               if (!success)
+                       brcmf_fws_bustxfail(drvr->fws, txp);
+       } else {
+-              if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
++              if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
+                       brcmu_pkt_buf_free_skb(txp);
+               else
+-                      brcmf_txfinalize(drvr, txp, ifidx, success);
++                      brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
+       }
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+       struct sk_buff *skb;
+       struct brcmf_skbuff_cb *skcb;
+       struct brcmf_fws_mac_descriptor *entry = NULL;
+-      u8 ifidx;
++      struct brcmf_if *ifp;
+       brcmf_dbg(DATA, "flags %d\n", flags);
+@@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+       }
+       brcmf_fws_macdesc_return_req_credit(skb);
+-      if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
++      ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
++      if (ret) {
+               brcmu_pkt_buf_free_skb(skb);
+               return -EINVAL;
+       }
+       if (!remove_from_hanger)
+-              ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
++              ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
+                                                   genbit, seq);
+       if (remove_from_hanger || ret)
+-              brcmf_txfinalize(fws->drvr, skb, ifidx, true);
++              brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
+       return 0;
+ }
+@@ -1848,7 +1849,7 @@ static int brcmf_fws_commit_skb(struct b
+               entry->transit_count--;
+               if (entry->suppressed)
+                       entry->suppr_transit_count--;
+-              brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
++              (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
+               goto rollback;
+       }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct
+ static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+-                              u8 *ifidx, struct sk_buff *skb)
++                              struct sk_buff *skb, struct brcmf_if **ifp)
+ {
+       return -ENODEV;
+ }
+@@ -1082,7 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
+       struct brcmf_if *ifp;
+       ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+-      if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
++      if (!ifp || !ifp->ndev) {
+               brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+               brcmu_pkt_buf_free_skb(skb);
+               return;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h
+@@ -24,8 +24,8 @@ enum proto_addr_mode {
+ struct brcmf_proto {
+-      int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
+-                     struct sk_buff *skb);
++      int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
++                     struct sk_buff *skb, struct brcmf_if **ifp);
+       int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
+                         void *buf, uint len);
+       int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+@@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub
+ void brcmf_proto_detach(struct brcmf_pub *drvr);
+ static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+-                                    u8 *ifidx, struct sk_buff *skb)
++                                    struct sk_buff *skb,
++                                    struct brcmf_if **ifp)
+ {
+-      return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb);
++      struct brcmf_if *tmp = NULL;
++
++      /* assure protocol is always called with
++       * non-null initialized pointer.
++       */
++      if (ifp)
++              *ifp = NULL;
++      else
++              ifp = &tmp;
++      return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
+ }
+ static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+                                        uint cmd, void *buf, uint len)
diff --git a/package/kernel/mac80211/patches/321-brcmfmac-change-parameters-for-brcmf_remove_interfac.patch b/package/kernel/mac80211/patches/321-brcmfmac-change-parameters-for-brcmf_remove_interfac.patch
new file mode 100644 (file)
index 0000000..3360cbc
--- /dev/null
@@ -0,0 +1,87 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:55 +0200
+Subject: [PATCH] brcmfmac: change parameters for
+ brcmf_remove_interface()
+
+Just pass the interface to be removed, ie. the struct brcmf_if instance.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -4983,7 +4983,7 @@ brcmf_notify_connect_status_ap(struct br
+               brcmf_dbg(CONN, "AP mode link down\n");
+               complete(&cfg->vif_disabled);
+               if (ifp->vif->mbss)
+-                      brcmf_remove_interface(ifp->drvr, ifp->bssidx);
++                      brcmf_remove_interface(ifp);
+               return 0;
+       }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -887,12 +887,13 @@ static void brcmf_del_if(struct brcmf_pu
+       }
+ }
+-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
++void brcmf_remove_interface(struct brcmf_if *ifp)
+ {
+-      if (drvr->iflist[bssidx]) {
+-              brcmf_fws_del_interface(drvr->iflist[bssidx]);
+-              brcmf_del_if(drvr, bssidx);
+-      }
++      if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
++              return;
++
++      brcmf_fws_del_interface(ifp);
++      brcmf_del_if(ifp->drvr, ifp->bssidx);
+ }
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
+@@ -1122,7 +1123,7 @@ void brcmf_detach(struct device *dev)
+       /* make sure primary interface removed last */
+       for (i = BRCMF_MAX_IFS-1; i > -1; i--)
+-              brcmf_remove_interface(drvr, i);
++              brcmf_remove_interface(drvr->iflist[i]);
+       brcmf_cfg80211_detach(drvr->config);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -206,7 +206,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+                             char *name, u8 *mac_addr);
+-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
++void brcmf_remove_interface(struct brcmf_if *ifp);
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+                         enum brcmf_netif_stop_reason reason, bool state);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -222,7 +222,7 @@ static void brcmf_fweh_handle_if_event(s
+       err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
+       if (ifp && ifevent->action == BRCMF_E_IF_DEL)
+-              brcmf_remove_interface(drvr, ifevent->bssidx);
++              brcmf_remove_interface(ifp);
+ }
+ /**
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(stru
+ {
+       cfg80211_unregister_wdev(&vif->wdev);
+       p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+-      brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
++      brcmf_remove_interface(vif->ifp);
+       brcmf_free_vif(vif);
+ }
diff --git a/package/kernel/mac80211/patches/322-brcmfmac-only-call-brcmf_cfg80211_detach-when-attach.patch b/package/kernel/mac80211/patches/322-brcmfmac-only-call-brcmf_cfg80211_detach-when-attach.patch
new file mode 100644 (file)
index 0000000..2b61f4e
--- /dev/null
@@ -0,0 +1,92 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:56 +0200
+Subject: [PATCH] brcmfmac: only call brcmf_cfg80211_detach() when attach
+ was successful
+
+In brcmf_bus_start() the function brcmf_cfg80211_attach() is called which
+may fail. If this happens we should not call brcmf_cfg80211_detach() in
+the failure path as it will result in NULL pointer dereference:
+
+  brcmf_fweh_activate_events: Set event_msgs error (-5)
+  brcmf_bus_start: failed: -5
+  brcmf_sdio_firmware_callback: dongle is not responding
+  BUG: unable to handle kernel NULL pointer dereference at 0000000000000068
+  IP: [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+  PGD 0
+  Oops: 0000 [#1] SMP
+  Modules linked in: brcmfmac(O) brcmutil(O) cfg80211 auth_rpcgss
+  CPU: 1 PID: 45 Comm: kworker/1:1 Tainted: G           O
+  Hardware name: Dell Inc. Latitude E6410/07XJP9, BIOS A07 02/15/2011
+  Workqueue: events request_firmware_work_func
+  task: ffff880036c09ac0 ti: ffff880036dd4000 task.ti: ffff880036dd4000
+  RIP: 0010:[<ffffffff811e8f08>]  [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+  RSP: 0018:ffff880036dd7a28  EFLAGS: 00010246
+  RAX: ffff880036c09ac0 RBX: 0000000000000000 RCX: 000000007fffffff
+  RDX: 0000000000000000 RSI: ffffffff816578b9 RDI: 0000000000000000
+  RBP: ffff880036dd7a48 R08: 0000000000000000 R09: ffff880036c0b340
+  R10: 00000000000002ec R11: ffff880036dd7b08 R12: ffffffff816578b9
+  R13: 0000000000000000 R14: ffffffff816578b9 R15: ffff8800c6c87000
+  FS:  0000000000000000(0000) GS:ffff88012bc40000(0000) knlGS:0000000000000000
+  CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
+  CR2: 0000000000000068 CR3: 0000000001a0b000 CR4: 00000000000006e0
+  Stack:
+   0000000000000000 ffffffff816578b9 0000000000000000 ffff8800c0d003c8
+   ffff880036dd7a78 ffffffff811e8ff5 0000000ffffffff1 ffffffff81a9b060
+   ffff8800c789f880 ffff8800c0d00000 ffff880036dd7a98 ffffffff811ebe0d
+  Call Trace:
+   [<ffffffff811e8ff5>] kernfs_find_and_get_ns+0x35/0x60
+   [<ffffffff811ebe0d>] sysfs_unmerge_group+0x1d/0x60
+   [<ffffffff81404ef2>] dpm_sysfs_remove+0x22/0x60
+   [<ffffffff813f9db9>] device_del+0x49/0x240
+   [<ffffffff815da768>] rfkill_unregister+0x58/0xc0
+   [<ffffffffa06bd91b>] wiphy_unregister+0xab/0x2f0 [cfg80211]
+   [<ffffffffa0742fe3>] brcmf_cfg80211_detach+0x23/0x50 [brcmfmac]
+   [<ffffffffa074d986>] brcmf_detach+0x86/0xe0 [brcmfmac]
+   [<ffffffffa0757de8>] brcmf_sdio_remove+0x48/0x120 [brcmfmac]
+   [<ffffffffa0758ed9>] brcmf_sdiod_remove+0x29/0xd0 [brcmfmac]
+   [<ffffffffa0759031>] brcmf_ops_sdio_remove+0xb1/0x110 [brcmfmac]
+   [<ffffffffa001c267>] sdio_bus_remove+0x37/0x100 [mmc_core]
+   [<ffffffff813fe026>] __device_release_driver+0x96/0x130
+   [<ffffffff813fe0e3>] device_release_driver+0x23/0x30
+   [<ffffffffa0754bc8>] brcmf_sdio_firmware_callback+0x2a8/0x5d0 [brcmfmac]
+   [<ffffffffa074deaf>] brcmf_fw_request_nvram_done+0x15f/0x5e0 [brcmfmac]
+   [<ffffffff8140142f>] ? devres_add+0x3f/0x50
+   [<ffffffff810642b5>] ? usermodehelper_read_unlock+0x15/0x20
+   [<ffffffff81400000>] ? platform_match+0x70/0xa0
+   [<ffffffff8140f400>] request_firmware_work_func+0x30/0x60
+   [<ffffffff8106828c>] process_one_work+0x14c/0x3d0
+   [<ffffffff8106862a>] worker_thread+0x11a/0x450
+   [<ffffffff81068510>] ? process_one_work+0x3d0/0x3d0
+   [<ffffffff8106d692>] kthread+0xd2/0xf0
+   [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
+   [<ffffffff815ed35f>] ret_from_fork+0x3f/0x70
+   [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
+  Code: e9 40 fe ff ff 48 89 d8 eb 87 66 0f 1f 84 00 00 00 00 00 66 66 66 66
+       90 55 48 89 e5 41 56 49 89 f6 41 55 49 89 d5 31 d2 41 54 53 <0f> b7
+       47 68 48 8b 5f 48 66 c1 e8 05 83 e0 01 4d 85 ed 0f b6 c8
+  RIP  [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+   RSP <ffff880036dd7a28>
+  CR2: 0000000000000068
+  ---[ end trace 87d6ec0d3fe46740 ]---
+
+Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -1049,7 +1049,10 @@ int brcmf_bus_start(struct device *dev)
+ fail:
+       if (ret < 0) {
+               brcmf_err("failed: %d\n", ret);
+-              brcmf_cfg80211_detach(drvr->config);
++              if (drvr->config) {
++                      brcmf_cfg80211_detach(drvr->config);
++                      drvr->config = NULL;
++              }
+               if (drvr->fws) {
+                       brcmf_fws_del_interface(ifp);
+                       brcmf_fws_deinit(drvr);
diff --git a/package/kernel/mac80211/patches/323-brcmfmac-correct-detection-of-p2pdev-interface-event.patch b/package/kernel/mac80211/patches/323-brcmfmac-correct-detection-of-p2pdev-interface-event.patch
new file mode 100644 (file)
index 0000000..868b0a8
--- /dev/null
@@ -0,0 +1,105 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:57 +0200
+Subject: [PATCH] brcmfmac: correct detection of p2pdev interface event
+
+The p2pdev interface is setup in firmware resulting in a interface
+event. This event has role and no-if flag. When role is p2p client
+and no-if flag is set it indicates that this is the p2pdev interface.
+This info is used in handling the event and adding interface in the
+driver.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -795,7 +795,7 @@ fail:
+ }
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+-                            char *name, u8 *mac_addr)
++                            bool is_p2pdev, char *name, u8 *mac_addr)
+ {
+       struct brcmf_if *ifp;
+       struct net_device *ndev;
+@@ -821,7 +821,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+               }
+       }
+-      if (!brcmf_p2p_enable && bssidx == 1) {
++      if (!brcmf_p2p_enable && is_p2pdev) {
+               /* this is P2P_DEVICE interface */
+               brcmf_dbg(INFO, "allocate non-netdev interface\n");
+               ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+@@ -999,12 +999,12 @@ int brcmf_bus_start(struct device *dev)
+       brcmf_dbg(TRACE, "\n");
+       /* add primary networking interface */
+-      ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
++      ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
+       if (IS_ERR(ifp))
+               return PTR_ERR(ifp);
+       if (brcmf_p2p_enable)
+-              p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL);
++              p2p_ifp = brcmf_add_if(drvr, 1, 0, false, "p2p%d", NULL);
+       else
+               p2p_ifp = NULL;
+       if (IS_ERR(p2p_ifp))
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -205,7 +205,7 @@ char *brcmf_ifname(struct brcmf_pub *drv
+ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+-                            char *name, u8 *mac_addr);
++                            bool is_p2pdev, char *name, u8 *mac_addr);
+ void brcmf_remove_interface(struct brcmf_if *ifp);
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -179,6 +179,7 @@ static void brcmf_fweh_handle_if_event(s
+ {
+       struct brcmf_if_event *ifevent = data;
+       struct brcmf_if *ifp;
++      bool is_p2pdev;
+       int err = 0;
+       brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
+@@ -186,18 +187,16 @@ static void brcmf_fweh_handle_if_event(s
+                 ifevent->flags, ifevent->role);
+       /* The P2P Device interface event must not be ignored
+-       * contrary to what firmware tells us. The only way to
+-       * distinguish the P2P Device is by looking at the ifidx
+-       * and bssidx received.
++       * contrary to what firmware tells us.
+        */
+-      if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) &&
+-          (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
++      is_p2pdev = (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
++                  ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT;
++      if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
+               brcmf_dbg(EVENT, "event can be ignored\n");
+               return;
+       }
+       if (ifevent->ifidx >= BRCMF_MAX_IFS) {
+-              brcmf_err("invalid interface index: %u\n",
+-                        ifevent->ifidx);
++              brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
+               return;
+       }
+@@ -207,7 +206,7 @@ static void brcmf_fweh_handle_if_event(s
+               brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
+                         emsg->addr);
+               ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
+-                                 emsg->ifname, emsg->addr);
++                                 is_p2pdev, emsg->ifname, emsg->addr);
+               if (IS_ERR(ifp))
+                       return;
+               brcmf_fws_add_interface(ifp);
diff --git a/package/kernel/mac80211/patches/324-brcmfmac-use-brcmf_get_ifp-to-map-ifidx-to-struct-br.patch b/package/kernel/mac80211/patches/324-brcmfmac-use-brcmf_get_ifp-to-map-ifidx-to-struct-br.patch
new file mode 100644 (file)
index 0000000..abd6681
--- /dev/null
@@ -0,0 +1,126 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:58 +0200
+Subject: [PATCH] brcmfmac: use brcmf_get_ifp() to map ifidx to struct
+ brcmf_if instance
+
+The knowledge on how to map the interface index to a struct brcmf_if
+instance is in brcmf_get_ifp() so use that function when only the
+interface index is known instead of accessing brcmf_pub::iflist
+directly.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+@@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(stru
+ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
+                                   bool trump_sco)
+ {
+-      struct brcmf_if *ifp = btci->cfg->pub->iflist[0];
++      struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
+       if (trump_sco && !btci->saved_regs_part2) {
+               /* this should reduce eSCO agressive
+@@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_c
+ {
+       struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+       struct brcmf_btcoex_info *btci = cfg->btcoex;
+-      struct brcmf_if *ifp = cfg->pub->iflist[0];
++      struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+       switch (mode) {
+       case BRCMF_BTCOEX_DISABLED:
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -6213,7 +6213,7 @@ static void brcmf_free_wiphy(struct wiph
+ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+                                                 struct device *busdev)
+ {
+-      struct net_device *ndev = drvr->iflist[0]->ndev;
++      struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
+       struct brcmf_cfg80211_info *cfg;
+       struct wiphy *wiphy;
+       struct brcmf_cfg80211_vif *vif;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+@@ -121,7 +121,7 @@ static void brcmf_feat_iovar_int_set(str
+ void brcmf_feat_attach(struct brcmf_pub *drvr)
+ {
+-      struct brcmf_if *ifp = drvr->iflist[0];
++      struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+       brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
+       brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
+--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+@@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct
+       bus_if = dev_get_drvdata(flow->dev);
+       drvr = bus_if->drvr;
+-      ifp = drvr->iflist[ifidx];
++      ifp = brcmf_get_ifp(drvr, ifidx);
+       brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);
+       spin_unlock_irqrestore(&flow->block_lock, flags);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -334,7 +334,7 @@ void brcmf_fweh_attach(struct brcmf_pub
+ void brcmf_fweh_detach(struct brcmf_pub *drvr)
+ {
+       struct brcmf_fweh_info *fweh = &drvr->fweh;
+-      struct brcmf_if *ifp = drvr->iflist[0];
++      struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+       s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+       if (ifp) {
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -972,7 +972,7 @@ static void
+ brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
+                            u8 if_id)
+ {
+-      struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
++      struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
+       if (WARN_ON(!ifp))
+               return;
+@@ -2118,6 +2118,7 @@ static int brcmf_debugfs_fws_stats_read(
+ int brcmf_fws_init(struct brcmf_pub *drvr)
+ {
+       struct brcmf_fws_info *fws;
++      struct brcmf_if *ifp;
+       u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
+       int rc;
+       u32 mode;
+@@ -2177,21 +2178,22 @@ int brcmf_fws_init(struct brcmf_pub *drv
+        * continue. Set mode back to none indicating not enabled.
+        */
+       fws->fw_signals = true;
+-      if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
++      ifp = brcmf_get_ifp(drvr, 0);
++      if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
+               brcmf_err("failed to set bdcv2 tlv signaling\n");
+               fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+               fws->fw_signals = false;
+       }
+-      if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
++      if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
+               brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+       /* Enable seq number reuse, if supported */
+-      if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) {
++      if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
+               if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
+                       mode = 0;
+                       BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
+-                      if (brcmf_fil_iovar_int_set(drvr->iflist[0],
++                      if (brcmf_fil_iovar_int_set(ifp,
+                                                   "wlfc_mode", mode) == 0) {
+                               BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
+                       }
diff --git a/package/kernel/mac80211/patches/325-brcmfmac-pass-struct-brcmf_if-instance-in-brcmf_txfi.patch b/package/kernel/mac80211/patches/325-brcmfmac-pass-struct-brcmf_if-instance-in-brcmf_txfi.patch
new file mode 100644 (file)
index 0000000..23a7b6f
--- /dev/null
@@ -0,0 +1,122 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:59 +0200
+Subject: [PATCH] brcmfmac: pass struct brcmf_if instance in
+ brcmf_txfinalize()
+
+Most call sites of brcmf_txfinalize already have struct brcmf_if
+instance so pass that to brcmf_txfinalize() as the function
+needs it anyway.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -560,17 +560,11 @@ void brcmf_rx_frame(struct device *dev,
+               brcmf_netif_rx(ifp, skb);
+ }
+-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
+-                    bool success)
++void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+ {
+-      struct brcmf_if *ifp;
+       struct ethhdr *eh;
+       u16 type;
+-      ifp = drvr->iflist[ifidx];
+-      if (!ifp)
+-              goto done;
+-
+       eh = (struct ethhdr *)(txp->data);
+       type = ntohs(eh->h_proto);
+@@ -582,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *
+       if (!success)
+               ifp->stats.tx_errors++;
+-done:
++
+       brcmu_pkt_buf_free_skb(txp);
+ }
+@@ -600,7 +594,7 @@ void brcmf_txcomplete(struct device *dev
+               if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
+                       brcmu_pkt_buf_free_skb(txp);
+               else
+-                      brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
++                      brcmf_txfinalize(ifp, txp, success);
+       }
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -210,8 +210,7 @@ void brcmf_remove_interface(struct brcmf
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+                         enum brcmf_netif_stop_reason reason, bool state);
+-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
+-                    bool success);
++void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+ void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+ /* Sets dongle media info (drv_version, mac address). */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1506,7 +1506,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
+                                                   genbit, seq);
+       if (remove_from_hanger || ret)
+-              brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
++              brcmf_txfinalize(ifp, skb, true);
+       return 0;
+ }
+@@ -1905,7 +1905,7 @@ int brcmf_fws_process_skb(struct brcmf_i
+       if (fws->avoid_queueing) {
+               rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
+               if (rc < 0)
+-                      brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
++                      brcmf_txfinalize(ifp, skb, false);
+               return rc;
+       }
+@@ -1929,7 +1929,7 @@ int brcmf_fws_process_skb(struct brcmf_i
+               brcmf_fws_schedule_deq(fws);
+       } else {
+               brcmf_err("drop skb: no hanger slot\n");
+-              brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
++              brcmf_txfinalize(ifp, skb, false);
+               rc = -ENOMEM;
+       }
+       brcmf_fws_unlock(fws);
+@@ -2009,8 +2009,9 @@ static void brcmf_fws_dequeue_worker(str
+                               ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
+                               brcmf_fws_lock(fws);
+                               if (ret < 0)
+-                                      brcmf_txfinalize(drvr, skb, ifidx,
+-                                                       false);
++                                      brcmf_txfinalize(brcmf_get_ifp(drvr,
++                                                                     ifidx),
++                                                       skb, false);
+                               if (fws->bus_flow_blocked)
+                                       break;
+                       }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -873,7 +873,11 @@ brcmf_msgbuf_process_txstatus(struct brc
+       commonring = msgbuf->flowrings[flowid];
+       atomic_dec(&commonring->outstanding_tx);
+-      brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
++      /* Hante: i believe this was a bug as tx_status->msg.ifidx was used
++       * in brcmf_txfinalize as index in drvr->iflist. Can you confirm/deny?
++       */
++      brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
++                       skb, true);
+ }
diff --git a/package/kernel/mac80211/patches/326-brcmfmac-add-mapping-for-interface-index-to-bsscfg-i.patch b/package/kernel/mac80211/patches/326-brcmfmac-add-mapping-for-interface-index-to-bsscfg-i.patch
new file mode 100644 (file)
index 0000000..8ddc0a6
--- /dev/null
@@ -0,0 +1,92 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:00 +0200
+Subject: [PATCH] brcmfmac: add mapping for interface index to bsscfg
+ index
+
+Because the P2P Device interface in firmware uses the same interface
+index as the primary interface we use the bsscfg index as index in the
+struct brcmf_pub::iflist. However, in the data path we get the interface
+index and not the bsscfg index. So we need a mapping of interface index
+to bsscfg index, which can be determined upon handle adding the interface.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -85,21 +85,20 @@ char *brcmf_ifname(struct brcmf_pub *drv
+ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
+ {
++      struct brcmf_if *ifp;
++      s32 bssidx;
++
+       if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+               brcmf_err("ifidx %d out of range\n", ifidx);
+               return NULL;
+       }
+-      /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+-       * events this is easy because it contains the bssidx which maps
+-       * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+-       * bssidx 1 is used for p2p0 and no data can be received or
+-       * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+-       */
+-      if (ifidx)
+-              ifidx++;
++      ifp = NULL;
++      bssidx = drvr->if2bss[ifidx];
++      if (bssidx >= 0)
++              ifp = drvr->iflist[bssidx];
+-      return drvr->iflist[ifidx];
++      return ifp;
+ }
+ static void _brcmf_set_multicast_list(struct work_struct *work)
+@@ -831,6 +830,8 @@ struct brcmf_if *brcmf_add_if(struct brc
+               ifp = netdev_priv(ndev);
+               ifp->ndev = ndev;
++              /* store mapping ifidx to bssidx */
++              drvr->if2bss[ifidx] = bssidx;
+       }
+       ifp->drvr = drvr;
+@@ -855,6 +856,7 @@ static void brcmf_del_if(struct brcmf_pu
+       struct brcmf_if *ifp;
+       ifp = drvr->iflist[bssidx];
++      drvr->if2bss[ifp->ifidx] = -1;
+       drvr->iflist[bssidx] = NULL;
+       if (!ifp) {
+               brcmf_err("Null interface, idx=%d\n", bssidx);
+@@ -862,6 +864,7 @@ static void brcmf_del_if(struct brcmf_pu
+       }
+       brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
+       if (ifp->ndev) {
++              drvr->if2bss[ifp->ifidx] = -1;
+               if (bssidx == 0) {
+                       if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+                               rtnl_lock();
+@@ -926,6 +929,7 @@ int brcmf_attach(struct device *dev)
+       if (!drvr)
+               return -ENOMEM;
++      memset(drvr->if2bss, 0xFF, sizeof(drvr->if2bss));
+       mutex_init(&drvr->proto_block);
+       /* Link to bus module */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -122,6 +122,7 @@ struct brcmf_pub {
+       struct mac_address addresses[BRCMF_MAX_IFS];
+       struct brcmf_if *iflist[BRCMF_MAX_IFS];
++      s32 if2bss[BRCMF_MAX_IFS];
+       struct mutex proto_block;
+       unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
diff --git a/package/kernel/mac80211/patches/327-brcmfmac-add-dedicated-debug-level-for-firmware-cons.patch b/package/kernel/mac80211/patches/327-brcmfmac-add-dedicated-debug-level-for-firmware-cons.patch
new file mode 100644 (file)
index 0000000..a0a798b
--- /dev/null
@@ -0,0 +1,103 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:01 +0200
+Subject: [PATCH] brcmfmac: add dedicated debug level for firmware
+ console logging
+
+Both PCIe and SDIO devices have the possibility to log the firmware
+console output in kernel log. For PCIe it is logged when PCIE debug
+level is enabled. For SDIO it is logged when user specifies a non-zero
+console interval through debugfs. This patch tries to make it a
+bit more consistent. The firmware console output is only logged when
+FWCON debug level is enabled.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
+@@ -37,6 +37,7 @@
+ #define BRCMF_SDIO_VAL                0x00020000
+ #define BRCMF_MSGBUF_VAL      0x00040000
+ #define BRCMF_PCIE_VAL                0x00080000
++#define BRCMF_FWCON_VAL               0x00100000
+ /* set default print format */
+ #undef pr_fmt
+@@ -78,6 +79,7 @@ do {                                                         \
+ #define BRCMF_GLOM_ON()               (brcmf_msg_level & BRCMF_GLOM_VAL)
+ #define BRCMF_EVENT_ON()      (brcmf_msg_level & BRCMF_EVENT_VAL)
+ #define BRCMF_FIL_ON()                (brcmf_msg_level & BRCMF_FIL_VAL)
++#define BRCMF_FWCON_ON()      (brcmf_msg_level & BRCMF_FWCON_VAL)
+ #else /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+@@ -90,6 +92,7 @@ do {                                                         \
+ #define BRCMF_GLOM_ON()               0
+ #define BRCMF_EVENT_ON()      0
+ #define BRCMF_FIL_ON()                0
++#define BRCMF_FWCON_ON()      0
+ #endif /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -644,7 +644,7 @@ static void brcmf_pcie_bus_console_init(
+       addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
+       console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
+-      brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n",
++      brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
+                 console->base_addr, console->buf_addr, console->bufsize);
+ }
+@@ -656,6 +656,9 @@ static void brcmf_pcie_bus_console_read(
+       u8 ch;
+       u32 newidx;
++      if (!BRCMF_FWCON_ON())
++              return;
++
+       console = &devinfo->shared.console;
+       addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
+       newidx = brcmf_pcie_read_tcm32(devinfo, addr);
+@@ -677,7 +680,7 @@ static void brcmf_pcie_bus_console_read(
+               }
+               if (ch == '\n') {
+                       console->log_str[console->log_idx] = 0;
+-                      brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
++                      pr_debug("CONSOLE: %s", console->log_str);
+                       console->log_idx = 0;
+               }
+       }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -123,6 +123,7 @@ struct rte_console {
+ #define BRCMF_FIRSTREAD       (1 << 6)
++#define BRCMF_CONSOLE 10      /* watchdog interval to poll console */
+ /* SBSDIO_DEVICE_CTL */
+@@ -3204,6 +3205,8 @@ static void brcmf_sdio_debugfs_create(st
+       if (IS_ERR_OR_NULL(dentry))
+               return;
++      bus->console_interval = BRCMF_CONSOLE;
++
+       brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+       brcmf_debugfs_add_entry(drvr, "counters",
+                               brcmf_debugfs_sdio_count_read);
+@@ -3613,7 +3616,7 @@ static void brcmf_sdio_bus_watchdog(stru
+       }
+ #ifdef DEBUG
+       /* Poll for console output periodically */
+-      if (bus->sdiodev->state == BRCMF_SDIOD_DATA &&
++      if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
+           bus->console_interval != 0) {
+               bus->console.count += BRCMF_WD_POLL_MS;
+               if (bus->console.count >= bus->console_interval) {
diff --git a/package/kernel/mac80211/patches/328-brcmfmac-remove-ifidx-parameter-from-brcmf_fws_txsta.patch b/package/kernel/mac80211/patches/328-brcmfmac-remove-ifidx-parameter-from-brcmf_fws_txsta.patch
new file mode 100644 (file)
index 0000000..53e7ede
--- /dev/null
@@ -0,0 +1,34 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:02 +0200
+Subject: [PATCH] brcmfmac: remove ifidx parameter from
+ brcmf_fws_txstatus_suppressed()
+
+The brcmf_fws_txstatus_suppressed() function prototype specifies an
+ifidx parameter which is not used within the function implementation.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1398,7 +1398,7 @@ done:
+ }
+ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
+-                                       struct sk_buff *skb, u8 ifidx,
++                                       struct sk_buff *skb,
+                                        u32 genbit, u16 seq)
+ {
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+@@ -1503,7 +1503,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+               return -EINVAL;
+       }
+       if (!remove_from_hanger)
+-              ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
++              ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+                                                   genbit, seq);
+       if (remove_from_hanger || ret)
+               brcmf_txfinalize(ifp, skb, true);
diff --git a/package/kernel/mac80211/patches/329-brcmfmac-change-prototype-for-brcmf_fws_hdrpull.patch b/package/kernel/mac80211/patches/329-brcmfmac-change-prototype-for-brcmf_fws_hdrpull.patch
new file mode 100644 (file)
index 0000000..bb05235
--- /dev/null
@@ -0,0 +1,97 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:03 +0200
+Subject: [PATCH] brcmfmac: change prototype for brcmf_fws_hdrpull()
+
+Instead of passing ifidx and drvr just pass struct brcmf_if pointer
+which holds both parameters.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -312,8 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+       skb_pull(pktbuf, BCDC_HEADER_LEN);
+       if (do_fws)
+-              brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
+-                                pktbuf);
++              brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
+       else
+               skb_pull(pktbuf, h->data_offset << 2);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1616,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_
+       return 0;
+ }
+-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+-                    struct sk_buff *skb)
++void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
+ {
+       struct brcmf_skb_reorder_data *rd;
+-      struct brcmf_fws_info *fws = drvr->fws;
++      struct brcmf_fws_info *fws = ifp->drvr->fws;
+       u8 *signal_data;
+       s16 data_len;
+       u8 type;
+@@ -1630,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
+       s32 err;
+       brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
+-                ifidx, skb->len, signal_len);
++                ifp->ifidx, skb->len, siglen);
+-      WARN_ON(signal_len > skb->len);
++      WARN_ON(siglen > skb->len);
+-      if (!signal_len)
+-              return 0;
++      if (!siglen)
++              return;
+       /* if flow control disabled, skip to packet data and leave */
+       if ((!fws) || (!fws->fw_signals)) {
+-              skb_pull(skb, signal_len);
+-              return 0;
++              skb_pull(skb, siglen);
++              return;
+       }
+       fws->stats.header_pulls++;
+-      data_len = signal_len;
++      data_len = siglen;
+       signal_data = skb->data;
+       status = BRCMF_FWS_RET_OK_NOSCHEDULE;
+@@ -1731,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
+       /* signalling processing result does
+        * not affect the actual ethernet packet.
+        */
+-      skb_pull(skb, signal_len);
++      skb_pull(skb, siglen);
+       /* this may be a signal-only packet
+        */
+       if (skb->len == 0)
+               fws->stats.header_only_pkt++;
+-
+-      return 0;
+ }
+ static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
+@@ -21,8 +21,7 @@
+ int brcmf_fws_init(struct brcmf_pub *drvr);
+ void brcmf_fws_deinit(struct brcmf_pub *drvr);
+ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
+-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+-                    struct sk_buff *skb);
++void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
+ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
+ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
diff --git a/package/kernel/mac80211/patches/330-brcmfmac-introduce-brcmf_net_detach-function.patch b/package/kernel/mac80211/patches/330-brcmfmac-introduce-brcmf_net_detach-function.patch
new file mode 100644 (file)
index 0000000..0651a2f
--- /dev/null
@@ -0,0 +1,99 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:04 +0200
+Subject: [PATCH] brcmfmac: introduce brcmf_net_detach() function
+
+In case of error during brcmf_bus_start() the network interfaces were
+freed using free_netdev(). However, the interfaces may have additional
+memory allocated which is not freed. The netdev has destructor set to
+brcmf_cfg80211_free_netdev() which frees the additional memory if
+allocated and call free_netdev(). The brcmf_net_detach() either calls
+brcmf_cfg80211_free_netdev() directly or uses unregister_netdev() when
+struct net_device::reg_state indicates the netdev was registered.
+
+Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -4747,7 +4747,8 @@ void brcmf_cfg80211_free_netdev(struct n
+       ifp = netdev_priv(ndev);
+       vif = ifp->vif;
+-      brcmf_free_vif(vif);
++      if (vif)
++              brcmf_free_vif(vif);
+       free_netdev(ndev);
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -718,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *if
+       }
+       brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+-
+-      ndev->destructor = brcmf_cfg80211_free_netdev;
+       return 0;
+ fail:
+@@ -729,6 +727,14 @@ fail:
+       return -EBADE;
+ }
++static void brcmf_net_detach(struct net_device *ndev)
++{
++      if (ndev->reg_state == NETREG_REGISTERED)
++              unregister_netdev(ndev);
++      else
++              brcmf_cfg80211_free_netdev(ndev);
++}
++
+ static int brcmf_net_p2p_open(struct net_device *ndev)
+ {
+       brcmf_dbg(TRACE, "Enter\n");
+@@ -805,8 +811,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+                         ifp->ndev->name);
+               if (ifidx) {
+                       netif_stop_queue(ifp->ndev);
+-                      unregister_netdev(ifp->ndev);
+-                      free_netdev(ifp->ndev);
++                      brcmf_net_detach(ifp->ndev);
+                       drvr->iflist[bssidx] = NULL;
+               } else {
+                       brcmf_err("ignore IF event\n");
+@@ -828,6 +833,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+               if (!ndev)
+                       return ERR_PTR(-ENOMEM);
++              ndev->destructor = brcmf_cfg80211_free_netdev;
+               ifp = netdev_priv(ndev);
+               ifp->ndev = ndev;
+               /* store mapping ifidx to bssidx */
+@@ -879,8 +885,7 @@ static void brcmf_del_if(struct brcmf_pu
+                       cancel_work_sync(&ifp->setmacaddr_work);
+                       cancel_work_sync(&ifp->multicast_work);
+               }
+-              /* unregister will take care of freeing it */
+-              unregister_netdev(ifp->ndev);
++              brcmf_net_detach(ifp->ndev);
+       }
+ }
+@@ -1056,11 +1061,11 @@ fail:
+                       brcmf_fws_deinit(drvr);
+               }
+               if (drvr->iflist[0]) {
+-                      free_netdev(ifp->ndev);
++                      brcmf_net_detach(ifp->ndev);
+                       drvr->iflist[0] = NULL;
+               }
+               if (p2p_ifp) {
+-                      free_netdev(p2p_ifp->ndev);
++                      brcmf_net_detach(p2p_ifp->ndev);
+                       drvr->iflist[1] = NULL;
+               }
+               return ret;