aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>2009-10-13 05:47:55 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-10-27 16:48:14 -0400
commit01c09162cd6170f3671825d6d5f2c1ae7b27cbf3 (patch)
tree12bea7cdc8b4d1308651a81f66407a054b569e80
parent1fd2794f36913992798184c464fe8f85753b13e0 (diff)
wl1271: Support for IPv4 ARP filtering
Add support for IPv4 ARP filtering in the driver. This will dramatically reduce the number of unnecessary interrupts by the device in conqested networks. This patch is based on a similar patch to wl1251 by Janne Ylälehto. Cc: Janne Ylälehto <janne.ylalehto@nokia.com> Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Reviewed-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c38
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h17
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c100
4 files changed, 157 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 79a732443151..1e399a2a7f6b 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -441,6 +441,8 @@ struct wl1271 {
441 441
442 /* Current chipset configuration */ 442 /* Current chipset configuration */
443 struct conf_drv_settings conf; 443 struct conf_drv_settings conf;
444
445 struct list_head list;
444}; 446};
445 447
446int wl1271_plt_start(struct wl1271 *wl); 448int wl1271_plt_start(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 44a1237fa794..e891cd5bd25c 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -1092,3 +1092,41 @@ out:
1092 kfree(acx); 1092 kfree(acx);
1093 return ret; 1093 return ret;
1094} 1094}
1095
1096int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
1097 u8 version)
1098{
1099 struct wl1271_acx_arp_filter *acx;
1100 int ret;
1101
1102 wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable);
1103
1104 acx = kzalloc(sizeof(*acx), GFP_KERNEL);
1105 if (!acx) {
1106 ret = -ENOMEM;
1107 goto out;
1108 }
1109
1110 acx->version = version;
1111 acx->enable = enable;
1112
1113 if (enable == true) {
1114 if (version == ACX_IPV4_VERSION)
1115 memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE);
1116 else if (version == ACX_IPV6_VERSION)
1117 memcpy(acx->address, address, sizeof(acx->address));
1118 else
1119 wl1271_error("Invalid IP version");
1120 }
1121
1122 ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER,
1123 acx, sizeof(*acx));
1124 if (ret < 0) {
1125 wl1271_warning("failed to set arp ip filter: %d", ret);
1126 goto out;
1127 }
1128
1129out:
1130 kfree(acx);
1131 return ret;
1132}
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 29fd3635e383..15803146d509 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -955,6 +955,21 @@ struct wl1271_acx_bet_enable {
955 u8 padding[2]; 955 u8 padding[2];
956} __attribute__ ((packed)); 956} __attribute__ ((packed));
957 957
958#define ACX_IPV4_VERSION 4
959#define ACX_IPV6_VERSION 6
960#define ACX_IPV4_ADDR_SIZE 4
961struct wl1271_acx_arp_filter {
962 struct acx_header header;
963 u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */
964 u8 enable; /* 1 to enable ARP filtering, 0 to disable */
965 u8 padding[2];
966 u8 address[16]; /* The configured device IP address - all ARP
967 requests directed to this IP address will pass
968 through. For IPv4, the first four bytes are
969 used. */
970} __attribute__((packed));
971
972
958enum { 973enum {
959 ACX_WAKE_UP_CONDITIONS = 0x0002, 974 ACX_WAKE_UP_CONDITIONS = 0x0002,
960 ACX_MEM_CFG = 0x0003, 975 ACX_MEM_CFG = 0x0003,
@@ -1064,5 +1079,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl);
1064int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); 1079int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
1065int wl1271_acx_smart_reflex(struct wl1271 *wl); 1080int wl1271_acx_smart_reflex(struct wl1271 *wl);
1066int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); 1081int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
1082int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
1083 u8 version);
1067 1084
1068#endif /* __WL1271_ACX_H__ */ 1085#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index d6d1a4c1b114..7d70f4168b2f 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -32,6 +32,7 @@
32#include <linux/etherdevice.h> 32#include <linux/etherdevice.h>
33#include <linux/vmalloc.h> 33#include <linux/vmalloc.h>
34#include <linux/spi/wl12xx.h> 34#include <linux/spi/wl12xx.h>
35#include <linux/inetdevice.h>
35 36
36#include "wl1271.h" 37#include "wl1271.h"
37#include "wl12xx_80211.h" 38#include "wl12xx_80211.h"
@@ -325,6 +326,8 @@ static struct conf_drv_settings default_conf = {
325 } 326 }
326}; 327};
327 328
329static LIST_HEAD(wl_list);
330
328static void wl1271_conf_init(struct wl1271 *wl) 331static void wl1271_conf_init(struct wl1271 *wl)
329{ 332{
330 333
@@ -843,6 +846,93 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
843 return NETDEV_TX_OK; 846 return NETDEV_TX_OK;
844} 847}
845 848
849static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
850 void *arg)
851{
852 struct net_device *dev;
853 struct wireless_dev *wdev;
854 struct wiphy *wiphy;
855 struct ieee80211_hw *hw;
856 struct wl1271 *wl;
857 struct wl1271 *wl_temp;
858 struct in_device *idev;
859 struct in_ifaddr *ifa = arg;
860 int ret = 0;
861
862 /* FIXME: this ugly function should probably be implemented in the
863 * mac80211, and here should only be a simple callback handling actual
864 * setting of the filters. Now we need to dig up references to
865 * various structures to gain access to what we need.
866 * Also, because of this, there is no "initial" setting of the filter
867 * in "op_start", because we don't want to dig up struct net_device
868 * there - the filter will be set upon first change of the interface
869 * IP address. */
870
871 dev = ifa->ifa_dev->dev;
872
873 wdev = dev->ieee80211_ptr;
874 if (wdev == NULL)
875 return -ENODEV;
876
877 wiphy = wdev->wiphy;
878 if (wiphy == NULL)
879 return -ENODEV;
880
881 hw = wiphy_priv(wiphy);
882 if (hw == NULL)
883 return -ENODEV;
884
885 /* Check that the interface is one supported by this driver. */
886 wl_temp = hw->priv;
887 list_for_each_entry(wl, &wl_list, list) {
888 if (wl == wl_temp)
889 break;
890 }
891 if (wl == NULL)
892 return -ENODEV;
893
894 /* Get the interface IP address for the device. "ifa" will become
895 NULL if:
896 - there is no IPV4 protocol address configured
897 - there are multiple (virtual) IPV4 addresses configured
898 When "ifa" is NULL, filtering will be disabled.
899 */
900 ifa = NULL;
901 idev = dev->ip_ptr;
902 if (idev)
903 ifa = idev->ifa_list;
904
905 if (ifa && ifa->ifa_next)
906 ifa = NULL;
907
908 mutex_lock(&wl->mutex);
909
910 if (wl->state == WL1271_STATE_OFF)
911 goto out;
912
913 ret = wl1271_ps_elp_wakeup(wl, false);
914 if (ret < 0)
915 goto out;
916 if (ifa)
917 ret = wl1271_acx_arp_ip_filter(wl, true,
918 (u8 *)&ifa->ifa_address,
919 ACX_IPV4_VERSION);
920 else
921 ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
922 ACX_IPV4_VERSION);
923 wl1271_ps_elp_sleep(wl);
924
925out:
926 mutex_unlock(&wl->mutex);
927
928 return ret;
929}
930
931static struct notifier_block wl1271_dev_notifier = {
932 .notifier_call = wl1271_dev_notify,
933};
934
935
846static int wl1271_op_start(struct ieee80211_hw *hw) 936static int wl1271_op_start(struct ieee80211_hw *hw)
847{ 937{
848 struct wl1271 *wl = hw->priv; 938 struct wl1271 *wl = hw->priv;
@@ -886,6 +976,11 @@ out_power_off:
886out: 976out:
887 mutex_unlock(&wl->mutex); 977 mutex_unlock(&wl->mutex);
888 978
979 if (!ret) {
980 list_add(&wl->list, &wl_list);
981 register_inetaddr_notifier(&wl1271_dev_notifier);
982 }
983
889 return ret; 984 return ret;
890} 985}
891 986
@@ -906,6 +1001,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
906 wl->filter_params = NULL; 1001 wl->filter_params = NULL;
907 spin_unlock_irqrestore(&wl->wl_lock, flags); 1002 spin_unlock_irqrestore(&wl->wl_lock, flags);
908 1003
1004 unregister_inetaddr_notifier(&wl1271_dev_notifier);
1005 list_del(&wl->list);
1006
909 mutex_lock(&wl->mutex); 1007 mutex_lock(&wl->mutex);
910 1008
911 WARN_ON(wl->state != WL1271_STATE_ON); 1009 WARN_ON(wl->state != WL1271_STATE_ON);
@@ -1754,6 +1852,8 @@ static int __devinit wl1271_probe(struct spi_device *spi)
1754 wl = hw->priv; 1852 wl = hw->priv;
1755 memset(wl, 0, sizeof(*wl)); 1853 memset(wl, 0, sizeof(*wl));
1756 1854
1855 INIT_LIST_HEAD(&wl->list);
1856
1757 wl->hw = hw; 1857 wl->hw = hw;
1758 dev_set_drvdata(&spi->dev, wl); 1858 dev_set_drvdata(&spi->dev, wl);
1759 wl->spi = spi; 1859 wl->spi = spi;