brcmfmac: add arp offload ip address table configuration support

Obtain ipv4 address through inetaddr notification for ARP offload host
ip table configuration.

Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@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>
This commit is contained in:
Franky Lin 2016-01-02 09:41:36 +01:00 committed by Kalle Valo
parent f7b7caa488
commit 44129ed04b
2 changed files with 110 additions and 0 deletions

View File

@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/inetdevice.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <brcmu_utils.h> #include <brcmu_utils.h>
@ -620,6 +621,8 @@ static int brcmf_netdev_stop(struct net_device *ndev)
brcmf_cfg80211_down(ndev); brcmf_cfg80211_down(ndev);
brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
brcmf_net_setcarrier(ifp, false); brcmf_net_setcarrier(ifp, false);
return 0; return 0;
@ -940,6 +943,98 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
return available ? bsscfgidx : -ENOMEM; return available ? bsscfgidx : -ENOMEM;
} }
#ifdef CONFIG_INET
#define ARPOL_MAX_ENTRIES 8
static int brcmf_inetaddr_changed(struct notifier_block *nb,
unsigned long action, void *data)
{
struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
inetaddr_notifier);
struct in_ifaddr *ifa = data;
struct net_device *ndev = ifa->ifa_dev->dev;
struct brcmf_if *ifp;
int idx, i, ret;
u32 val;
__be32 addr_table[ARPOL_MAX_ENTRIES] = {0};
/* Find out if the notification is meant for us */
for (idx = 0; idx < BRCMF_MAX_IFS; idx++) {
ifp = drvr->iflist[idx];
if (ifp && ifp->ndev == ndev)
break;
if (idx == BRCMF_MAX_IFS - 1)
return NOTIFY_DONE;
}
/* check if arp offload is supported */
ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val);
if (ret)
return NOTIFY_OK;
/* old version only support primary index */
ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val);
if (ret)
val = 1;
if (val == 1)
ifp = drvr->iflist[0];
/* retrieve the table from firmware */
ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table,
sizeof(addr_table));
if (ret) {
brcmf_err("fail to get arp ip table err:%d\n", ret);
return NOTIFY_OK;
}
for (i = 0; i < ARPOL_MAX_ENTRIES; i++)
if (ifa->ifa_address == addr_table[i])
break;
switch (action) {
case NETDEV_UP:
if (i == ARPOL_MAX_ENTRIES) {
brcmf_dbg(TRACE, "add %pI4 to arp table\n",
&ifa->ifa_address);
/* set it directly */
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
&ifa->ifa_address, sizeof(ifa->ifa_address));
if (ret)
brcmf_err("add arp ip err %d\n", ret);
}
break;
case NETDEV_DOWN:
if (i < ARPOL_MAX_ENTRIES) {
addr_table[i] = 0;
brcmf_dbg(TRACE, "remove %pI4 from arp table\n",
&ifa->ifa_address);
/* clear the table in firmware */
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear",
NULL, 0);
if (ret) {
brcmf_err("fail to clear arp ip table err:%d\n",
ret);
return NOTIFY_OK;
}
for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
if (addr_table[i] != 0) {
brcmf_fil_iovar_data_set(ifp,
"arp_hostip", &addr_table[i],
sizeof(addr_table[i]));
if (ret)
brcmf_err("add arp ip err %d\n",
ret);
}
}
}
break;
default:
break;
}
return NOTIFY_OK;
}
#endif
int brcmf_attach(struct device *dev) int brcmf_attach(struct device *dev)
{ {
struct brcmf_pub *drvr = NULL; struct brcmf_pub *drvr = NULL;
@ -1068,6 +1163,15 @@ int brcmf_bus_start(struct device *dev)
if (p2p_ifp) if (p2p_ifp)
ret = brcmf_net_p2p_attach(p2p_ifp); ret = brcmf_net_p2p_attach(p2p_ifp);
} }
if (ret)
goto fail;
#ifdef CONFIG_INET
drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
#endif
fail: fail:
if (ret < 0) { if (ret < 0) {
brcmf_err("failed: %d\n", ret); brcmf_err("failed: %d\n", ret);
@ -1133,6 +1237,10 @@ void brcmf_detach(struct device *dev)
if (drvr == NULL) if (drvr == NULL)
return; return;
#ifdef CONFIG_INET
unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
#endif
/* stop firmware event handling */ /* stop firmware event handling */
brcmf_fweh_detach(drvr); brcmf_fweh_detach(drvr);
if (drvr->config) if (drvr->config)

View File

@ -141,6 +141,8 @@ struct brcmf_pub {
#ifdef DEBUG #ifdef DEBUG
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
#endif #endif
struct notifier_block inetaddr_notifier;
}; };
/* forward declarations */ /* forward declarations */