mac80211: brcmfmac: backport patches that were skipped previously #1
[openwrt/svn-archive/archive.git] / package / kernel / mac80211 / patches / 344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
diff --git a/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch b/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
new file mode 100644 (file)
index 0000000..888ad5b
--- /dev/null
@@ -0,0 +1,333 @@
+From: Franky Lin <frankyl@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:55 +0100
+Subject: [PATCH] brcmfmac: add neighbor discovery offload ip address table
+ configuration
+
+Configure ipv6 address for neighbor discovery offload ip table in
+firmware obtained through ipv6 address notification callback.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -456,7 +456,7 @@ send_key_to_dongle(struct brcmf_if *ifp,
+ }
+ static s32
+-brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
++brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
+ {
+       s32 err;
+       u32 mode;
+@@ -484,6 +484,15 @@ brcmf_configure_arp_offload(struct brcmf
+                                 enable, mode);
+       }
++      err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
++      if (err) {
++              brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
++                        enable, err);
++              err = 0;
++      } else
++              brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
++                        enable, mode);
++
+       return err;
+ }
+@@ -3543,7 +3552,7 @@ static s32 brcmf_cfg80211_resume(struct
+               brcmf_report_wowl_wakeind(wiphy, ifp);
+               brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+               brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+-              brcmf_configure_arp_offload(ifp, true);
++              brcmf_configure_arp_nd_offload(ifp, true);
+               brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+                                     cfg->wowl.pre_pmmode);
+               cfg->wowl.active = false;
+@@ -3567,7 +3576,7 @@ static void brcmf_configure_wowl(struct
+       brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+-      brcmf_configure_arp_offload(ifp, false);
++      brcmf_configure_arp_nd_offload(ifp, false);
+       brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+       brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
+@@ -4336,7 +4345,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi
+       if (!mbss) {
+               brcmf_set_mpc(ifp, 0);
+-              brcmf_configure_arp_offload(ifp, false);
++              brcmf_configure_arp_nd_offload(ifp, false);
+       }
+       /* find the RSN_IE */
+@@ -4482,7 +4491,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wi
+ exit:
+       if ((err) && (!mbss)) {
+               brcmf_set_mpc(ifp, 1);
+-              brcmf_configure_arp_offload(ifp, true);
++              brcmf_configure_arp_nd_offload(ifp, true);
+       }
+       return err;
+ }
+@@ -4540,7 +4549,7 @@ static int brcmf_cfg80211_stop_ap(struct
+                       brcmf_err("bss_enable config failed %d\n", err);
+       }
+       brcmf_set_mpc(ifp, 1);
+-      brcmf_configure_arp_offload(ifp, true);
++      brcmf_configure_arp_nd_offload(ifp, true);
+       clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
+       brcmf_net_setcarrier(ifp, false);
+@@ -6287,7 +6296,7 @@ static s32 brcmf_config_dongle(struct br
+       if (err)
+               goto default_conf_out;
+-      brcmf_configure_arp_offload(ifp, true);
++      brcmf_configure_arp_nd_offload(ifp, true);
+       cfg->dongle_up = true;
+ default_conf_out:
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -20,6 +20,8 @@
+ #include <linux/inetdevice.h>
+ #include <net/cfg80211.h>
+ #include <net/rtnetlink.h>
++#include <net/addrconf.h>
++#include <net/ipv6.h>
+ #include <brcmu_utils.h>
+ #include <brcmu_wifi.h>
+@@ -172,6 +174,35 @@ _brcmf_set_mac_address(struct work_struc
+       }
+ }
++#if IS_ENABLED(CONFIG_IPV6)
++static void _brcmf_update_ndtable(struct work_struct *work)
++{
++      struct brcmf_if *ifp;
++      int i, ret;
++
++      ifp = container_of(work, struct brcmf_if, ndoffload_work);
++
++      /* clear the table in firmware */
++      ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0);
++      if (ret) {
++              brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret);
++              return;
++      }
++
++      for (i = 0; i < ifp->ipv6addr_idx; i++) {
++              ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip",
++                                             &ifp->ipv6_addr_tbl[i],
++                                             sizeof(struct in6_addr));
++              if (ret)
++                      brcmf_err("add nd ip err %d\n", ret);
++      }
++}
++#else
++static void _brcmf_update_ndtable(struct work_struct *work)
++{
++}
++#endif
++
+ static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
+ {
+       struct brcmf_if *ifp = netdev_priv(ndev);
+@@ -685,6 +716,7 @@ int brcmf_net_attach(struct brcmf_if *if
+       INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
+       INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
++      INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
+       if (rtnl_locked)
+               err = register_netdevice(ndev);
+@@ -884,6 +916,7 @@ static void brcmf_del_if(struct brcmf_pu
+               if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+                       cancel_work_sync(&ifp->setmacaddr_work);
+                       cancel_work_sync(&ifp->multicast_work);
++                      cancel_work_sync(&ifp->ndoffload_work);
+               }
+               brcmf_net_detach(ifp->ndev);
+       } else {
+@@ -1025,6 +1058,56 @@ static int brcmf_inetaddr_changed(struct
+ }
+ #endif
++#if IS_ENABLED(CONFIG_IPV6)
++static int brcmf_inet6addr_changed(struct notifier_block *nb,
++                                 unsigned long action, void *data)
++{
++      struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
++                                            inet6addr_notifier);
++      struct inet6_ifaddr *ifa = data;
++      struct brcmf_if *ifp;
++      int i;
++      struct in6_addr *table;
++
++      /* Only handle primary interface */
++      ifp = drvr->iflist[0];
++      if (!ifp)
++              return NOTIFY_DONE;
++      if (ifp->ndev != ifa->idev->dev)
++              return NOTIFY_DONE;
++
++      table = ifp->ipv6_addr_tbl;
++      for (i = 0; i < NDOL_MAX_ENTRIES; i++)
++              if (ipv6_addr_equal(&ifa->addr, &table[i]))
++                      break;
++
++      switch (action) {
++      case NETDEV_UP:
++              if (i == NDOL_MAX_ENTRIES) {
++                      if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
++                              table[ifp->ipv6addr_idx++] = ifa->addr;
++                      } else {
++                              for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
++                                      table[i] = table[i + 1];
++                              table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
++                      }
++              }
++              break;
++      case NETDEV_DOWN:
++              if (i < NDOL_MAX_ENTRIES)
++                      for (; i < ifp->ipv6addr_idx; i++)
++                              table[i] = table[i + 1];
++              break;
++      default:
++              break;
++      }
++
++      schedule_work(&ifp->ndoffload_work);
++
++      return NOTIFY_OK;
++}
++#endif
++
+ int brcmf_attach(struct device *dev)
+ {
+       struct brcmf_pub *drvr = NULL;
+@@ -1164,30 +1247,41 @@ int brcmf_bus_start(struct device *dev)
+ #ifdef CONFIG_INET
+       drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
+       ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
++      if (ret)
++              goto fail;
++
++#if IS_ENABLED(CONFIG_IPV6)
++      drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed;
++      ret = register_inet6addr_notifier(&drvr->inet6addr_notifier);
++      if (ret) {
++              unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
++              goto fail;
++      }
+ #endif
++#endif /* CONFIG_INET */
++
++      return 0;
+ fail:
+-      if (ret < 0) {
+-              brcmf_err("failed: %d\n", ret);
+-              if (drvr->config) {
+-                      brcmf_cfg80211_detach(drvr->config);
+-                      drvr->config = NULL;
+-              }
+-              if (drvr->fws) {
+-                      brcmf_fws_del_interface(ifp);
+-                      brcmf_fws_deinit(drvr);
+-              }
+-              if (ifp)
+-                      brcmf_net_detach(ifp->ndev);
+-              if (p2p_ifp)
+-                      brcmf_net_detach(p2p_ifp->ndev);
+-              drvr->iflist[0] = NULL;
+-              drvr->iflist[1] = NULL;
+-              if (brcmf_ignoring_probe_fail(drvr))
+-                      ret = 0;
+-              return ret;
++      brcmf_err("failed: %d\n", ret);
++      if (drvr->config) {
++              brcmf_cfg80211_detach(drvr->config);
++              drvr->config = NULL;
++      }
++      if (drvr->fws) {
++              brcmf_fws_del_interface(ifp);
++              brcmf_fws_deinit(drvr);
+       }
+-      return 0;
++      if (ifp)
++              brcmf_net_detach(ifp->ndev);
++      if (p2p_ifp)
++              brcmf_net_detach(p2p_ifp->ndev);
++      drvr->iflist[0] = NULL;
++      drvr->iflist[1] = NULL;
++      if (brcmf_ignoring_probe_fail(drvr))
++              ret = 0;
++
++      return ret;
+ }
+ void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
+@@ -1237,6 +1331,10 @@ void brcmf_detach(struct device *dev)
+       unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+ #endif
++#if IS_ENABLED(CONFIG_IPV6)
++      unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
++#endif
++
+       /* stop firmware event handling */
+       brcmf_fweh_detach(drvr);
+       if (drvr->config)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -48,6 +48,8 @@
+  */
+ #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN     32
++#define NDOL_MAX_ENTRIES      8
++
+ /**
+  * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+  *
+@@ -143,6 +145,7 @@ struct brcmf_pub {
+ #endif
+       struct notifier_block inetaddr_notifier;
++      struct notifier_block inet6addr_notifier;
+       struct brcmf_mp_device *settings;
+ };
+@@ -175,6 +178,7 @@ enum brcmf_netif_stop_reason {
+  * @stats: interface specific network statistics.
+  * @setmacaddr_work: worker object for setting mac address.
+  * @multicast_work: worker object for multicast provisioning.
++ * @ndoffload_work: worker object for neighbor discovery offload configuration.
+  * @fws_desc: interface specific firmware-signalling descriptor.
+  * @ifidx: interface index in device firmware.
+  * @bsscfgidx: index of bss associated with this interface.
+@@ -191,6 +195,7 @@ struct brcmf_if {
+       struct net_device_stats stats;
+       struct work_struct setmacaddr_work;
+       struct work_struct multicast_work;
++      struct work_struct ndoffload_work;
+       struct brcmf_fws_mac_descriptor *fws_desc;
+       int ifidx;
+       s32 bsscfgidx;
+@@ -199,6 +204,8 @@ struct brcmf_if {
+       spinlock_t netif_stop_lock;
+       atomic_t pend_8021x_cnt;
+       wait_queue_head_t pend_8021x_wait;
++      struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
++      u8 ipv6addr_idx;
+ };
+ struct brcmf_skb_reorder_data {