summaryrefslogtreecommitdiff
path: root/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch')
-rw-r--r--package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch333
1 files changed, 333 insertions, 0 deletions
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
index 0000000..888ad5b
--- /dev/null
+++ b/package/kernel/mac80211/patches/344-0006-brcmfmac-add-neighbor-discovery-offload-ip-address-t.patch
@@ -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 {