diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 93 | ||||
-rw-r--r-- | net/mac80211/Makefile | 35 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 552 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 60 | ||||
-rw-r--r-- | net/mac80211/ieee80211.c | 199 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 99 | ||||
-rw-r--r-- | net/mac80211/ieee80211_iface.c | 17 | ||||
-rw-r--r-- | net/mac80211/ieee80211_ioctl.c | 112 | ||||
-rw-r--r-- | net/mac80211/ieee80211_led.c | 35 | ||||
-rw-r--r-- | net/mac80211/ieee80211_led.h | 6 | ||||
-rw-r--r-- | net/mac80211/ieee80211_rate.c | 59 | ||||
-rw-r--r-- | net/mac80211/ieee80211_rate.h | 108 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 731 | ||||
-rw-r--r-- | net/mac80211/key.c | 6 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid.h | 285 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_algo.c | 549 | ||||
-rw-r--r-- | net/mac80211/rc80211_pid_debugfs.c | 223 | ||||
-rw-r--r-- | net/mac80211/rc80211_simple.c | 82 | ||||
-rw-r--r-- | net/mac80211/rx.c | 752 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 49 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 50 | ||||
-rw-r--r-- | net/mac80211/tx.c | 370 | ||||
-rw-r--r-- | net/mac80211/util.c | 132 | ||||
-rw-r--r-- | net/mac80211/wep.c | 10 | ||||
-rw-r--r-- | net/mac80211/wme.c | 27 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 14 |
26 files changed, 3749 insertions, 906 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index ce176e691af..09c255002e5 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -10,27 +10,84 @@ config MAC80211 select CFG80211 select NET_SCH_FIFO ---help--- - This option enables the hardware independent IEEE 802.11 - networking stack. + This option enables the hardware independent IEEE 802.11 + networking stack. -config MAC80211_RCSIMPLE - bool "'simple' rate control algorithm" if EMBEDDED - default y - depends on MAC80211 +menu "Rate control algorithm selection" + depends on MAC80211 != n + +choice + prompt "Default rate control algorithm" + default MAC80211_RC_DEFAULT_PID + ---help--- + This option selects the default rate control algorithm + mac80211 will use. Note that this default can still be + overriden through the ieee80211_default_rc_algo module + parameter if different algorithms are available. + +config MAC80211_RC_DEFAULT_PID + bool "PID controller based rate control algorithm" + select MAC80211_RC_PID + ---help--- + Select the PID controller based rate control as the + default rate control algorithm. You should choose + this unless you know what you are doing. + +config MAC80211_RC_DEFAULT_SIMPLE + bool "Simple rate control algorithm" + select MAC80211_RC_SIMPLE + ---help--- + Select the simple rate control as the default rate + control algorithm. Note that this is a non-responsive, + dumb algorithm. You should choose the PID rate control + instead. + +config MAC80211_RC_DEFAULT_NONE + bool "No default algorithm" + depends on EMBEDDED help - This option allows you to turn off the 'simple' rate - control algorithm in mac80211. If you do turn it off, - you absolutely need another rate control algorithm. + Selecting this option will select no default algorithm + and allow you to not build any. Do not choose this + option unless you know your driver comes with another + suitable algorithm. +endchoice + +comment "Selecting 'y' for an algorithm will" +comment "build the algorithm into mac80211." + +config MAC80211_RC_DEFAULT + string + default "pid" if MAC80211_RC_DEFAULT_PID + default "simple" if MAC80211_RC_DEFAULT_SIMPLE + default "" - Say Y unless you know you will have another algorithm - available. +config MAC80211_RC_PID + tristate "PID controller based rate control algorithm" + ---help--- + This option enables a TX rate control algorithm for + mac80211 that uses a PID controller to select the TX + rate. + + Say Y or M unless you're sure you want to use a + different rate control algorithm. + +config MAC80211_RC_SIMPLE + tristate "Simple rate control algorithm (DEPRECATED)" + ---help--- + This option enables a very simple, non-responsive TX + rate control algorithm. This algorithm is deprecated + and will be removed from the kernel in the near future. + It has been replaced by the PID algorithm. + + Say N unless you know what you are doing. +endmenu config MAC80211_LEDS bool "Enable LED triggers" depends on MAC80211 && LEDS_TRIGGERS ---help--- - This option enables a few LED triggers for different - packet receive/transmit events. + This option enables a few LED triggers for different + packet receive/transmit events. config MAC80211_DEBUGFS bool "Export mac80211 internals in DebugFS" @@ -51,6 +108,16 @@ config MAC80211_DEBUG If you are not trying to debug or develop the ieee80211 subsystem, you most likely want to say N here. +config MAC80211_HT_DEBUG + bool "Enable HT debugging output" + depends on MAC80211_DEBUG + ---help--- + This option enables 802.11n High Throughput features + debug tracing output. + + If you are not trying to debug of develop the ieee80211 + subsystem, you most likely want to say N here. + config MAC80211_VERBOSE_DEBUG bool "Verbose debugging output" depends on MAC80211_DEBUG diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 1e6237b3484..54f46bc80cf 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -1,11 +1,15 @@ obj-$(CONFIG_MAC80211) += mac80211.o -mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o -mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o -mac80211-objs-$(CONFIG_NET_SCHED) += wme.o -mac80211-objs-$(CONFIG_MAC80211_RCSIMPLE) += rc80211_simple.o +# objects for PID algorithm +rc80211_pid-y := rc80211_pid_algo.o +rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o -mac80211-objs := \ +# build helper for PID algorithm +rc-pid-y := $(rc80211_pid-y) +rc-pid-m := rc80211_pid.o + +# mac80211 objects +mac80211-y := \ ieee80211.o \ ieee80211_ioctl.o \ sta_info.o \ @@ -23,5 +27,22 @@ mac80211-objs := \ tx.o \ key.o \ util.o \ - event.o \ - $(mac80211-objs-y) + event.o + +mac80211-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o +mac80211-$(CONFIG_NET_SCHED) += wme.o +mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ + debugfs.o \ + debugfs_sta.o \ + debugfs_netdev.o \ + debugfs_key.o + + +# Build rate control algorithm(s) +CFLAGS_rc80211_simple.o += -DRC80211_SIMPLE_COMPILE +CFLAGS_rc80211_pid_algo.o += -DRC80211_PID_COMPILE +mac80211-$(CONFIG_MAC80211_RC_SIMPLE) += rc80211_simple.o +mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc-pid-$(CONFIG_MAC80211_RC_PID)) + +# Modular rate algorithms are assigned to mac80211-m - make separate modules +obj-m += $(mac80211-m) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9e2bc1fd023..22c9619ba77 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1,17 +1,20 @@ /* * mac80211 configuration hooks for cfg80211 * - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net> * * This file is GPLv2 as found in COPYING. */ +#include <linux/ieee80211.h> #include <linux/nl80211.h> #include <linux/rtnetlink.h> #include <net/net_namespace.h> +#include <linux/rcupdate.h> #include <net/cfg80211.h> #include "ieee80211_i.h" #include "cfg.h" +#include "ieee80211_rate.h" static enum ieee80211_if_types nl80211_type_to_mac80211_type(enum nl80211_iftype type) @@ -90,7 +93,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_VLAN) + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) return -EOPNOTSUPP; ieee80211_if_reinit(dev); @@ -99,8 +102,553 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, return 0; } +static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, u8 *mac_addr, + struct key_params *params) +{ + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta = NULL; + enum ieee80211_key_alg alg; + int ret; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + alg = ALG_WEP; + break; + case WLAN_CIPHER_SUITE_TKIP: + alg = ALG_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + alg = ALG_CCMP; + break; + default: + return -EINVAL; + } + + if (mac_addr) { + sta = sta_info_get(sdata->local, mac_addr); + if (!sta) + return -ENOENT; + } + + ret = 0; + if (!ieee80211_key_alloc(sdata, sta, alg, key_idx, + params->key_len, params->key)) + ret = -ENOMEM; + + if (sta) + sta_info_put(sta); + + return ret; +} + +static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, u8 *mac_addr) +{ + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + int ret; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (mac_addr) { + sta = sta_info_get(sdata->local, mac_addr); + if (!sta) + return -ENOENT; + + ret = 0; + if (sta->key) + ieee80211_key_free(sta->key); + else + ret = -ENOENT; + + sta_info_put(sta); + return ret; + } + + if (!sdata->keys[key_idx]) + return -ENOENT; + + ieee80211_key_free(sdata->keys[key_idx]); + + return 0; +} + +static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, + struct key_params *params)) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta = NULL; + u8 seq[6] = {0}; + struct key_params params; + struct ieee80211_key *key; + u32 iv32; + u16 iv16; + int err = -ENOENT; + + if (mac_addr) { + sta = sta_info_get(sdata->local, mac_addr); + if (!sta) + goto out; + + key = sta->key; + } else + key = sdata->keys[key_idx]; + + if (!key) + goto out; + + memset(¶ms, 0, sizeof(params)); + + switch (key->conf.alg) { + case ALG_TKIP: + params.cipher = WLAN_CIPHER_SUITE_TKIP; + + iv32 = key->u.tkip.iv32; + iv16 = key->u.tkip.iv16; + + if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && + sdata->local->ops->get_tkip_seq) + sdata->local->ops->get_tkip_seq( + local_to_hw(sdata->local), + key->conf.hw_key_idx, + &iv32, &iv16); + + seq[0] = iv16 & 0xff; + seq[1] = (iv16 >> 8) & 0xff; + seq[2] = iv32 & 0xff; + seq[3] = (iv32 >> 8) & 0xff; + seq[4] = (iv32 >> 16) & 0xff; + seq[5] = (iv32 >> 24) & 0xff; + params.seq = seq; + params.seq_len = 6; + break; + case ALG_CCMP: + params.cipher = WLAN_CIPHER_SUITE_CCMP; + seq[0] = key->u.ccmp.tx_pn[5]; + seq[1] = key->u.ccmp.tx_pn[4]; + seq[2] = key->u.ccmp.tx_pn[3]; + seq[3] = key->u.ccmp.tx_pn[2]; + seq[4] = key->u.ccmp.tx_pn[1]; + seq[5] = key->u.ccmp.tx_pn[0]; + params.seq = seq; + params.seq_len = 6; + break; + case ALG_WEP: + if (key->conf.keylen == 5) + params.cipher = WLAN_CIPHER_SUITE_WEP40; + else + params.cipher = WLAN_CIPHER_SUITE_WEP104; + break; + } + + params.key = key->conf.key; + params.key_len = key->conf.keylen; + + callback(cookie, ¶ms); + err = 0; + + out: + if (sta) + sta_info_put(sta); + return err; +} + +static int ieee80211_config_default_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_idx) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ieee80211_set_default_key(sdata, key_idx); + + return 0; +} + +static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_stats *stats) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + sta = sta_info_get(local, mac); + if (!sta) + return -ENOENT; + + /* XXX: verify sta->dev == dev */ + + stats->filled = STATION_STAT_INACTIVE_TIME | + STATION_STAT_RX_BYTES | + STATION_STAT_TX_BYTES; + + stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); + stats->rx_bytes = sta->rx_bytes; + stats->tx_bytes = sta->tx_bytes; + + sta_info_put(sta); + + return 0; +} + +/* + * This handles both adding a beacon and setting new beacon info + */ +static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, + struct beacon_parameters *params) +{ + struct beacon_data *new, *old; + int new_head_len, new_tail_len; + int size; + int err = -EINVAL; + + old = sdata->u.ap.beacon; + + /* head must not be zero-length */ + if (params->head && !params->head_len) + return -EINVAL; + + /* + * This is a kludge. beacon interval should really be part + * of the beacon information. + */ + if (params->interval) { + sdata->local->hw.conf.beacon_int = params->interval; + if (ieee80211_hw_config(sdata->local)) + return -EINVAL; + /* + * We updated some parameter so if below bails out + * it's not an error. + */ + err = 0; + } + + /* Need to have a beacon head if we don't have one yet */ + if (!params->head && !old) + return err; + + /* sorry, no way to start beaconing without dtim period */ + if (!params->dtim_period && !old) + return err; + + /* new or old head? */ + if (params->head) + new_head_len = params->head_len; + else + new_head_len = old->head_len; + + /* new or old tail? */ + if (params->tail || !old) + /* params->tail_len will be zero for !params->tail */ + new_tail_len = params->tail_len; + else + new_tail_len = old->tail_len; + + size = sizeof(*new) + new_head_len + new_tail_len; + + new = kzalloc(size, GFP_KERNEL); + if (!new) + return -ENOMEM; + + /* start filling the new info now */ + + /* new or old dtim period? */ + if (params->dtim_period) + new->dtim_period = params->dtim_period; + else + new->dtim_period = old->dtim_period; + + /* + * pointers go into the block we allocated, + * memory is | beacon_data | head | tail | + */ + new->head = ((u8 *) new) + sizeof(*new); + new->tail = new->head + new_head_len; + new->head_len = new_head_len; + new->tail_len = new_tail_len; + + /* copy in head */ + if (params->head) + memcpy(new->head, params->head, new_head_len); + else + memcpy(new->head, old->head, new_head_len); + + /* copy in optional tail */ + if (params->tail) + memcpy(new->tail, params->tail, new_tail_len); + else + if (old) + memcpy(new->tail, old->tail, new_tail_len); + + rcu_assign_pointer(sdata->u.ap.beacon, new); + + synchronize_rcu(); + + kfree(old); + + return ieee80211_if_config_beacon(sdata->dev); +} + +static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct beacon_data *old; + + if (sdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + + old = sdata->u.ap.beacon; + + if (old) + return -EALREADY; + + return ieee80211_config_beacon(sdata, params); +} + +static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct beacon_data *old; + + if (sdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + + old = sdata->u.ap.beacon; + + if (!old) + return -ENOENT; + + return ieee80211_config_beacon(sdata, params); +} + +static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct beacon_data *old; + + if (sdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + + old = sdata->u.ap.beacon; + + if (!old) + return -ENOENT; + + rcu_assign_pointer(sdata->u.ap.beacon, NULL); + synchronize_rcu(); + kfree(old); + + return ieee80211_if_config_beacon(dev); +} + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + __be16 len; /* 6 */ + u8 dsap; /* 0 */ + u8 ssap; /* 0 */ + u8 control; + u8 xid_info[3]; +} __attribute__ ((packed)); + +static void ieee80211_send_layer2_update(struct sta_info *sta) +{ + struct iapp_layer2_update *msg; + struct sk_buff *skb; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + skb = dev_alloc_skb(sizeof(*msg)); + if (!skb) + return; + msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg)); + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + memset(msg->da, 0xff, ETH_ALEN); + memcpy(msg->sa, sta->addr, ETH_ALEN); + msg->len = htons(6); + msg->dsap = 0; + msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ + msg->control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg->xid_info[0] = 0x81; /* XID format identifier */ + msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ + + skb->dev = sta->dev; + skb->protocol = eth_type_trans(skb, sta->dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +static void sta_apply_parameters(struct ieee80211_local *local, + struct sta_info *sta, + struct station_parameters *params) +{ + u32 rates; + int i, j; + struct ieee80211_hw_mode *mode; + + if (params->station_flags & STATION_FLAG_CHANGED) { + sta->flags &= ~WLAN_STA_AUTHORIZED; + if (params->station_flags & STATION_FLAG_AUTHORIZED) + sta->flags |= WLAN_STA_AUTHORIZED; + + sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; + if (params->station_flags & STATION_FLAG_SHORT_PREAMBLE) + sta->flags |= WLAN_STA_SHORT_PREAMBLE; + + sta->flags &= ~WLAN_STA_WME; + if (params->station_flags & STATION_FLAG_WME) + sta->flags |= WLAN_STA_WME; + } + + if (params->aid) { + sta->aid = params->aid; + if (sta->aid > IEEE80211_MAX_AID) + sta->aid = 0; /* XXX: should this be an error? */ + } + + if (params->listen_interval >= 0) + sta->listen_interval = params->listen_interval; + + if (params->supported_rates) { + rates = 0; + mode = local->oper_hw_mode; + for (i = 0; i < params->supported_rates_len; i++) { + int rate = (params->supported_rates[i] & 0x7f) * 5; + for (j = 0; j < mode->num_rates; j++) { + if (mode->rates[j].rate == rate) + rates |= BIT(j); + } + } + sta->supp_rates = rates; + } +} + +static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata; + + /* Prevent a race with changing the rate control algorithm */ + if (!netif_running(dev)) + return -ENETDOWN; + + /* XXX: get sta belonging to dev */ + sta = sta_info_get(local, mac); + if (sta) { + sta_info_put(sta); + return -EEXIST; + } + + if (params->vlan) { + sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); + + if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN || + sdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + } else + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + sta = sta_info_add(local, dev, mac, GFP_KERNEL); + if (!sta) + return -ENOMEM; + + sta->dev = sdata->dev; + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN || + sdata->vif.type == IEEE80211_IF_TYPE_AP) + ieee80211_send_layer2_update(sta); + + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + + sta_apply_parameters(local, sta, params); + + rate_control_rate_init(sta, local); + + sta_info_put(sta); + + return 0; +} + +static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + if (mac) { + /* XXX: get sta belonging to dev */ + sta = sta_info_get(local, mac); + if (!sta) + return -ENOENT; + + sta_info_free(sta); + sta_info_put(sta); + } else + sta_info_flush(local, dev); + + return 0; +} + +static int ieee80211_change_station(struct wiphy *wiphy, + struct net_device *dev, + u8 *mac, + struct station_parameters *params) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + struct ieee80211_sub_if_data *vlansdata; + + /* XXX: get sta belonging to dev */ + sta = sta_info_get(local, mac); + if (!sta) + return -ENOENT; + + if (params->vlan && params->vlan != sta->dev) { + vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); + + if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN || + vlansdata->vif.type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + + sta->dev = params->vlan; + ieee80211_send_layer2_update(sta); + } + + sta_apply_parameters(local, sta, params); + + sta_info_put(sta); + + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, .change_virtual_intf = ieee80211_change_iface, + .add_key = ieee80211_add_key, + .del_key = ieee80211_del_key, + .get_key = ieee80211_get_key, + .set_default_key = ieee80211_config_default_key, + .add_beacon = ieee80211_add_beacon, + .set_beacon = ieee80211_set_beacon, + .del_beacon = ieee80211_del_beacon, + .add_station = ieee80211_add_station, + .del_station = ieee80211_del_station, + .change_station = ieee80211_change_station, + .get_station = ieee80211_get_station, }; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index f0e6ab7eb62..829872a3ae8 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -91,8 +91,7 @@ static const struct file_operations name##_ops = { \ /* common attributes */ IEEE80211_IF_FILE(channel_use, channel_use, DEC); IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); -IEEE80211_IF_FILE(eapol, eapol, DEC); -IEEE80211_IF_FILE(ieee8021_x, ieee802_1x, DEC); +IEEE80211_IF_FILE(ieee802_1x_pac, ieee802_1x_pac, DEC); /* STA/IBSS attributes */ IEEE80211_IF_FILE(state, u.sta.state, DEC); @@ -119,13 +118,12 @@ static ssize_t ieee80211_if_fmt_flags( sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "", sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "", sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "", - sdata->flags & IEEE80211_SDATA_USE_PROTECTION ? "CTS prot\n" : ""); + sdata->bss_conf.use_cts_prot ? "CTS prot\n" : ""); } __IEEE80211_IF_FILE(flags); /* AP attributes */ IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); -IEEE80211_IF_FILE(dtim_period, u.ap.dtim_period, DEC); IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); IEEE80211_IF_FILE(num_beacons, u.ap.num_beacons, DEC); IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC); @@ -139,26 +137,6 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( } __IEEE80211_IF_FILE(num_buffered_multicast); -static ssize_t ieee80211_if_fmt_beacon_head_len( - const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ - if (sdata->u.ap.beacon_head) - return scnprintf(buf, buflen, "%d\n", - sdata->u.ap.beacon_head_len); - return scnprintf(buf, buflen, "\n"); -} -__IEEE80211_IF_FILE(beacon_head_len); - -static ssize_t ieee80211_if_fmt_beacon_tail_len( - const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ - if (sdata->u.ap.beacon_tail) - return scnprintf(buf, buflen, "%d\n", - sdata->u.ap.beacon_tail_len); - return scnprintf(buf, buflen, "\n"); -} -__IEEE80211_IF_FILE(beacon_tail_len); - /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); @@ -170,8 +148,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(channel_use, sta); DEBUGFS_ADD(drop_unencrypted, sta); - DEBUGFS_ADD(eapol, sta); - DEBUGFS_ADD(ieee8021_x, sta); + DEBUGFS_ADD(ieee802_1x_pac, sta); DEBUGFS_ADD(state, sta); DEBUGFS_ADD(bssid, sta); DEBUGFS_ADD(prev_bssid, sta); @@ -192,25 +169,20 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(channel_use, ap); DEBUGFS_ADD(drop_unencrypted, ap); - DEBUGFS_ADD(eapol, ap); - DEBUGFS_ADD(ieee8021_x, ap); + DEBUGFS_ADD(ieee802_1x_pac, ap); DEBUGFS_ADD(num_sta_ps, ap); - DEBUGFS_ADD(dtim_period, ap); DEBUGFS_ADD(dtim_count, ap); DEBUGFS_ADD(num_beacons, ap); DEBUGFS_ADD(force_unicast_rateidx, ap); DEBUGFS_ADD(max_ratectrl_rateidx, ap); DEBUGFS_ADD(num_buffered_multicast, ap); - DEBUGFS_ADD(beacon_head_len, ap); - DEBUGFS_ADD(beacon_tail_len, ap); } static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(channel_use, wds); DEBUGFS_ADD(drop_unencrypted, wds); - DEBUGFS_ADD(eapol, wds); - DEBUGFS_ADD(ieee8021_x, wds); + DEBUGFS_ADD(ieee802_1x_pac, wds); DEBUGFS_ADD(peer, wds); } @@ -218,8 +190,7 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(channel_use, vlan); DEBUGFS_ADD(drop_unencrypted, vlan); - DEBUGFS_ADD(eapol, vlan); - DEBUGFS_ADD(ieee8021_x, vlan); + DEBUGFS_ADD(ieee802_1x_pac, vlan); } static void add_monitor_files(struct ieee80211_sub_if_data *sdata) @@ -231,7 +202,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata) if (!sdata->debugfsdir) return; - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: add_sta_files(sdata); @@ -263,8 +234,7 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_DEL(channel_use, sta); DEBUGFS_DEL(drop_unencrypted, sta); - DEBUGFS_DEL(eapol, sta); - DEBUGFS_DEL(ieee8021_x, sta); + DEBUGFS_DEL(ieee802_1x_pac, sta); DEBUGFS_DEL(state, sta); DEBUGFS_DEL(bssid, sta); DEBUGFS_DEL(prev_bssid, sta); @@ -285,25 +255,20 @@ static void del_ap_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_DEL(channel_use, ap); DEBUGFS_DEL(drop_unencrypted, ap); - DEBUGFS_DEL(eapol, ap); - DEBUGFS_DEL(ieee8021_x, ap); + DEBUGFS_DEL(ieee802_1x_pac, ap); DEBUGFS_DEL(num_sta_ps, ap); - DEBUGFS_DEL(dtim_period, ap); DEBUGFS_DEL(dtim_count, ap); DEBUGFS_DEL(num_beacons, ap); DEBUGFS_DEL(force_unicast_rateidx, ap); DEBUGFS_DEL(max_ratectrl_rateidx, ap); DEBUGFS_DEL(num_buffered_multicast, ap); - DEBUGFS_DEL(beacon_head_len, ap); - DEBUGFS_DEL(beacon_tail_len, ap); } static void del_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_DEL(channel_use, wds); DEBUGFS_DEL(drop_unencrypted, wds); - DEBUGFS_DEL(eapol, wds); - DEBUGFS_DEL(ieee8021_x, wds); + DEBUGFS_DEL(ieee802_1x_pac, wds); DEBUGFS_DEL(peer, wds); } @@ -311,8 +276,7 @@ static void del_vlan_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_DEL(channel_use, vlan); DEBUGFS_DEL(drop_unencrypted, vlan); - DEBUGFS_DEL(eapol, vlan); - DEBUGFS_DEL(ieee8021_x, vlan); + DEBUGFS_DEL(ieee802_1x_pac, vlan); } static void del_monitor_files(struct ieee80211_sub_if_data *sdata) @@ -362,7 +326,7 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata) void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) { - del_files(sdata, sdata->type); + del_files(sdata, sdata->vif.type); debugfs_remove(sdata->debugfsdir); sdata->debugfsdir = NULL; } diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 6378850d858..5dcc2d61551 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -34,6 +34,8 @@ #include "debugfs.h" #include "debugfs_netdev.h" +#define SUPP_MCS_SET_LEN 16 + /* * For seeing transmitted packets on monitor interfaces * we have a radiotap header too. @@ -175,21 +177,21 @@ static int ieee80211_open(struct net_device *dev) /* * check whether it may have the same address */ - if (!identical_mac_addr_allowed(sdata->type, - nsdata->type)) + if (!identical_mac_addr_allowed(sdata->vif.type, + nsdata->vif.type)) return -ENOTUNIQ; /* * can only add VLANs to enabled APs */ - if (sdata->type == IEEE80211_IF_TYPE_VLAN && - nsdata->type == IEEE80211_IF_TYPE_AP && + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN && + nsdata->vif.type == IEEE80211_IF_TYPE_AP && netif_running(nsdata->dev)) sdata->u.vlan.ap = nsdata; } } - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_WDS: if (is_zero_ether_addr(sdata->u.wds.remote_addr)) return -ENOLINK; @@ -217,9 +219,10 @@ static int ieee80211_open(struct net_device *dev) if (res) return res; ieee80211_hw_config(local); + ieee80211_led_radio(local, local->hw.conf.radio_enabled); } - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_VLAN: list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans); /* no need to tell driver */ @@ -240,8 +243,8 @@ static int ieee80211_open(struct net_device *dev) sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET; /* fall through */ default: - conf.if_id = dev->ifindex; - conf.type = sdata->type; + conf.vif = &sdata->vif; + conf.type = sdata->vif.type; conf.mac_addr = dev->dev_addr; res = local->ops->add_interface(local_to_hw(local), &conf); if (res && !local->open_count && local->ops->stop) @@ -253,7 +256,7 @@ static int ieee80211_open(struct net_device *dev) ieee80211_reset_erp_info(dev); ieee80211_enable_keys(sdata); - if (sdata->type == IEEE80211_IF_TYPE_STA && + if (sdata->vif.type == IEEE80211_IF_TYPE_STA && !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)) netif_carrier_off(dev); else @@ -290,9 +293,20 @@ static int ieee80211_stop(struct net_device *dev) struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_if_init_conf conf; + struct sta_info *sta; + int i; sdata = IEEE80211_DEV_TO_SUB_IF(dev); + list_for_each_entry(sta, &local->sta_list, list) { + if (sta->dev == dev) + for (i = 0; i < STA_TID_NUM; i++) + ieee80211_sta_stop_rx_ba_session(sta->dev, + sta->addr, i, + WLAN_BACK_RECIPIENT, + WLAN_REASON_QSTA_LEAVE_QBSS); + } + netif_stop_queue(dev); /* @@ -309,10 +323,17 @@ static int ieee80211_stop(struct net_device *dev) dev_mc_unsync(local->mdev, dev); - /* down all dependent devices, that is VLANs */ - if (sdata->type == IEEE80211_IF_TYPE_AP) { + /* APs need special treatment */ + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmp; + struct beacon_data *old_beacon = sdata->u.ap.beacon; + /* remove beacon */ + rcu_assign_pointer(sdata->u.ap.beacon, NULL); + synchronize_rcu(); + kfree(old_beacon); + + /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); @@ -321,7 +342,7 @@ static int ieee80211_stop(struct net_device *dev) local->open_count--; - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_VLAN: list_del(&sdata->u.vlan.list); sdata->u.vlan.ap = NULL; @@ -350,11 +371,14 @@ static int ieee80211_stop(struct net_device *dev) synchronize_rcu(); skb_queue_purge(&sdata->u.sta.skb_queue); - if (!local->ops->hw_scan && - local->scan_dev == sdata->dev) { - local->sta_scanning = 0; - cancel_delayed_work(&local->scan_work); + if (local->scan_dev == sdata->dev) { + if (!local->ops->hw_scan) { + local->sta_sw_scanning = 0; + cancel_delayed_work(&local->scan_work); + } else + local->sta_hw_scanning = 0; } + flush_workqueue(local->hw.workqueue); sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; @@ -363,8 +387,8 @@ static int ieee80211_stop(struct net_device *dev) sdata->u.sta.extra_ie_len = 0; /* fall through */ default: - conf.if_id = dev->ifindex; - conf.type = sdata->type; + conf.vif = &sdata->vif; + conf.type = sdata->vif.type; conf.mac_addr = dev->dev_addr; /* disable all keys for as long as this netdev is down */ ieee80211_disable_keys(sdata); @@ -378,6 +402,8 @@ static int ieee80211_stop(struct net_device *dev) if (local->ops->stop) local->ops->stop(local_to_hw(local)); + ieee80211_led_radio(local, 0); + tasklet_disable(&local->tx_pending_tasklet); tasklet_disable(&local->tasklet); } @@ -485,20 +511,20 @@ static int __ieee80211_if_config(struct net_device *dev, return 0; memset(&conf, 0, sizeof(conf)); - conf.type = sdata->type; - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + conf.type = sdata->vif.type; + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { conf.bssid = sdata->u.sta.bssid; conf.ssid = sdata->u.sta.ssid; conf.ssid_len = sdata->u.sta.ssid_len; - } else if (sdata->type == IEEE80211_IF_TYPE_AP) { + } else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { conf.ssid = sdata->u.ap.ssid; conf.ssid_len = sdata->u.ap.ssid_len; conf.beacon = beacon; conf.beacon_control = control; } return local->ops->config_interface(local_to_hw(local), - dev->ifindex, &conf); + &sdata->vif, &conf); } int ieee80211_if_config(struct net_device *dev) @@ -510,11 +536,13 @@ int ieee80211_if_config_beacon(struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_tx_control control; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sk_buff *skb; if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE)) return 0; - skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control); + skb = ieee80211_beacon_get(local_to_hw(local), &sdata->vif, + &control); if (!skb) return -ENOMEM; return __ieee80211_if_config(dev, skb, &control); @@ -526,7 +554,7 @@ int ieee80211_hw_config(struct ieee80211_local *local) struct ieee80211_channel *chan; int ret = 0; - if (local->sta_scanning) { + if (local->sta_sw_scanning) { chan = local->scan_channel; mode = local->scan_hw_mode; } else { @@ -560,25 +588,79 @@ int ieee80211_hw_config(struct ieee80211_local *local) return ret; } -void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes) +/** + * ieee80211_hw_config_ht should be used only after legacy configuration + * has been determined, as ht configuration depends upon the hardware's + * HT abilities for a _specific_ band. + */ +int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht, + struct ieee80211_ht_info *req_ht_cap, + struct ieee80211_ht_bss_info *req_bss_cap) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (local->ops->erp_ie_changed) - local->ops->erp_ie_changed(local_to_hw(local), changes, - !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION), - !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE)); + struct ieee80211_conf *conf = &local->hw.conf; + struct ieee80211_hw_mode *mode = conf->mode; + int i; + + /* HT is not supported */ + if (!mode->ht_info.ht_supported) { + conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; + return -EOPNOTSUPP; + } + + /* disable HT */ + if (!enable_ht) { + conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; + } else { + conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; + conf->ht_conf.cap = req_ht_cap->cap & mode->ht_info.cap; + conf->ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); + conf->ht_conf.cap |= + mode->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + conf->ht_bss_conf.primary_channel = + req_bss_cap->primary_channel; + conf->ht_bss_conf.bss_cap = req_bss_cap->bss_cap; + conf->ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; + for (i = 0; i < SUPP_MCS_SET_LEN; i++) + conf->ht_conf.supp_mcs_set[i] = + mode->ht_info.supp_mcs_set[i] & + req_ht_cap->supp_mcs_set[i]; + + /* In STA mode, this gives us indication + * to the AP's mode of operation */ + conf->ht_conf.ht_supported = 1; + conf->ht_conf.ampdu_factor = req_ht_cap->ampdu_factor; + conf->ht_conf.ampdu_density = req_ht_cap->ampdu_density; + } + + local->ops->conf_ht(local_to_hw(local), &local->hw.conf); + + return 0; +} + +void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, + u32 changed) +{ + struct ieee80211_local *local = sdata->local; + + if (!changed) + return; + + if (local->ops->bss_info_changed) + local->ops->bss_info_changed(local_to_hw(local), + &sdata->vif, + &sdata->bss_conf, + changed); } void ieee80211_reset_erp_info(struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - sdata->flags &= ~(IEEE80211_SDATA_USE_PROTECTION | - IEEE80211_SDATA_SHORT_PREAMBLE); - ieee80211_erp_info_change_notify(dev, - IEEE80211_ERP_CHANGE_PROTECTION | - IEEE80211_ERP_CHANGE_PREAMBLE); + sdata->bss_conf.use_cts_prot = 0; + sdata->bss_conf.use_short_preamble = 0; + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE); } void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, @@ -635,7 +717,7 @@ static void ieee80211_tasklet_handler(unsigned long data) case IEEE80211_RX_MSG: /* status is in skb->cb */ memcpy(&rx_status, skb->cb, sizeof(rx_status)); - /* Clear skb->type in order to not confuse kernel + /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; __ieee80211_rx(local_to_hw(local), skb, &rx_status); @@ -670,7 +752,7 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local, struct ieee80211_tx_packet_data *pkt_data; pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; - pkt_data->ifindex = control->ifindex; + pkt_data->ifindex = vif_to_sdata(control->vif)->dev->ifindex; pkt_data->flags = 0; if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS) pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS; @@ -678,6 +760,8 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local, pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT; if (control->flags & IEEE80211_TXCTL_REQUEUE) pkt_data->flags |= IEEE80211_TXPD_REQUEUE; + if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME) + pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME; pkt_data->queue = control->queue; hdrlen = ieee80211_get_hdrlen_from_skb(skb); @@ -805,10 +889,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, sta_info_put(sta); return; } - } else { - /* FIXME: STUPID to call this with both local and local->mdev */ - rate_control_tx_status(local, local->mdev, skb, status); - } + } else + rate_control_tx_status(local->mdev, skb, status); ieee80211_led_tx(local, 0); @@ -894,7 +976,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, if (!monitors || !skb) goto out; - if (sdata->type == IEEE80211_IF_TYPE_MNTR) { + if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR) { if (!netif_running(sdata->dev)) continue; monitors--; @@ -1016,7 +1098,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, mdev->header_ops = &ieee80211_header_ops; mdev->set_multicast_list = ieee80211_master_set_multicast_list; - sdata->type = IEEE80211_IF_TYPE_AP; + sdata->vif.type = IEEE80211_IF_TYPE_AP; sdata->dev = mdev; sdata->local = local; sdata->u.ap.force_unicast_rateidx = -1; @@ -1260,33 +1342,38 @@ static int __init ieee80211_init(void) BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb)); -#ifdef CONFIG_MAC80211_RCSIMPLE - ret = ieee80211_rate_control_register(&mac80211_rcsimple); + ret = rc80211_simple_init(); if (ret) - return ret; -#endif + goto fail; + + ret = rc80211_pid_init(); + if (ret) + goto fail_simple; ret = ieee80211_wme_register(); if (ret) { -#ifdef CONFIG_MAC80211_RCSIMPLE - ieee80211_rate_control_unregister(&mac80211_rcsimple); -#endif printk(KERN_DEBUG "ieee80211_init: failed to " "initialize WME (err=%d)\n", ret); - return ret; + goto fail_pid; } ieee80211_debugfs_netdev_init(); ieee80211_regdomain_init(); return 0; + + fail_pid: + rc80211_simple_exit(); + fail_simple: + rc80211_pid_exit(); + fail: + return ret; } static void __exit ieee80211_exit(void) { -#ifdef CONFIG_MAC80211_RCSIMPLE - ieee80211_rate_control_unregister(&mac80211_rcsimple); -#endif + rc80211_simple_exit(); + rc80211_pid_exit(); ieee80211_wme_unregister(); ieee80211_debugfs_netdev_exit(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 72e1c93dd87..72ecbf7bf96 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -89,6 +89,8 @@ struct ieee80211_sta_bss { size_t rsn_ie_len; u8 *wmm_ie; size_t wmm_ie_len; + u8 *ht_ie; + size_t ht_ie_len; #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; @@ -121,6 +123,7 @@ typedef enum { /* frame is destined to interface currently processed (incl. multicast frames) */ #define IEEE80211_TXRXD_RXRA_MATCH BIT(5) #define IEEE80211_TXRXD_TX_INJECTED BIT(6) +#define IEEE80211_TXRXD_RX_AMSDU BIT(7) struct ieee80211_txrx_data { struct sk_buff *skb; struct net_device *dev; @@ -161,6 +164,7 @@ struct ieee80211_txrx_data { #define IEEE80211_TXPD_REQ_TX_STATUS BIT(0) #define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1) #define IEEE80211_TXPD_REQUEUE BIT(2) +#define IEEE80211_TXPD_EAPOL_FRAME BIT(3) /* Stored in sk_buff->cb */ struct ieee80211_tx_packet_data { int ifindex; @@ -186,9 +190,14 @@ typedef ieee80211_txrx_result (*ieee80211_tx_handler) typedef ieee80211_txrx_result (*ieee80211_rx_handler) (struct ieee80211_txrx_data *rx); +struct beacon_data { + u8 *head, *tail; + int head_len, tail_len; + int dtim_period; +}; + struct ieee80211_if_ap { - u8 *beacon_head, *beacon_tail; - int beacon_head_len, beacon_tail_len; + struct beacon_data *beacon; struct list_head vlans; @@ -201,7 +210,7 @@ struct ieee80211_if_ap { u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; atomic_t num_sta_ps; /* number of stations in PS mode */ struct sk_buff_head ps_bc_buf; - int dtim_period, dtim_count; + int dtim_count; int force_unicast_rateidx; /* forced TX rateidx for unicast frames */ int max_ratectrl_rateidx; /* max TX rateidx for rate control */ int num_beacons; /* number of TXed beacon frames for this BSS */ @@ -282,15 +291,9 @@ struct ieee80211_if_sta { /* flags used in struct ieee80211_sub_if_data.flags */ #define IEEE80211_SDATA_ALLMULTI BIT(0) #define IEEE80211_SDATA_PROMISC BIT(1) -#define IEEE80211_SDATA_USE_PROTECTION BIT(2) /* CTS protect ERP frames */ -/* use short preamble with IEEE 802.11b: this flag is set when the AP or beacon - * generator reports that there are no present stations that cannot support short - * preambles */ -#define IEEE80211_SDATA_SHORT_PREAMBLE BIT(3) -#define IEEE80211_SDATA_USERSPACE_MLME BIT(4) +#define IEEE80211_SDATA_USERSPACE_MLME BIT(2) struct ieee80211_sub_if_data { struct list_head list; - enum ieee80211_if_types type; struct wireless_dev wdev; @@ -303,11 +306,11 @@ struct ieee80211_sub_if_data { unsigned int flags; int drop_unencrypted; - int eapol; /* 0 = process EAPOL frames as normal data frames, - * 1 = send EAPOL frames through wlan#ap to hostapd - * (default) */ - int ieee802_1x; /* IEEE 802.1X PAE - drop packet to/from unauthorized - * port */ + /* + * IEEE 802.1X Port access control in effect, + * drop packets to/from unauthorized port + */ + int ieee802_1x_pac; u16 sequence; @@ -319,6 +322,15 @@ struct ieee80211_sub_if_data { struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; struct ieee80211_key *default_key; + /* + * BSS configuration for this interface. + * + * FIXME: I feel bad putting this here when we already have a + * bss pointer, but the bss pointer is just wrong when + * you have multiple virtual STA mode interfaces... + * This needs to be fixed. + */ + struct ieee80211_bss_conf bss_conf; struct ieee80211_if_ap *bss; /* BSS that this device belongs to */ union { @@ -336,8 +348,7 @@ struct ieee80211_sub_if_data { struct { struct dentry *channel_use; struct dentry *drop_unencrypted; - struct dentry *eapol; - struct dentry *ieee8021_x; + struct dentry *ieee802_1x_pac; struct dentry *state; struct dentry *bssid; struct dentry *prev_bssid; @@ -356,30 +367,24 @@ struct ieee80211_sub_if_data { struct { struct dentry *channel_use; struct dentry *drop_unencrypted; - struct dentry *eapol; - struct dentry *ieee8021_x; + struct dentry *ieee802_1x_pac; struct dentry *num_sta_ps; - struct dentry *dtim_period; struct dentry *dtim_count; struct dentry *num_beacons; struct dentry *force_unicast_rateidx; struct dentry *max_ratectrl_rateidx; struct dentry *num_buffered_multicast; - struct dentry *beacon_head_len; - struct dentry *beacon_tail_len; } ap; struct { struct dentry *channel_use; struct dentry *drop_unencrypted; - struct dentry *eapol; - struct dentry *ieee8021_x; + struct dentry *ieee802_1x_pac; struct dentry *peer; } wds; struct { struct dentry *channel_use; struct dentry *drop_unencrypted; - struct dentry *eapol; - struct dentry *ieee8021_x; + struct dentry *ieee802_1x_pac; } vlan; struct { struct dentry *mode; @@ -387,8 +392,16 @@ struct ieee80211_sub_if_data { struct dentry *default_key; } debugfs; #endif + /* must be last, dynamically sized area in this! */ + struct ieee80211_vif vif; }; +static inline +struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) +{ + return container_of(p, struct ieee80211_sub_if_data, vif); +} + #define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev) enum { @@ -470,7 +483,8 @@ struct ieee80211_local { struct list_head interfaces; - int sta_scanning; + bool sta_sw_scanning; + bool sta_hw_scanning; int scan_channel_idx; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; unsigned long last_scan_completed; @@ -483,10 +497,6 @@ struct ieee80211_local { struct list_head sta_bss_list; struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; spinlock_t sta_bss_lock; -#define IEEE80211_SCAN_MATCH_SSID BIT(0) -#define IEEE80211_SCAN_WPA_ONLY BIT(1) -#define IEEE80211_SCAN_EXTRA_INFO BIT(2) - int scan_flags; /* SNMP counters */ /* dot11CountersTable */ @@ -503,8 +513,9 @@ struct ieee80211_local { #ifdef CONFIG_MAC80211_LEDS int tx_led_counter, rx_led_counter; - struct led_trigger *tx_led, *rx_led, *assoc_led; - char tx_led_name[32], rx_led_name[32], assoc_led_name[32]; + struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; + char tx_led_name[32], rx_led_name[32], + assoc_led_name[32], radio_led_name[32]; #endif u32 channel_use; @@ -708,6 +719,9 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr); void ieee80211_if_setup(struct net_device *dev); struct ieee80211_rate *ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hwrate); +int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht, + struct ieee80211_ht_info *req_ht_cap, + struct ieee80211_ht_bss_info *req_bss_cap); /* ieee80211_ioctl.c */ extern const struct iw_handler_def ieee80211_iw_handler_def; @@ -749,7 +763,8 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); void ieee80211_sta_req_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta); int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, +ieee80211_txrx_result ieee80211_sta_rx_scan(struct net_device *dev, + struct sk_buff *skb, struct ieee80211_rx_status *rx_status); void ieee80211_rx_bss_list_init(struct net_device *dev); void ieee80211_rx_bss_list_deinit(struct net_device *dev); @@ -759,9 +774,17 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, u8 *addr); int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason); int ieee80211_sta_disassociate(struct net_device *dev, u16 reason); -void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes); +void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, + u32 changed); void ieee80211_reset_erp_info(struct net_device *dev); - +int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, + struct ieee80211_ht_info *ht_info); +int ieee80211_ht_addt_info_ie_to_ht_bss_info( + struct ieee80211_ht_addt_info *ht_add_info_ie, + struct ieee80211_ht_bss_info *bss_info); +void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da, + u16 tid, u16 initiator, u16 reason); +void sta_rx_agg_session_timer_expired(unsigned long data); /* ieee80211_iface.c */ int ieee80211_if_add(struct net_device *dev, const char *name, struct net_device **new_dev, int type); @@ -793,8 +816,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); extern void *mac80211_wiphy_privid; /* for wiphy privid */ extern const unsigned char rfc1042_header[6]; extern const unsigned char bridge_tunnel_header[6]; -u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len); -int ieee80211_is_eapol(const struct sk_buff *skb); +u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, + enum ieee80211_if_types type); int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, int rate, int erp, int short_preamble); void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx, diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index 43e505d2945..92f1eb2da31 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c @@ -22,7 +22,6 @@ void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) /* Default values for sub-interface parameters */ sdata->drop_unencrypted = 0; - sdata->eapol = 1; for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) skb_queue_head_init(&sdata->fragments[i].skb_list); @@ -48,7 +47,7 @@ int ieee80211_if_add(struct net_device *dev, const char *name, int ret; ASSERT_RTNL(); - ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), + ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name, ieee80211_if_setup); if (!ndev) return -ENOMEM; @@ -67,7 +66,7 @@ int ieee80211_if_add(struct net_device *dev, const char *name, sdata = IEEE80211_DEV_TO_SUB_IF(ndev); ndev->ieee80211_ptr = &sdata->wdev; sdata->wdev.wiphy = local->hw.wiphy; - sdata->type = IEEE80211_IF_TYPE_AP; + sdata->vif.type = IEEE80211_IF_TYPE_AP; sdata->dev = ndev; sdata->local = local; ieee80211_if_sdata_init(sdata); @@ -99,7 +98,7 @@ fail: void ieee80211_if_set_type(struct net_device *dev, int type) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int oldtype = sdata->type; + int oldtype = sdata->vif.type; /* * We need to call this function on the master interface @@ -117,7 +116,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type) /* most have no BSS pointer */ sdata->bss = NULL; - sdata->type = type; + sdata->vif.type = type; switch (type) { case IEEE80211_IF_TYPE_WDS: @@ -127,7 +126,6 @@ void ieee80211_if_set_type(struct net_device *dev, int type) sdata->u.vlan.ap = NULL; break; case IEEE80211_IF_TYPE_AP: - sdata->u.ap.dtim_period = 2; sdata->u.ap.force_unicast_rateidx = -1; sdata->u.ap.max_ratectrl_rateidx = -1; skb_queue_head_init(&sdata->u.ap.ps_bc_buf); @@ -182,7 +180,7 @@ void ieee80211_if_reinit(struct net_device *dev) ieee80211_if_sdata_deinit(sdata); - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_INVALID: /* cannot happen */ WARN_ON(1); @@ -208,8 +206,7 @@ void ieee80211_if_reinit(struct net_device *dev) } } - kfree(sdata->u.ap.beacon_head); - kfree(sdata->u.ap.beacon_tail); + kfree(sdata->u.ap.beacon); while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { local->total_ps_buffered--; @@ -280,7 +277,7 @@ int ieee80211_if_remove(struct net_device *dev, const char *name, int id) ASSERT_RTNL(); list_for_each_entry_safe(sdata, n, &local->interfaces, list) { - if ((sdata->type == id || id == -1) && + if ((sdata->vif.type == id || id == -1) && strcmp(name, sdata->dev->name) == 0 && sdata->dev != local->mdev) { list_del_rcu(&sdata->list); diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c index 308bbe4a133..5024d373383 100644 --- a/net/mac80211/ieee80211_ioctl.c +++ b/net/mac80211/ieee80211_ioctl.c @@ -21,6 +21,7 @@ #include <net/mac80211.h> #include "ieee80211_i.h" +#include "ieee80211_led.h" #include "ieee80211_rate.h" #include "wpa.h" #include "aes_ccm.h" @@ -111,8 +112,8 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev, if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) return -EOPNOTSUPP; - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { int ret = ieee80211_sta_set_extra_ie(dev, extra, data->length); if (ret) return ret; @@ -218,6 +219,8 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev, IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + range->scan_capa |= IW_SCAN_CAPA_ESSID; + return 0; } @@ -229,7 +232,7 @@ static int ieee80211_ioctl_siwmode(struct net_device *dev, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int type; - if (sdata->type == IEEE80211_IF_TYPE_VLAN) + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) return -EOPNOTSUPP; switch (*mode) { @@ -246,7 +249,7 @@ static int ieee80211_ioctl_siwmode(struct net_device *dev, return -EINVAL; } - if (type == sdata->type) + if (type == sdata->vif.type) return 0; if (netif_running(dev)) return -EBUSY; @@ -265,7 +268,7 @@ static int ieee80211_ioctl_giwmode(struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_AP: *mode = IW_MODE_MASTER; break; @@ -315,7 +318,7 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq) } if (set) { - if (local->sta_scanning) + if (local->sta_sw_scanning) ret = 0; else ret = ieee80211_hw_config(local); @@ -333,13 +336,13 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ if (freq->e == 0) { if (freq->m < 0) { - if (sdata->type == IEEE80211_IF_TYPE_STA) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) sdata->u.sta.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; return 0; @@ -385,8 +388,8 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, len--; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { int ret; if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { if (len > IEEE80211_MAX_SSID_LEN) @@ -406,7 +409,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, return 0; } - if (sdata->type == IEEE80211_IF_TYPE_AP) { + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { memcpy(sdata->u.ap.ssid, ssid, len); memset(sdata->u.ap.ssid + len, 0, IEEE80211_MAX_SSID_LEN - len); @@ -425,8 +428,8 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { int res = ieee80211_sta_get_ssid(dev, ssid, &len); if (res == 0) { data->length = len; @@ -436,7 +439,7 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, return res; } - if (sdata->type == IEEE80211_IF_TYPE_AP) { + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { len = sdata->u.ap.ssid_len; if (len > IW_ESSID_MAX_SIZE) len = IW_ESSID_MAX_SIZE; @@ -456,8 +459,8 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { int ret; if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) { memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data, @@ -476,7 +479,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, return ret; ieee80211_sta_req_auth(dev, &sdata->u.sta); return 0; - } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { + } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) { if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, ETH_ALEN) == 0) return 0; @@ -494,12 +497,12 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { ap_addr->sa_family = ARPHRD_ETHER; memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN); return 0; - } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { + } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) { ap_addr->sa_family = ARPHRD_ETHER; memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); return 0; @@ -513,7 +516,6 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct iw_scan_req *req = NULL; u8 *ssid = NULL; @@ -522,23 +524,10 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - switch (sdata->type) { - case IEEE80211_IF_TYPE_STA: - case IEEE80211_IF_TYPE_IBSS: - if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) { - ssid = sdata->u.sta.ssid; - ssid_len = sdata->u.sta.ssid_len; - } - break; - case IEEE80211_IF_TYPE_AP: - if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) { - ssid = sdata->u.ap.ssid; - ssid_len = sdata->u.ap.ssid_len; - } - break; - default: + if (sdata->vif.type != IEEE80211_IF_TYPE_STA && + sdata->vif.type != IEEE80211_IF_TYPE_IBSS && + sdata->vif.type != IEEE80211_IF_TYPE_AP) return -EOPNOTSUPP; - } /* if SSID was specified explicitly then use that */ if (wrqu->data.length == sizeof(struct iw_scan_req) && @@ -558,8 +547,10 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev, { int res; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - if (local->sta_scanning) + + if (local->sta_sw_scanning || local->sta_hw_scanning) return -EAGAIN; + res = ieee80211_sta_scan_results(dev, extra, data->length); if (res >= 0) { data->length = res; @@ -614,7 +605,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev, struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_STA) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) sta = sta_info_get(local, sdata->u.sta.bssid); else return -EOPNOTSUPP; @@ -634,22 +625,36 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev, { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); bool need_reconfig = 0; + u8 new_power_level; if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL; - if (!data->txpower.fixed) - return -EINVAL; - if (local->hw.conf.power_level != data->txpower.value) { - local->hw.conf.power_level = data->txpower.value; + if (data->txpower.fixed) { + new_power_level = data->txpower.value; + } else { + /* Automatic power level. Get the px power from the current + * channel. */ + struct ieee80211_channel* chan = local->oper_channel; + if (!chan) + return -EINVAL; + + new_power_level = chan->power_level; + } + + if (local->hw.conf.power_level != new_power_level) { + local->hw.conf.power_level = new_power_level; need_reconfig = 1; } + if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) { local->hw.conf.radio_enabled = !(data->txpower.disabled); need_reconfig = 1; + ieee80211_led_radio(local, local->hw.conf.radio_enabled); } + if (need_reconfig) { ieee80211_hw_config(local); /* The return value of hw_config is not of big interest here, @@ -814,8 +819,8 @@ static int ieee80211_ioctl_siwmlme(struct net_device *dev, struct iw_mlme *mlme = (struct iw_mlme *) extra; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type != IEEE80211_IF_TYPE_STA && - sdata->type != IEEE80211_IF_TYPE_IBSS) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA && + sdata->vif.type != IEEE80211_IF_TYPE_IBSS) return -EINVAL; switch (mlme->cmd) { @@ -928,8 +933,11 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_KEY_MGMT: break; + case IW_AUTH_DROP_UNENCRYPTED: + sdata->drop_unencrypted = !!data->value; + break; case IW_AUTH_PRIVACY_INVOKED: - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) ret = -EINVAL; else { sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; @@ -944,8 +952,8 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, } break; case IW_AUTH_80211_AUTH_ALG: - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) sdata->u.sta.auth_algs = data->value; else ret = -EOPNOTSUPP; @@ -965,8 +973,8 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta = NULL; - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) sta = sta_info_get(local, sdata->u.sta.bssid); if (!sta) { wstats->discard.fragment = 0; @@ -994,8 +1002,8 @@ static int ieee80211_ioctl_giwauth(struct net_device *dev, switch (data->flags & IW_AUTH_INDEX) { case IW_AUTH_80211_AUTH_ALG: - if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) + if (sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) data->value = sdata->u.sta.auth_algs; else ret = -EOPNOTSUPP; diff --git a/net/mac80211/ieee80211_led.c b/net/mac80211/ieee80211_led.c index 4cf89af9d10..f401484ab6d 100644 --- a/net/mac80211/ieee80211_led.c +++ b/net/mac80211/ieee80211_led.c @@ -43,6 +43,16 @@ void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) led_trigger_event(local->assoc_led, LED_OFF); } +void ieee80211_led_radio(struct ieee80211_local *local, bool enabled) +{ + if (unlikely(!local->radio_led)) + return; + if (enabled) + led_trigger_event(local->radio_led, LED_FULL); + else + led_trigger_event(local->radio_led, LED_OFF); +} + void ieee80211_led_init(struct ieee80211_local *local) { local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); @@ -77,10 +87,25 @@ void ieee80211_led_init(struct ieee80211_local *local) local->assoc_led = NULL; } } + + local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); + if (local->radio_led) { + snprintf(local->radio_led_name, sizeof(local->radio_led_name), + "%sradio", wiphy_name(local->hw.wiphy)); + local->radio_led->name = local->radio_led_name; + if (led_trigger_register(local->radio_led)) { + kfree(local->radio_led); + local->radio_led = NULL; + } + } } void ieee80211_led_exit(struct ieee80211_local *local) { + if (local->radio_led) { + led_trigger_unregister(local->radio_led); + kfree(local->radio_led); + } if (local->assoc_led) { led_trigger_unregister(local->assoc_led); kfree(local->assoc_led); @@ -95,6 +120,16 @@ void ieee80211_led_exit(struct ieee80211_local *local) } } +char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (local->radio_led) + return local->radio_led_name; + return NULL; +} +EXPORT_SYMBOL(__ieee80211_get_radio_led_name); + char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); diff --git a/net/mac80211/ieee80211_led.h b/net/mac80211/ieee80211_led.h index 0feb2261983..77b1e1ba603 100644 --- a/net/mac80211/ieee80211_led.h +++ b/net/mac80211/ieee80211_led.h @@ -16,6 +16,8 @@ extern void ieee80211_led_rx(struct ieee80211_local *local); extern void ieee80211_led_tx(struct ieee80211_local *local, int q); extern void ieee80211_led_assoc(struct ieee80211_local *local, bool associated); +extern void ieee80211_led_radio(struct ieee80211_local *local, + bool enabled); extern void ieee80211_led_init(struct ieee80211_local *local); extern void ieee80211_led_exit(struct ieee80211_local *local); #else @@ -29,6 +31,10 @@ static inline void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) { } +static inline void ieee80211_led_radio(struct ieee80211_local *local, + bool enabled) +{ +} static inline void ieee80211_led_init(struct ieee80211_local *local) { } diff --git a/net/mac80211/ieee80211_rate.c b/net/mac80211/ieee80211_rate.c index c3f27839374..b957e67c5fb 100644 --- a/net/mac80211/ieee80211_rate.c +++ b/net/mac80211/ieee80211_rate.c @@ -21,6 +21,11 @@ struct rate_control_alg { static LIST_HEAD(rate_ctrl_algs); static DEFINE_MUTEX(rate_ctrl_mutex); +static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT; +module_param(ieee80211_default_rc_algo, charp, 0644); +MODULE_PARM_DESC(ieee80211_default_rc_algo, + "Default rate control algorithm for mac80211 to use"); + int ieee80211_rate_control_register(struct rate_control_ops *ops) { struct rate_control_alg *alg; @@ -89,21 +94,31 @@ ieee80211_try_rate_control_ops_get(const char *name) return ops; } -/* Get the rate control algorithm. If `name' is NULL, get the first - * available algorithm. */ +/* Get the rate control algorithm. */ static struct rate_control_ops * ieee80211_rate_control_ops_get(const char *name) { struct rate_control_ops *ops; + const char *alg_name; if (!name) - name = "simple"; + alg_name = ieee80211_default_rc_algo; + else + alg_name = name; - ops = ieee80211_try_rate_control_ops_get(name); + ops = ieee80211_try_rate_control_ops_get(alg_name); if (!ops) { - request_module("rc80211_%s", name); - ops = ieee80211_try_rate_control_ops_get(name); + request_module("rc80211_%s", alg_name); + ops = ieee80211_try_rate_control_ops_get(alg_name); } + if (!ops && name) + /* try default if specific alg requested but not found */ + ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); + + /* try built-in one if specific alg requested but not found */ + if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) + ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); + return ops; } @@ -147,6 +162,37 @@ static void rate_control_release(struct kref *kref) kfree(ctrl_ref); } +void rate_control_get_rate(struct net_device *dev, + struct ieee80211_hw_mode *mode, struct sk_buff *skb, + struct rate_selection *sel) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct rate_control_ref *ref = local->rate_ctrl; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct sta_info *sta = sta_info_get(local, hdr->addr1); + int i; + + memset(sel, 0, sizeof(struct rate_selection)); + + ref->ops->get_rate(ref->priv, dev, mode, skb, sel); + + /* Select a non-ERP backup rate. */ + if (!sel->nonerp) { + for (i = 0; i < mode->num_rates - 1; i++) { + struct ieee80211_rate *rate = &mode->rates[i]; + if (sel->rate->rate < rate->rate) + break; + + if (rate_supported(sta, mode, i) && + !(rate->flags & IEEE80211_RATE_ERP)) + sel->nonerp = rate; + } + } + + if (sta) + sta_info_put(sta); +} + struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) { kref_get(&ref->kref); @@ -197,3 +243,4 @@ void rate_control_deinitialize(struct ieee80211_local *local) local->rate_ctrl = NULL; rate_control_put(ref); } + diff --git a/net/mac80211/ieee80211_rate.h b/net/mac80211/ieee80211_rate.h index 23688139ffb..73f19e8aa51 100644 --- a/net/mac80211/ieee80211_rate.h +++ b/net/mac80211/ieee80211_rate.h @@ -18,31 +18,24 @@ #include "ieee80211_i.h" #include "sta_info.h" -#define RATE_CONTROL_NUM_DOWN 20 -#define RATE_CONTROL_NUM_UP 15 - - -struct rate_control_extra { - /* values from rate_control_get_rate() to the caller: */ - struct ieee80211_rate *probe; /* probe with this rate, or NULL for no - * probing */ +struct rate_selection { + /* Selected transmission rate */ + struct ieee80211_rate *rate; + /* Non-ERP rate to use if mac80211 decides it cannot use an ERP rate */ struct ieee80211_rate *nonerp; - - /* parameters from the caller to rate_control_get_rate(): */ - struct ieee80211_hw_mode *mode; - u16 ethertype; + /* probe with this rate, or NULL for no probing */ + struct ieee80211_rate *probe; }; - struct rate_control_ops { struct module *module; const char *name; void (*tx_status)(void *priv, struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status); - struct ieee80211_rate *(*get_rate)(void *priv, struct net_device *dev, - struct sk_buff *skb, - struct rate_control_extra *extra); + void (*get_rate)(void *priv, struct net_device *dev, + struct ieee80211_hw_mode *mode, struct sk_buff *skb, + struct rate_selection *sel); void (*rate_init)(void *priv, void *priv_sta, struct ieee80211_local *local, struct sta_info *sta); void (*clear)(void *priv); @@ -65,9 +58,6 @@ struct rate_control_ref { struct kref kref; }; -/* default 'simple' algorithm */ -extern struct rate_control_ops mac80211_rcsimple; - int ieee80211_rate_control_register(struct rate_control_ops *ops); void ieee80211_rate_control_unregister(struct rate_control_ops *ops); @@ -75,25 +65,20 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops); * first available algorithm. */ struct rate_control_ref *rate_control_alloc(const char *name, struct ieee80211_local *local); +void rate_control_get_rate(struct net_device *dev, + struct ieee80211_hw_mode *mode, struct sk_buff *skb, + struct rate_selection *sel); struct rate_control_ref *rate_control_get(struct rate_control_ref *ref); void rate_control_put(struct rate_control_ref *ref); -static inline void rate_control_tx_status(struct ieee80211_local *local, - struct net_device *dev, +static inline void rate_control_tx_status(struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status) { + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct rate_control_ref *ref = local->rate_ctrl; - ref->ops->tx_status(ref->priv, dev, skb, status); -} - -static inline struct ieee80211_rate * -rate_control_get_rate(struct ieee80211_local *local, struct net_device *dev, - struct sk_buff *skb, struct rate_control_extra *extra) -{ - struct rate_control_ref *ref = local->rate_ctrl; - return ref->ops->get_rate(ref->priv, dev, skb, extra); + ref->ops->tx_status(ref->priv, dev, skb, status); } @@ -142,10 +127,73 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta) #endif } +static inline int +rate_supported(struct sta_info *sta, struct ieee80211_hw_mode *mode, int index) +{ + return (sta == NULL || sta->supp_rates & BIT(index)) && + (mode->rates[index].flags & IEEE80211_RATE_SUPPORTED); +} + +static inline int +rate_lowest_index(struct ieee80211_local *local, struct ieee80211_hw_mode *mode, + struct sta_info *sta) +{ + int i; + + for (i = 0; i < mode->num_rates; i++) { + if (rate_supported(sta, mode, i)) + return i; + } + + /* warn when we cannot find a rate. */ + WARN_ON(1); + + return 0; +} + +static inline struct ieee80211_rate * +rate_lowest(struct ieee80211_local *local, struct ieee80211_hw_mode *mode, + struct sta_info *sta) +{ + return &mode->rates[rate_lowest_index(local, mode, sta)]; +} + /* functions for rate control related to a device */ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, const char *name); void rate_control_deinitialize(struct ieee80211_local *local); + +/* Rate control algorithms */ +#if defined(RC80211_SIMPLE_COMPILE) || \ + (defined(CONFIG_MAC80211_RC_SIMPLE) && \ + !defined(CONFIG_MAC80211_RC_SIMPLE_MODULE)) +extern int rc80211_simple_init(void); +extern void rc80211_simple_exit(void); +#else +static inline int rc80211_simple_init(void) +{ + return 0; +} +static inline void rc80211_simple_exit(void) +{ +} +#endif + +#if defined(RC80211_PID_COMPILE) || \ + (defined(CONFIG_MAC80211_RC_PID) && \ + !defined(CONFIG_MAC80211_RC_PID_MODULE)) +extern int rc80211_pid_init(void); +extern void rc80211_pid_exit(void); +#else +static inline int rc80211_pid_init(void) +{ + return 0; +} +static inline void rc80211_pid_exit(void) +{ +} +#endif + #endif /* IEEE80211_RATE_H */ diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index bee8080f224..2019b4f0528 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -57,6 +57,20 @@ #define ERP_INFO_USE_PROTECTION BIT(1) +/* mgmt header + 1 byte action code */ +#define IEEE80211_MIN_ACTION_SIZE (24 + 1) + +#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002 +#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C +#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0 +#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000 +#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800 + +/* next values represent the buffer size for A-MPDU frame. + * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */ +#define IEEE80211_MIN_AMPDU_BUF 0x8 +#define IEEE80211_MAX_AMPDU_BUF 0x40 + static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, u8 *ssid, size_t ssid_len); static struct ieee80211_sta_bss * @@ -90,7 +104,8 @@ struct ieee802_11_elems { u8 *ext_supp_rates; u8 *wmm_info; u8 *wmm_param; - + u8 *ht_cap_elem; + u8 *ht_info_elem; /* length of them, respectively */ u8 ssid_len; u8 supp_rates_len; @@ -106,6 +121,8 @@ struct ieee802_11_elems { u8 ext_supp_rates_len; u8 wmm_info_len; u8 wmm_param_len; + u8 ht_cap_elem_len; + u8 ht_info_elem_len; }; static void ieee802_11_parse_elems(u8 *start, size_t len, @@ -190,6 +207,14 @@ static void ieee802_11_parse_elems(u8 *start, size_t len, elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; + case WLAN_EID_HT_CAPABILITY: + elems->ht_cap_elem = pos; + elems->ht_cap_elem_len = elen; + break; + case WLAN_EID_HT_EXTRA_INFO: + elems->ht_info_elem = pos; + elems->ht_info_elem_len = elen; + break; default: break; } @@ -288,50 +313,89 @@ static void ieee80211_sta_wmm_params(struct net_device *dev, } -static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value) +static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata, + u8 erp_value) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf; struct ieee80211_if_sta *ifsta = &sdata->u.sta; - int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; - int preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0; - u8 changes = 0; + bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; + bool preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0; DECLARE_MAC_BUF(mac); + u32 changed = 0; - if (use_protection != !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION)) { + if (use_protection != bss_conf->use_cts_prot) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: CTS protection %s (BSSID=" "%s)\n", - dev->name, + sdata->dev->name, use_protection ? "enabled" : "disabled", print_mac(mac, ifsta->bssid)); } - if (use_protection) - sdata->flags |= IEEE80211_SDATA_USE_PROTECTION; - else - sdata->flags &= ~IEEE80211_SDATA_USE_PROTECTION; - changes |= IEEE80211_ERP_CHANGE_PROTECTION; + bss_conf->use_cts_prot = use_protection; + changed |= BSS_CHANGED_ERP_CTS_PROT; } - if (preamble_mode != !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE)) { + if (preamble_mode != bss_conf->use_short_preamble) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: switched to %s barker preamble" " (BSSID=%s)\n", - dev->name, + sdata->dev->name, (preamble_mode == WLAN_ERP_PREAMBLE_SHORT) ? "short" : "long", print_mac(mac, ifsta->bssid)); } - if (preamble_mode) - sdata->flags &= ~IEEE80211_SDATA_SHORT_PREAMBLE; - else - sdata->flags |= IEEE80211_SDATA_SHORT_PREAMBLE; - changes |= IEEE80211_ERP_CHANGE_PREAMBLE; + bss_conf->use_short_preamble = preamble_mode; + changed |= BSS_CHANGED_ERP_PREAMBLE; } - if (changes) - ieee80211_erp_info_change_notify(dev, changes); + return changed; } +int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, + struct ieee80211_ht_info *ht_info) +{ + + if (ht_info == NULL) + return -EINVAL; + + memset(ht_info, 0, sizeof(*ht_info)); + + if (ht_cap_ie) { + u8 ampdu_info = ht_cap_ie->ampdu_params_info; + + ht_info->ht_supported = 1; + ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info); + ht_info->ampdu_factor = + ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR; + ht_info->ampdu_density = + (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2; + memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16); + } else + ht_info->ht_supported = 0; + + return 0; +} + +int ieee80211_ht_addt_info_ie_to_ht_bss_info( + struct ieee80211_ht_addt_info *ht_add_info_ie, + struct ieee80211_ht_bss_info *bss_info) +{ + if (bss_info == NULL) + return -EINVAL; + + memset(bss_info, 0, sizeof(*bss_info)); + + if (ht_add_info_ie) { + u16 op_mode; + op_mode = le16_to_cpu(ht_add_info_ie->operation_mode); + + bss_info->primary_channel = ht_add_info_ie->control_chan; + bss_info->bss_cap = ht_add_info_ie->ht_param; + bss_info->bss_op_mode = (u8)(op_mode & 0xff); + } + + return 0; +} static void ieee80211_sta_send_associnfo(struct net_device *dev, struct ieee80211_if_sta *ifsta) @@ -388,20 +452,17 @@ static void ieee80211_set_associated(struct net_device *dev, struct ieee80211_if_sta *ifsta, bool assoc) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; union iwreq_data wrqu; - - if (!!(ifsta->flags & IEEE80211_STA_ASSOCIATED) == assoc) - return; + u32 changed = BSS_CHANGED_ASSOC; if (assoc) { - struct ieee80211_sub_if_data *sdata; struct ieee80211_sta_bss *bss; ifsta->flags |= IEEE80211_STA_ASSOCIATED; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) return; bss = ieee80211_rx_bss_get(dev, ifsta->bssid, @@ -409,7 +470,8 @@ static void ieee80211_set_associated(struct net_device *dev, ifsta->ssid, ifsta->ssid_len); if (bss) { if (bss->has_erp_value) - ieee80211_handle_erp_ie(dev, bss->erp_value); + changed |= ieee80211_handle_erp_ie( + sdata, bss->erp_value); ieee80211_rx_bss_put(dev, bss); } @@ -429,6 +491,8 @@ static void ieee80211_set_associated(struct net_device *dev, wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); ifsta->last_probe = jiffies; ieee80211_led_assoc(local, assoc); + + ieee80211_bss_info_change_notify(sdata, changed); } static void ieee80211_set_disassoc(struct net_device *dev, @@ -630,6 +694,19 @@ static void ieee80211_send_assoc(struct net_device *dev, *pos++ = 1; /* WME ver */ *pos++ = 0; } + /* wmm support is a must to HT */ + if (wmm && mode->ht_info.ht_supported) { + __le16 tmp = cpu_to_le16(mode->ht_info.cap); + pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + memcpy(pos, &tmp, sizeof(u16)); + pos += sizeof(u16); + *pos++ = (mode->ht_info.ampdu_factor | + (mode->ht_info.ampdu_density << 2)); + memcpy(pos, mode->ht_info.supp_mcs_set, 16); + } kfree(ifsta->assocreq_ies); ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; @@ -918,6 +995,320 @@ static void ieee80211_auth_challenge(struct net_device *dev, elems.challenge_len + 2, 1); } +static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid, + u8 dialog_token, u16 status, u16 policy, + u16 buf_size, u16 timeout) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u16 capab; + + skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 + + sizeof(mgmt->u.action.u.addba_resp)); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer " + "for addba resp frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, da, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) + memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN); + else + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp)); + mgmt->u.action.category = WLAN_CATEGORY_BACK; + mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; + mgmt->u.action.u.addba_resp.dialog_token = dialog_token; + + capab = (u16)(policy << 1); /* bit 1 aggregation policy */ + capab |= (u16)(tid << 2); /* bit 5:2 TID number */ + capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */ + + mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab); + mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout); + mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); + + ieee80211_sta_tx(dev, skb, 0); + + return; +} + +static void ieee80211_sta_process_addba_request(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + struct ieee80211_conf *conf = &hw->conf; + struct sta_info *sta; + struct tid_ampdu_rx *tid_agg_rx; + u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; + u8 dialog_token; + int ret = -EOPNOTSUPP; + DECLARE_MAC_BUF(mac); + + sta = sta_info_get(local, mgmt->sa); + if (!sta) + return; + + /* extract session parameters from addba request frame */ + dialog_token = mgmt->u.action.u.addba_req.dialog_token; + timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); + start_seq_num = + le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; + + capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); + ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; + tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; + + status = WLAN_STATUS_REQUEST_DECLINED; + + /* sanity check for incoming parameters: + * check if configuration can support the BA policy + * and if buffer size does not exceeds max value */ + if (((ba_policy != 1) + && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA))) + || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { + status = WLAN_STATUS_INVALID_QOS_PARAM; +#ifdef CONFIG_MAC80211_HT_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "Block Ack Req with bad params from " + "%s on tid %u. policy %d, buffer size %d\n", + print_mac(mac, mgmt->sa), tid, ba_policy, + buf_size); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + goto end_no_lock; + } + /* determine default buffer size */ + if (buf_size == 0) { + struct ieee80211_hw_mode *mode = conf->mode; + buf_size = IEEE80211_MIN_AMPDU_BUF; + buf_size = buf_size << mode->ht_info.ampdu_factor; + } + + tid_agg_rx = &sta->ampdu_mlme.tid_rx[tid]; + + /* examine state machine */ + spin_lock_bh(&sta->ampdu_mlme.ampdu_rx); + + if (tid_agg_rx->state != HT_AGG_STATE_IDLE) { +#ifdef CONFIG_MAC80211_HT_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "unexpected Block Ack Req from " + "%s on tid %u\n", + print_mac(mac, mgmt->sa), tid); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + goto end; + } + + /* prepare reordering buffer */ + tid_agg_rx->reorder_buf = + kmalloc(buf_size * sizeof(struct sk_buf *), GFP_ATOMIC); + if ((!tid_agg_rx->reorder_buf) && net_ratelimit()) { + printk(KERN_ERR "can not allocate reordering buffer " + "to tid %d\n", tid); + goto end; + } + memset(tid_agg_rx->reorder_buf, 0, + buf_size * sizeof(struct sk_buf *)); + + if (local->ops->ampdu_action) + ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START, + sta->addr, tid, start_seq_num); +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "Rx A-MPDU on tid %d result %d", tid, ret); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + + if (ret) { + kfree(tid_agg_rx->reorder_buf); + goto end; + } + + /* change state and send addba resp */ + tid_agg_rx->state = HT_AGG_STATE_OPERATIONAL; + tid_agg_rx->dialog_token = dialog_token; + tid_agg_rx->ssn = start_seq_num; + tid_agg_rx->head_seq_num = start_seq_num; + tid_agg_rx->buf_size = buf_size; + tid_agg_rx->timeout = timeout; + tid_agg_rx->stored_mpdu_num = 0; + status = WLAN_STATUS_SUCCESS; +end: + spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); + +end_no_lock: + ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token, + status, 1, buf_size, timeout); + sta_info_put(sta); +} + +static void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, + u16 initiator, u16 reason_code) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u16 params; + + skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 + + sizeof(mgmt->u.action.u.delba)); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer " + "for delba frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, da, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) + memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN); + else + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba)); + + mgmt->u.action.category = WLAN_CATEGORY_BACK; + mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; + params = (u16)(initiator << 11); /* bit 11 initiator */ + params |= (u16)(tid << 12); /* bit 15:12 TID number */ + + mgmt->u.action.u.delba.params = cpu_to_le16(params); + mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); + + ieee80211_sta_tx(dev, skb, 0); +} + +void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, + u16 initiator, u16 reason) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + struct sta_info *sta; + int ret, i; + + sta = sta_info_get(local, ra); + if (!sta) + return; + + /* check if TID is in operational state */ + spin_lock_bh(&sta->ampdu_mlme.ampdu_rx); + if (sta->ampdu_mlme.tid_rx[tid].state + != HT_AGG_STATE_OPERATIONAL) { + spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); + sta_info_put(sta); + return; + } + sta->ampdu_mlme.tid_rx[tid].state = + HT_AGG_STATE_REQ_STOP_BA_MSK | + (initiator << HT_AGG_STATE_INITIATOR_SHIFT); + spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); + + /* stop HW Rx aggregation. ampdu_action existence + * already verified in session init so we add the BUG_ON */ + BUG_ON(!local->ops->ampdu_action); + + ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP, + ra, tid, EINVAL); + if (ret) + printk(KERN_DEBUG "HW problem - can not stop rx " + "aggergation for tid %d\n", tid); + + /* shutdown timer has not expired */ + if (initiator != WLAN_BACK_TIMER) + del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]. + session_timer); + + /* check if this is a self generated aggregation halt */ + if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) + ieee80211_send_delba(dev, ra, tid, 0, reason); + + /* free the reordering buffer */ + for (i = 0; i < sta->ampdu_mlme.tid_rx[tid].buf_size; i++) { + if (sta->ampdu_mlme.tid_rx[tid].reorder_buf[i]) { + /* release the reordered frames */ + dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid].reorder_buf[i]); + sta->ampdu_mlme.tid_rx[tid].stored_mpdu_num--; + sta->ampdu_mlme.tid_rx[tid].reorder_buf[i] = NULL; + } + } + kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf); + + sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE; + sta_info_put(sta); +} + +static void ieee80211_sta_process_delba(struct net_device *dev, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + u16 tid, params; + u16 initiator; + DECLARE_MAC_BUF(mac); + + sta = sta_info_get(local, mgmt->sa); + if (!sta) + return; + + params = le16_to_cpu(mgmt->u.action.u.delba.params); + tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; + initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; + +#ifdef CONFIG_MAC80211_HT_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "delba from %s on tid %d reason code %d\n", + print_mac(mac, mgmt->sa), tid, + mgmt->u.action.u.delba.reason_code); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + + if (initiator == WLAN_BACK_INITIATOR) + ieee80211_sta_stop_rx_ba_session(dev, sta->addr, tid, + WLAN_BACK_INITIATOR, 0); + sta_info_put(sta); +} + +/* + * After receiving Block Ack Request (BAR) we activated a + * timer after each frame arrives from the originator. + * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed. + */ +void sta_rx_agg_session_timer_expired(unsigned long data) +{ + /* not an elegant detour, but there is no choice as the timer passes + * only one argument, and verious sta_info are needed here, so init + * flow in sta_info_add gives the TID as data, while the timer_to_id + * array gives the sta through container_of */ + u8 *ptid = (u8 *)data; + u8 *timer_to_id = ptid - *ptid; + struct sta_info *sta = container_of(timer_to_id, struct sta_info, + timer_to_tid[0]); + + printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); + ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid, + WLAN_BACK_TIMER, + WLAN_REASON_QSTA_TIMEOUT); +} + static void ieee80211_rx_mgmt_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, @@ -929,7 +1320,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev, DECLARE_MAC_BUF(mac); if (ifsta->state != IEEE80211_AUTHENTICATE && - sdata->type != IEEE80211_IF_TYPE_IBSS) { + sdata->vif.type != IEEE80211_IF_TYPE_IBSS) { printk(KERN_DEBUG "%s: authentication frame received from " "%s, but not in authenticate state - ignored\n", dev->name, print_mac(mac, mgmt->sa)); @@ -943,7 +1334,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev, return; } - if (sdata->type != IEEE80211_IF_TYPE_IBSS && + if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS && memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { printk(KERN_DEBUG "%s: authentication frame received from " "unknown AP (SA=%s BSSID=%s) - " @@ -952,7 +1343,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev, return; } - if (sdata->type != IEEE80211_IF_TYPE_IBSS && + if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS && memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) { printk(KERN_DEBUG "%s: authentication frame received from " "unknown BSSID (SA=%s BSSID=%s) - " @@ -970,7 +1361,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev, dev->name, print_mac(mac, mgmt->sa), auth_alg, auth_transaction, status_code); - if (sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { /* IEEE 802.11 standard does not require authentication in IBSS * networks and most implementations do not seem to use it. * However, try to reply to authentication attempts if someone @@ -1136,18 +1527,20 @@ static void ieee80211_rx_mgmt_disassoc(struct net_device *dev, } -static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, +static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_local *local = sdata->local; + struct net_device *dev = sdata->dev; struct ieee80211_hw_mode *mode; struct sta_info *sta; u32 rates; u16 capab_info, status_code, aid; struct ieee802_11_elems elems; + struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf; u8 *pos; int i, j; DECLARE_MAC_BUF(mac); @@ -1210,20 +1603,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, return; } - /* it probably doesn't, but if the frame includes an ERP value then - * update our stored copy */ - if (elems.erp_info && elems.erp_info_len >= 1) { - struct ieee80211_sta_bss *bss - = ieee80211_rx_bss_get(dev, ifsta->bssid, - local->hw.conf.channel, - ifsta->ssid, ifsta->ssid_len); - if (bss) { - bss->erp_value = elems.erp_info[0]; - bss->has_erp_value = 1; - ieee80211_rx_bss_put(dev, bss); - } - } - printk(KERN_DEBUG "%s: associated\n", dev->name); ifsta->aid = aid; ifsta->ap_capab = capab_info; @@ -1234,6 +1613,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, if (ifsta->assocresp_ies) memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len); + /* set AID, ieee80211_set_associated() will tell the driver */ + bss_conf->aid = aid; ieee80211_set_associated(dev, ifsta, 1); /* Add STA entry for the AP */ @@ -1276,6 +1657,19 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, } sta->supp_rates = rates; + if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && + local->ops->conf_ht) { + struct ieee80211_ht_bss_info bss_info; + + ieee80211_ht_cap_ie_to_ht_info( + (struct ieee80211_ht_cap *) + elems.ht_cap_elem, &sta->ht_info); + ieee80211_ht_addt_info_ie_to_ht_bss_info( + (struct ieee80211_ht_addt_info *) + elems.ht_info_elem, &bss_info); + ieee80211_hw_config_ht(local, 1, &sta->ht_info, &bss_info); + } + rate_control_rate_init(sta, local); if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) { @@ -1380,6 +1774,7 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) kfree(bss->wpa_ie); kfree(bss->rsn_ie); kfree(bss->wmm_ie); + kfree(bss->ht_ie); kfree(bss); } @@ -1449,7 +1844,7 @@ static void ieee80211_rx_bss_info(struct net_device *dev, timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); - if (sdata->type == IEEE80211_IF_TYPE_IBSS && beacon && + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && beacon && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { #ifdef CONFIG_MAC80211_IBSS_DEBUG static unsigned long last_tsf_debug = 0; @@ -1474,7 +1869,7 @@ static void ieee80211_rx_bss_info(struct net_device *dev, ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - if (sdata->type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 && (sta = sta_info_get(local, mgmt->sa))) { struct ieee80211_hw_mode *mode; @@ -1483,8 +1878,18 @@ static void ieee80211_rx_bss_info(struct net_device *dev, u32 supp_rates, prev_rates; int i, j; - mode = local->sta_scanning ? + mode = local->sta_sw_scanning ? local->scan_hw_mode : local->oper_hw_mode; + + if (local->sta_hw_scanning) { + /* search for the correct mode matches the beacon */ + list_for_each_entry(mode, &local->modes_list, list) + if (mode->mode == rx_status->phymode) + break; + + if (mode == NULL) + mode = local->oper_hw_mode; + } rates = mode->rates; num_rates = mode->num_rates; @@ -1627,7 +2032,22 @@ static void ieee80211_rx_bss_info(struct net_device *dev, bss->wmm_ie = NULL; bss->wmm_ie_len = 0; } - + if (elems.ht_cap_elem && + (!bss->ht_ie || bss->ht_ie_len != elems.ht_cap_elem_len || + memcmp(bss->ht_ie, elems.ht_cap_elem, elems.ht_cap_elem_len))) { + kfree(bss->ht_ie); + bss->ht_ie = kmalloc(elems.ht_cap_elem_len + 2, GFP_ATOMIC); + if (bss->ht_ie) { + memcpy(bss->ht_ie, elems.ht_cap_elem - 2, + elems.ht_cap_elem_len + 2); + bss->ht_ie_len = elems.ht_cap_elem_len + 2; + } else + bss->ht_ie_len = 0; + } else if (!elems.ht_cap_elem && bss->ht_ie) { + kfree(bss->ht_ie); + bss->ht_ie = NULL; + bss->ht_ie_len = 0; + } bss->hw_mode = rx_status->phymode; bss->freq = rx_status->freq; @@ -1672,11 +2092,14 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev, struct ieee80211_if_sta *ifsta; size_t baselen; struct ieee802_11_elems elems; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + u32 changed = 0; ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1); sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) return; ifsta = &sdata->u.sta; @@ -1692,12 +2115,31 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev, ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); if (elems.erp_info && elems.erp_info_len >= 1) - ieee80211_handle_erp_ie(dev, elems.erp_info[0]); + changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]); + + if (elems.ht_cap_elem && elems.ht_info_elem && + elems.wmm_param && local->ops->conf_ht && + conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) { + struct ieee80211_ht_bss_info bss_info; + + ieee80211_ht_addt_info_ie_to_ht_bss_info( + (struct ieee80211_ht_addt_info *) + elems.ht_info_elem, &bss_info); + /* check if AP changed bss inforamation */ + if ((conf->ht_bss_conf.primary_channel != + bss_info.primary_channel) || + (conf->ht_bss_conf.bss_cap != bss_info.bss_cap) || + (conf->ht_bss_conf.bss_op_mode != bss_info.bss_op_mode)) + ieee80211_hw_config_ht(local, 1, &conf->ht_conf, + &bss_info); + } if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) { ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, elems.wmm_param_len); } + + ieee80211_bss_info_change_notify(sdata, changed); } @@ -1719,7 +2161,7 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, DECLARE_MAC_BUF(mac3); #endif - if (sdata->type != IEEE80211_IF_TYPE_IBSS || + if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS || ifsta->state != IEEE80211_IBSS_JOINED || len < 24 + 2 || !ifsta->probe_resp) return; @@ -1775,6 +2217,40 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, ieee80211_sta_tx(dev, skb, 0); } +static void ieee80211_rx_mgmt_action(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + if (len < IEEE80211_MIN_ACTION_SIZE) + return; + + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_BACK: + switch (mgmt->u.action.u.addba_req.action_code) { + case WLAN_ACTION_ADDBA_REQ: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.addba_req))) + break; + ieee80211_sta_process_addba_request(dev, mgmt, len); + break; + case WLAN_ACTION_DELBA: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.delba))) + break; + ieee80211_sta_process_delba(dev, mgmt, len); + break; + default: + if (net_ratelimit()) + printk(KERN_DEBUG "%s: Rx unknown A-MPDU action\n", + dev->name); + break; + } + break; + default: + break; + } +} void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) @@ -1804,6 +2280,7 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, case IEEE80211_STYPE_REASSOC_RESP: case IEEE80211_STYPE_DEAUTH: case IEEE80211_STYPE_DISASSOC: + case IEEE80211_STYPE_ACTION: skb_queue_tail(&ifsta->skb_queue, skb); queue_work(local->hw.workqueue, &ifsta->work); return; @@ -1850,10 +2327,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev, ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len); break; case IEEE80211_STYPE_ASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0); + ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 0); break; case IEEE80211_STYPE_REASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1); + ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 1); break; case IEEE80211_STYPE_DEAUTH: ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len); @@ -1861,37 +2338,48 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev, case IEEE80211_STYPE_DISASSOC: ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len); break; + case IEEE80211_STYPE_ACTION: + ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len); + break; } kfree_skb(skb); } -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_txrx_result +ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) { struct ieee80211_mgmt *mgmt; u16 fc; - if (skb->len < 24) { - dev_kfree_skb(skb); - return; - } + if (skb->len < 2) + return TXRX_DROP; mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) + return TXRX_CONTINUE; + + if (skb->len < 24) + return TXRX_DROP; + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) { ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb->len, rx_status); + dev_kfree_skb(skb); + return TXRX_QUEUED; } else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) { ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status); + dev_kfree_skb(skb); + return TXRX_QUEUED; } } - - dev_kfree_skb(skb); + return TXRX_CONTINUE; } @@ -1981,13 +2469,13 @@ void ieee80211_sta_work(struct work_struct *work) if (!netif_running(dev)) return; - if (local->sta_scanning) + if (local->sta_sw_scanning || local->sta_hw_scanning) return; - if (sdata->type != IEEE80211_IF_TYPE_STA && - sdata->type != IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type != IEEE80211_IF_TYPE_STA && + sdata->vif.type != IEEE80211_IF_TYPE_IBSS) { printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface " - "(type=%d)\n", dev->name, sdata->type); + "(type=%d)\n", dev->name, sdata->vif.type); return; } ifsta = &sdata->u.sta; @@ -2082,7 +2570,7 @@ void ieee80211_sta_req_auth(struct net_device *dev, struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) return; if ((ifsta->flags & (IEEE80211_STA_BSSID_SET | @@ -2204,9 +2692,8 @@ static int ieee80211_sta_join_ibss(struct net_device *dev, struct sk_buff *skb; struct ieee80211_mgmt *mgmt; struct ieee80211_tx_control control; - struct ieee80211_rate *rate; struct ieee80211_hw_mode *mode; - struct rate_control_extra extra; + struct rate_selection ratesel; u8 *pos; struct ieee80211_sub_if_data *sdata; @@ -2291,18 +2778,17 @@ static int ieee80211_sta_join_ibss(struct net_device *dev, } memset(&control, 0, sizeof(control)); - memset(&extra, 0, sizeof(extra)); - extra.mode = local->oper_hw_mode; - rate = rate_control_get_rate(local, dev, skb, &extra); - if (!rate) { + rate_control_get_rate(dev, local->oper_hw_mode, skb, &ratesel); + if (!ratesel.rate) { printk(KERN_DEBUG "%s: Failed to determine TX rate " "for IBSS beacon\n", dev->name); break; } + control.vif = &sdata->vif; control.tx_rate = - ((sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) && - (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? - rate->val2 : rate->val; + (sdata->bss_conf.use_short_preamble && + (ratesel.rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + ratesel.rate->val2 : ratesel.rate->val; control.antenna_sel_tx = local->hw.conf.antenna_sel_tx; control.power_level = local->hw.conf.power_level; control.flags |= IEEE80211_TXCTL_NO_ACK; @@ -2552,7 +3038,7 @@ int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len) ifsta->flags |= IEEE80211_STA_SSID_SET; else ifsta->flags &= ~IEEE80211_STA_SSID_SET; - if (sdata->type == IEEE80211_IF_TYPE_IBSS && + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && !(ifsta->flags & IEEE80211_STA_BSSID_SET)) { ifsta->ibss_join_req = jiffies; ifsta->state = IEEE80211_IBSS_SEARCH; @@ -2639,9 +3125,15 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) union iwreq_data wrqu; local->last_scan_completed = jiffies; - wmb(); - local->sta_scanning = 0; + memset(&wrqu, 0, sizeof(wrqu)); + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + + if (local->sta_hw_scanning) { + local->sta_hw_scanning = 0; + goto done; + } + local->sta_sw_scanning = 0; if (ieee80211_hw_config(local)) printk(KERN_DEBUG "%s: failed to restore operational " "channel after scan\n", dev->name); @@ -2657,9 +3149,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) netif_tx_unlock_bh(local->mdev); - memset(&wrqu, 0, sizeof(wrqu)); - wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); - rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -2667,7 +3156,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) if (sdata->dev == local->mdev) continue; - if (sdata->type == IEEE80211_IF_TYPE_STA) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) ieee80211_send_nullfunc(local, sdata, 0); ieee80211_sta_timer((unsigned long)sdata); @@ -2677,8 +3166,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) } rcu_read_unlock(); +done: sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { struct ieee80211_if_sta *ifsta = &sdata->u.sta; if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) || (!ifsta->state == IEEE80211_IBSS_JOINED && @@ -2699,7 +3189,7 @@ void ieee80211_sta_scan_work(struct work_struct *work) int skip; unsigned long next_delay = 0; - if (!local->sta_scanning) + if (!local->sta_sw_scanning) return; switch (local->scan_state) { @@ -2713,7 +3203,7 @@ void ieee80211_sta_scan_work(struct work_struct *work) skip = !(local->enabled_modes & (1 << mode->mode)); chan = &mode->channels[local->scan_channel_idx]; if (!(chan->flag & IEEE80211_CHAN_W_SCAN) || - (sdata->type == IEEE80211_IF_TYPE_IBSS && + (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && !(chan->flag & IEEE80211_CHAN_W_IBSS)) || (local->hw_modes & local->enabled_modes & (1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B)) @@ -2762,7 +3252,7 @@ void ieee80211_sta_scan_work(struct work_struct *work) break; } - if (local->sta_scanning) + if (local->sta_sw_scanning) queue_delayed_work(local->hw.workqueue, &local->scan_work, next_delay); } @@ -2794,7 +3284,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev, * ResultCode: SUCCESS, INVALID_PARAMETERS */ - if (local->sta_scanning) { + if (local->sta_sw_scanning || local->sta_hw_scanning) { if (local->scan_dev == dev) return 0; return -EBUSY; @@ -2802,15 +3292,15 @@ static int ieee80211_sta_start_scan(struct net_device *dev, if (local->ops->hw_scan) { int rc = local->ops->hw_scan(local_to_hw(local), - ssid, ssid_len); + ssid, ssid_len); if (!rc) { - local->sta_scanning = 1; + local->sta_hw_scanning = 1; local->scan_dev = dev; } return rc; } - local->sta_scanning = 1; + local->sta_sw_scanning = 1; rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -2821,7 +3311,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev, continue; netif_stop_queue(sdata->dev); - if (sdata->type == IEEE80211_IF_TYPE_STA && + if (sdata->vif.type == IEEE80211_IF_TYPE_STA && (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)) ieee80211_send_nullfunc(local, sdata, 1); } @@ -2862,10 +3352,10 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) struct ieee80211_if_sta *ifsta = &sdata->u.sta; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) return ieee80211_sta_start_scan(dev, ssid, ssid_len); - if (local->sta_scanning) { + if (local->sta_sw_scanning || local->sta_hw_scanning) { if (local->scan_dev == dev) return 0; return -EBUSY; @@ -2894,15 +3384,6 @@ ieee80211_sta_scan_result(struct net_device *dev, if (!(local->enabled_modes & (1 << bss->hw_mode))) return current_ev; - if (local->scan_flags & IEEE80211_SCAN_WPA_ONLY && - !bss->wpa_ie && !bss->rsn_ie) - return current_ev; - - if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID && - (local->scan_ssid_len != bss->ssid_len || - memcmp(local->scan_ssid, bss->ssid, bss->ssid_len) != 0)) - return current_ev; - memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; @@ -3006,34 +3487,6 @@ ieee80211_sta_scan_result(struct net_device *dev, } } - do { - char *buf; - - if (!(local->scan_flags & IEEE80211_SCAN_EXTRA_INFO)) - break; - - buf = kmalloc(100, GFP_ATOMIC); - if (!buf) - break; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "bcn_int=%d", bss->beacon_int); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "capab=0x%04x", bss->capability); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); - - kfree(buf); - break; - } while (0); - return current_ev; } @@ -3122,8 +3575,8 @@ int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason) printk(KERN_DEBUG "%s: deauthenticate(reason=%d)\n", dev->name, reason); - if (sdata->type != IEEE80211_IF_TYPE_STA && - sdata->type != IEEE80211_IF_TYPE_IBSS) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA && + sdata->vif.type != IEEE80211_IF_TYPE_IBSS) return -EINVAL; ieee80211_send_deauth(dev, ifsta, reason); @@ -3140,7 +3593,7 @@ int ieee80211_sta_disassociate(struct net_device *dev, u16 reason) printk(KERN_DEBUG "%s: disassociate(reason=%d)\n", dev->name, reason); - if (sdata->type != IEEE80211_IF_TYPE_STA) + if (sdata->vif.type != IEEE80211_IF_TYPE_STA) return -EINVAL; if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED)) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 0b2328f7d67..ed57fb8e82f 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -49,8 +49,8 @@ static const u8 *get_mac_for_key(struct ieee80211_key *key) * address to indicate a transmit-only key. */ if (key->conf.alg != ALG_WEP && - (key->sdata->type == IEEE80211_IF_TYPE_AP || - key->sdata->type == IEEE80211_IF_TYPE_VLAN)) + (key->sdata->vif.type == IEEE80211_IF_TYPE_AP || + key->sdata->vif.type == IEEE80211_IF_TYPE_VLAN)) addr = zero_addr; if (key->sta) @@ -172,7 +172,7 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, if (sta->flags & WLAN_STA_WME) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } else { - if (sdata->type == IEEE80211_IF_TYPE_STA) { + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { struct sta_info *ap; /* same here, the AP could be using QoS */ diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h new file mode 100644 index 00000000000..04afc13ed82 --- /dev/null +++ b/net/mac80211/rc80211_pid.h @@ -0,0 +1,285 @@ +/* + * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> + * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RC80211_PID_H +#define RC80211_PID_H + +/* Sampling period for measuring percentage of failed frames in ms. */ +#define RC_PID_INTERVAL 125 + +/* Exponential averaging smoothness (used for I part of PID controller) */ +#define RC_PID_SMOOTHING_SHIFT 3 +#define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT) + +/* Sharpening factor (used for D part of PID controller) */ +#define RC_PID_SHARPENING_FACTOR 0 +#define RC_PID_SHARPENING_DURATION 0 + +/* Fixed point arithmetic shifting amount. */ +#define RC_PID_ARITH_SHIFT 8 + +/* Fixed point arithmetic factor. */ +#define RC_PID_ARITH_FACTOR (1 << RC_PID_ARITH_SHIFT) + +/* Proportional PID component coefficient. */ +#define RC_PID_COEFF_P 15 +/* Integral PID component coefficient. */ +#define RC_PID_COEFF_I 9 +/* Derivative PID component coefficient. */ +#define RC_PID_COEFF_D 15 + +/* Target failed frames rate for the PID controller. NB: This effectively gives + * maximum failed frames percentage we're willing to accept. If the wireless + * link quality is good, the controller will fail to adjust failed frames + * percentage to the target. This is intentional. + */ +#define RC_PID_TARGET_PF 14 + +/* Rate behaviour normalization quantity over time. */ +#define RC_PID_NORM_OFFSET 3 + +/* Push high rates right after loading. */ +#define RC_PID_FAST_START 0 + +/* Arithmetic right shift for positive and negative values for ISO C. */ +#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \ + (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y) + +enum rc_pid_event_type { + RC_PID_EVENT_TYPE_TX_STATUS, + RC_PID_EVENT_TYPE_RATE_CHANGE, + RC_PID_EVENT_TYPE_TX_RATE, + RC_PID_EVENT_TYPE_PF_SAMPLE, +}; + +union rc_pid_event_data { + /* RC_PID_EVENT_TX_STATUS */ + struct { + struct ieee80211_tx_status tx_status; + }; + /* RC_PID_EVENT_TYPE_RATE_CHANGE */ + /* RC_PID_EVENT_TYPE_TX_RATE */ + struct { + int index; + int rate; + }; + /* RC_PID_EVENT_TYPE_PF_SAMPLE */ + struct { + s32 pf_sample; + s32 prop_err; + s32 int_err; + s32 der_err; + }; +}; + +struct rc_pid_event { + /* The time when the event occured */ + unsigned long timestamp; + + /* Event ID number */ + unsigned int id; + + /* Type of event */ + enum rc_pid_event_type type; + + /* type specific data */ + union rc_pid_event_data data; +}; + +/* Size of the event ring buffer. */ +#define RC_PID_EVENT_RING_SIZE 32 + +struct rc_pid_event_buffer { + /* Counter that generates event IDs */ + unsigned int ev_count; + + /* Ring buffer of events */ + struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE]; + + /* Index to the entry in events_buf to be reused */ + unsigned int next_entry; + + /* Lock that guards against concurrent access to this buffer struct */ + spinlock_t lock; + + /* Wait queue for poll/select and blocking I/O */ + wait_queue_head_t waitqueue; +}; + +struct rc_pid_events_file_info { + /* The event buffer we read */ + struct rc_pid_event_buffer *events; + + /* The entry we have should read next */ + unsigned int next_entry; +}; + +/** + * struct rc_pid_debugfs_entries - tunable parameters + * + * Algorithm parameters, tunable via debugfs. + * @dir: the debugfs directory for a specific phy + * @target: target percentage for failed frames + * @sampling_period: error sampling interval in milliseconds + * @coeff_p: absolute value of the proportional coefficient + * @coeff_i: absolute value of the integral coefficient + * @coeff_d: absolute value of the derivative coefficient + * @smoothing_shift: absolute value of the integral smoothing factor (i.e. + * amount of smoothing introduced by the exponential moving average) + * @sharpen_factor: absolute value of the derivative sharpening factor (i.e. + * amount of emphasis given to the derivative term after low activity + * events) + * @sharpen_duration: duration of the sharpening effect after the detected low + * activity event, relative to sampling_period + * @norm_offset: amount of normalization periodically performed on the learnt + * rate behaviour values (lower means we should trust more what we learnt + * about behaviour of rates, higher means we should trust more the natural + * ordering of rates) + * @fast_start: if Y, push high rates right after initialization + */ +struct rc_pid_debugfs_entries { + struct dentry *dir; + struct dentry *target; + struct dentry *sampling_period; + struct dentry *coeff_p; + struct dentry *coeff_i; + struct dentry *coeff_d; + struct dentry *smoothing_shift; + struct dentry *sharpen_factor; + struct dentry *sharpen_duration; + struct dentry *norm_offset; + struct dentry *fast_start; +}; + +void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, + struct ieee80211_tx_status *stat); + +void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, + int index, int rate); + +void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, + int index, int rate); + +void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, + s32 pf_sample, s32 prop_err, + s32 int_err, s32 der_err); + +void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, + struct dentry *dir); + +void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta); + +struct rc_pid_sta_info { + unsigned long last_change; + unsigned long last_sample; + + u32 tx_num_failed; + u32 tx_num_xmit; + + /* Average failed frames percentage error (i.e. actual vs. target + * percentage), scaled by RC_PID_SMOOTHING. This value is computed + * using using an exponential weighted average technique: + * + * (RC_PID_SMOOTHING - 1) * err_avg_old + err + * err_avg = ------------------------------------------ + * RC_PID_SMOOTHING + * + * where err_avg is the new approximation, err_avg_old the previous one + * and err is the error w.r.t. to the current failed frames percentage + * sample. Note that the bigger RC_PID_SMOOTHING the more weight is + * given to the previous estimate, resulting in smoother behavior (i.e. + * corresponding to a longer integration window). + * + * For computation, we actually don't use the above formula, but this + * one: + * + * err_avg_scaled = err_avg_old_scaled - err_avg_old + err + * + * where: + * err_avg_scaled = err * RC_PID_SMOOTHING + * err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING + * + * This avoids floating point numbers and the per_failed_old value can + * easily be obtained by shifting per_failed_old_scaled right by + * RC_PID_SMOOTHING_SHIFT. + */ + s32 err_avg_sc; + + /* Last framed failes percentage sample. */ + u32 last_pf; + + /* Sharpening needed. */ + u8 sharp_cnt; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* Event buffer */ + struct rc_pid_event_buffer events; + + /* Events debugfs file entry */ + struct dentry *events_entry; +#endif +}; + +/* Algorithm parameters. We keep them on a per-algorithm approach, so they can + * be tuned individually for each interface. + */ +struct rc_pid_rateinfo { + + /* Map sorted rates to rates in ieee80211_hw_mode. */ + int index; + + /* Map rates in ieee80211_hw_mode to sorted rates. */ + int rev_index; + + /* Did we do any measurement on this rate? */ + bool valid; + + /* Comparison with the lowest rate. */ + int diff; +}; + +struct rc_pid_info { + + /* The failed frames percentage target. */ + unsigned int target; + + /* Rate at which failed frames percentage is sampled in 0.001s. */ + unsigned int sampling_period; + + /* P, I and D coefficients. */ + int coeff_p; + int coeff_i; + int coeff_d; + + /* Exponential averaging shift. */ + unsigned int smoothing_shift; + + /* Sharpening factor and duration. */ + unsigned int sharpen_factor; + unsigned int sharpen_duration; + + /* Normalization offset. */ + unsigned int norm_offset; + + /* Fast starst parameter. */ + unsigned int fast_start; + + /* Rates information. */ + struct rc_pid_rateinfo *rinfo; + + /* Index of the last used rate. */ + int oldrate; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* Debugfs entries created for the parameters above. */ + struct rc_pid_debugfs_entries dentries; +#endif +}; + +#endif /* RC80211_PID_H */ diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c new file mode 100644 index 00000000000..554c4baed6f --- /dev/null +++ b/net/mac80211/rc80211_pid_algo.c @@ -0,0 +1,549 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> + * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/netdevice.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/debugfs.h> +#include <net/mac80211.h> +#include "ieee80211_rate.h" + +#include "rc80211_pid.h" + + +/* This is an implementation of a TX rate control algorithm that uses a PID + * controller. Given a target failed frames rate, the controller decides about + * TX rate changes to meet the target failed frames rate. + * + * The controller basically computes the following: + * + * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening) + * + * where + * adj adjustment value that is used to switch TX rate (see below) + * err current error: target vs. current failed frames percentage + * last_err last error + * err_avg average (i.e. poor man's integral) of recent errors + * sharpening non-zero when fast response is needed (i.e. right after + * association or no frames sent for a long time), heading + * to zero over time + * CP Proportional coefficient + * CI Integral coefficient + * CD Derivative coefficient + * + * CP, CI, CD are subject to careful tuning. + * + * The integral component uses a exponential moving average approach instead of + * an actual sliding window. The advantage is that we don't need to keep an + * array of the last N error values and computation is easier. + * + * Once we have the adj value, we map it to a rate by means of a learning + * algorithm. This algorithm keeps the state of the percentual failed frames + * difference between rates. The behaviour of the lowest available rate is kept + * as a reference value, and every time we switch between two rates, we compute + * the difference between the failed frames each rate exhibited. By doing so, + * we compare behaviours which different rates exhibited in adjacent timeslices, + * thus the comparison is minimally affected by external conditions. This + * difference gets propagated to the whole set of measurements, so that the + * reference is always the same. Periodically, we normalize this set so that + * recent events weigh the most. By comparing the adj value with this set, we + * avoid pejorative switches to lower rates and allow for switches to higher + * rates if they behaved well. + * + * Note that for the computations we use a fixed-point representation to avoid + * floating point arithmetic. Hence, all values are shifted left by + * RC_PID_ARITH_SHIFT. + */ + + +/* Shift the adjustment so that we won't switch to a lower rate if it exhibited + * a worse failed frames behaviour and we'll choose the highest rate whose + * failed frames behaviour is not worse than the one of the original rate + * target. While at it, check that the adjustment is within the ranges. Then, + * provide the new rate index. */ +static int rate_control_pid_shift_adjust(struct rc_pid_rateinfo *r, + int adj, int cur, int l) +{ + int i, j, k, tmp; + + j = r[cur].rev_index; + i = j + adj; + + if (i < 0) + return r[0].index; + if (i >= l - 1) + return r[l - 1].index; + + tmp = i; + + if (adj < 0) { + for (k = j; k >= i; k--) + if (r[k].diff <= r[j].diff) + tmp = k; + } else { + for (k = i + 1; k + i < l; k++) + if (r[k].diff <= r[i].diff) + tmp = k; + } + + return r[tmp].index; +} + +static void rate_control_pid_adjust_rate(struct ieee80211_local *local, + struct sta_info *sta, int adj, + struct rc_pid_rateinfo *rinfo) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hw_mode *mode; + int newidx; + int maxrate; + int back = (adj > 0) ? 1 : -1; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + + mode = local->oper_hw_mode; + maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1; + + newidx = rate_control_pid_shift_adjust(rinfo, adj, sta->txrate, + mode->num_rates); + + while (newidx != sta->txrate) { + if (rate_supported(sta, mode, newidx) && + (maxrate < 0 || newidx <= maxrate)) { + sta->txrate = newidx; + break; + } + + newidx += back; + } + +#ifdef CONFIG_MAC80211_DEBUGFS + rate_control_pid_event_rate_change( + &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events, + newidx, mode->rates[newidx].rate); +#endif +} + +/* Normalize the failed frames per-rate differences. */ +static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l) +{ + int i, norm_offset = pinfo->norm_offset; + struct rc_pid_rateinfo *r = pinfo->rinfo; + + if (r[0].diff > norm_offset) + r[0].diff -= norm_offset; + else if (r[0].diff < -norm_offset) + r[0].diff += norm_offset; + for (i = 0; i < l - 1; i++) + if (r[i + 1].diff > r[i].diff + norm_offset) + r[i + 1].diff -= norm_offset; + else if (r[i + 1].diff <= r[i].diff) + r[i + 1].diff += norm_offset; +} + +static void rate_control_pid_sample(struct rc_pid_info *pinfo, + struct ieee80211_local *local, + struct sta_info *sta) +{ + struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv; + struct rc_pid_rateinfo *rinfo = pinfo->rinfo; + struct ieee80211_hw_mode *mode; + u32 pf; + s32 err_avg; + u32 err_prop; + u32 err_int; + u32 err_der; + int adj, i, j, tmp; + unsigned long period; + + mode = local->oper_hw_mode; + spinfo = sta->rate_ctrl_priv; + + /* In case nothing happened during the previous control interval, turn + * the sharpening factor on. */ + period = (HZ * pinfo->sampling_period + 500) / 1000; + if (!period) + period = 1; + if (jiffies - spinfo->last_sample > 2 * period) + spinfo->sharp_cnt = pinfo->sharpen_duration; + + spinfo->last_sample = jiffies; + + /* This should never happen, but in case, we assume the old sample is + * still a good measurement and copy it. */ + if (unlikely(spinfo->tx_num_xmit == 0)) + pf = spinfo->last_pf; + else { + pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; + pf <<= RC_PID_ARITH_SHIFT; + } + + spinfo->tx_num_xmit = 0; + spinfo->tx_num_failed = 0; + + /* If we just switched rate, update the rate behaviour info. */ + if (pinfo->oldrate != sta->txrate) { + + i = rinfo[pinfo->oldrate].rev_index; + j = rinfo[sta->txrate].rev_index; + + tmp = (pf - spinfo->last_pf); + tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT); + + rinfo[j].diff = rinfo[i].diff + tmp; + pinfo->oldrate = sta->txrate; + } + rate_control_pid_normalize(pinfo, mode->num_rates); + + /* Compute the proportional, integral and derivative errors. */ + err_prop = (pinfo->target << RC_PID_ARITH_SHIFT) - pf; + + err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift; + spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop; + err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift; + + err_der = (pf - spinfo->last_pf) * + (1 + pinfo->sharpen_factor * spinfo->sharp_cnt); + spinfo->last_pf = pf; + if (spinfo->sharp_cnt) + spinfo->sharp_cnt--; + +#ifdef CONFIG_MAC80211_DEBUGFS + rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int, + err_der); +#endif + + /* Compute the controller output. */ + adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i + + err_der * pinfo->coeff_d); + adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT); + + /* Change rate. */ + if (adj) + rate_control_pid_adjust_rate(local, sta, adj, rinfo); +} + +static void rate_control_pid_tx_status(void *priv, struct net_device *dev, + struct sk_buff *skb, + struct ieee80211_tx_status *status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_sub_if_data *sdata; + struct rc_pid_info *pinfo = priv; + struct sta_info *sta; + struct rc_pid_sta_info *spinfo; + unsigned long period; + + sta = sta_info_get(local, hdr->addr1); + + if (!sta) + return; + + /* Don't update the state if we're not controlling the rate. */ + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { + sta->txrate = sdata->bss->max_ratectrl_rateidx; + return; + } + + /* Ignore all frames that were sent with a different rate than the rate + * we currently advise mac80211 to use. */ + if (status->control.rate != &local->oper_hw_mode->rates[sta->txrate]) + goto ignore; + + spinfo = sta->rate_ctrl_priv; + spinfo->tx_num_xmit++; + +#ifdef CONFIG_MAC80211_DEBUGFS + rate_control_pid_event_tx_status(&spinfo->events, status); +#endif + + /* We count frames that totally failed to be transmitted as two bad + * frames, those that made it out but had some retries as one good and + * one bad frame. */ + if (status->excessive_retries) { + spinfo->tx_num_failed += 2; + spinfo->tx_num_xmit++; + } else if (status->retry_count) { + spinfo->tx_num_failed++; + spinfo->tx_num_xmit++; + } + + if (status->excessive_retries) { + sta->tx_retry_failed++; + sta->tx_num_consecutive_failures++; + sta->tx_num_mpdu_fail++; + } else { + sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; + sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; + sta->last_ack_rssi[2] = status->ack_signal; + sta->tx_num_consecutive_failures = 0; + sta->tx_num_mpdu_ok++; + } + sta->tx_retry_count += status->retry_count; + sta->tx_num_mpdu_fail += status->retry_count; + + /* Update PID controller state. */ + period = (HZ * pinfo->sampling_period + 500) / 1000; + if (!period) + period = 1; + if (time_after(jiffies, spinfo->last_sample + period)) + rate_control_pid_sample(pinfo, local, sta); + +ignore: + sta_info_put(sta); +} + +static void rate_control_pid_get_rate(void *priv, struct net_device *dev, + struct ieee80211_hw_mode *mode, + struct sk_buff *skb, + struct rate_selection *sel) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + int rateidx; + u16 fc; + + sta = sta_info_get(local, hdr->addr1); + + /* Send management frames and broadcast/multicast data using lowest + * rate. */ + fc = le16_to_cpu(hdr->frame_control); + if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || + is_multicast_ether_addr(hdr->addr1) || !sta) { + sel->rate = rate_lowest(local, mode, sta); + if (sta) + sta_info_put(sta); + return; + } + + /* If a forced rate is in effect, select it. */ + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) + sta->txrate = sdata->bss->force_unicast_rateidx; + + rateidx = sta->txrate; + + if (rateidx >= mode->num_rates) + rateidx = mode->num_rates - 1; + + sta->last_txrate = rateidx; + + sta_info_put(sta); + + sel->rate = &mode->rates[rateidx]; + +#ifdef CONFIG_MAC80211_DEBUGFS + rate_control_pid_event_tx_rate( + &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events, + rateidx, mode->rates[rateidx].rate); +#endif +} + +static void rate_control_pid_rate_init(void *priv, void *priv_sta, + struct ieee80211_local *local, + struct sta_info *sta) +{ + /* TODO: This routine should consider using RSSI from previous packets + * as we need to have IEEE 802.1X auth succeed immediately after assoc.. + * Until that method is implemented, we will use the lowest supported + * rate as a workaround. */ + sta->txrate = rate_lowest_index(local, local->oper_hw_mode, sta); +} + +static void *rate_control_pid_alloc(struct ieee80211_local *local) +{ + struct rc_pid_info *pinfo; + struct rc_pid_rateinfo *rinfo; + struct ieee80211_hw_mode *mode; + int i, j, tmp; + bool s; +#ifdef CONFIG_MAC80211_DEBUGFS + struct rc_pid_debugfs_entries *de; +#endif + + pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC); + if (!pinfo) + return NULL; + + /* We can safely assume that oper_hw_mode won't change unless we get + * reinitialized. */ + mode = local->oper_hw_mode; + rinfo = kmalloc(sizeof(*rinfo) * mode->num_rates, GFP_ATOMIC); + if (!rinfo) { + kfree(pinfo); + return NULL; + } + + /* Sort the rates. This is optimized for the most common case (i.e. + * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed + * mapping too. */ + for (i = 0; i < mode->num_rates; i++) { + rinfo[i].index = i; + rinfo[i].rev_index = i; + if (pinfo->fast_start) + rinfo[i].diff = 0; + else + rinfo[i].diff = i * pinfo->norm_offset; + } + for (i = 1; i < mode->num_rates; i++) { + s = 0; + for (j = 0; j < mode->num_rates - i; j++) + if (unlikely(mode->rates[rinfo[j].index].rate > + mode->rates[rinfo[j + 1].index].rate)) { + tmp = rinfo[j].index; + rinfo[j].index = rinfo[j + 1].index; + rinfo[j + 1].index = tmp; + rinfo[rinfo[j].index].rev_index = j; + rinfo[rinfo[j + 1].index].rev_index = j + 1; + s = 1; + } + if (!s) + break; + } + + pinfo->target = RC_PID_TARGET_PF; + pinfo->sampling_period = RC_PID_INTERVAL; + pinfo->coeff_p = RC_PID_COEFF_P; + pinfo->coeff_i = RC_PID_COEFF_I; + pinfo->coeff_d = RC_PID_COEFF_D; + pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT; + pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR; + pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION; + pinfo->norm_offset = RC_PID_NORM_OFFSET; + pinfo->fast_start = RC_PID_FAST_START; + pinfo->rinfo = rinfo; + pinfo->oldrate = 0; + +#ifdef CONFIG_MAC80211_DEBUGFS + de = &pinfo->dentries; + de->dir = debugfs_create_dir("rc80211_pid", + local->hw.wiphy->debugfsdir); + de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR, + de->dir, &pinfo->target); + de->sampling_period = debugfs_create_u32("sampling_period", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->sampling_period); + de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, + de->dir, &pinfo->coeff_p); + de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, + de->dir, &pinfo->coeff_i); + de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, + de->dir, &pinfo->coeff_d); + de->smoothing_shift = debugfs_create_u32("smoothing_shift", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->smoothing_shift); + de->sharpen_factor = debugfs_create_u32("sharpen_factor", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->sharpen_factor); + de->sharpen_duration = debugfs_create_u32("sharpen_duration", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->sharpen_duration); + de->norm_offset = debugfs_create_u32("norm_offset", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->norm_offset); + de->fast_start = debugfs_create_bool("fast_start", + S_IRUSR | S_IWUSR, de->dir, + &pinfo->fast_start); +#endif + + return pinfo; +} + +static void rate_control_pid_free(void *priv) +{ + struct rc_pid_info *pinfo = priv; +#ifdef CONFIG_MAC80211_DEBUGFS + struct rc_pid_debugfs_entries *de = &pinfo->dentries; + + debugfs_remove(de->fast_start); + debugfs_remove(de->norm_offset); + debugfs_remove(de->sharpen_duration); + debugfs_remove(de->sharpen_factor); + debugfs_remove(de->smoothing_shift); + debugfs_remove(de->coeff_d); + debugfs_remove(de->coeff_i); + debugfs_remove(de->coeff_p); + debugfs_remove(de->sampling_period); + debugfs_remove(de->target); + debugfs_remove(de->dir); +#endif + + kfree(pinfo->rinfo); + kfree(pinfo); +} + +static void rate_control_pid_clear(void *priv) +{ +} + +static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp) +{ + struct rc_pid_sta_info *spinfo; + + spinfo = kzalloc(sizeof(*spinfo), gfp); + if (spinfo == NULL) + return NULL; + + spinfo->last_sample = jiffies; + +#ifdef CONFIG_MAC80211_DEBUGFS + spin_lock_init(&spinfo->events.lock); + init_waitqueue_head(&spinfo->events.waitqueue); +#endif + + return spinfo; +} + +static void rate_control_pid_free_sta(void *priv, void *priv_sta) +{ + struct rc_pid_sta_info *spinfo = priv_sta; + kfree(spinfo); +} + +static struct rate_control_ops mac80211_rcpid = { + .name = "pid", + .tx_status = rate_control_pid_tx_status, + .get_rate = rate_control_pid_get_rate, + .rate_init = rate_control_pid_rate_init, + .clear = rate_control_pid_clear, + .alloc = rate_control_pid_alloc, + .free = rate_control_pid_free, + .alloc_sta = rate_control_pid_alloc_sta, + .free_sta = rate_control_pid_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rate_control_pid_add_sta_debugfs, + .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs, +#endif +}; + +MODULE_DESCRIPTION("PID controller based rate control algorithm"); +MODULE_AUTHOR("Stefano Brivio"); +MODULE_AUTHOR("Mattias Nissler"); +MODULE_LICENSE("GPL"); + +int __init rc80211_pid_init(void) +{ + return ieee80211_rate_control_register(&mac80211_rcpid); +} + +void __exit rc80211_pid_exit(void) +{ + ieee80211_rate_control_unregister(&mac80211_rcpid); +} + +#ifdef CONFIG_MAC80211_RC_PID_MODULE +module_init(rc80211_pid_init); +module_exit(rc80211_pid_exit); +#endif diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c new file mode 100644 index 00000000000..88b8dc9999b --- /dev/null +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -0,0 +1,223 @@ +/* + * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/spinlock.h> +#include <linux/poll.h> +#include <linux/netdevice.h> +#include <linux/types.h> +#include <linux/skbuff.h> + +#include <net/mac80211.h> +#include "ieee80211_rate.h" + +#include "rc80211_pid.h" + +static void rate_control_pid_event(struct rc_pid_event_buffer *buf, + enum rc_pid_event_type type, + union rc_pid_event_data *data) +{ + struct rc_pid_event *ev; + unsigned long status; + + spin_lock_irqsave(&buf->lock, status); + ev = &(buf->ring[buf->next_entry]); + buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE; + + ev->timestamp = jiffies; + ev->id = buf->ev_count++; + ev->type = type; + ev->data = *data; + + spin_unlock_irqrestore(&buf->lock, status); + + wake_up_all(&buf->waitqueue); +} + +void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf, + struct ieee80211_tx_status *stat) +{ + union rc_pid_event_data evd; + + memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_status)); + rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd); +} + +void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf, + int index, int rate) +{ + union rc_pid_event_data evd; + + evd.index = index; + evd.rate = rate; + rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd); +} + +void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf, + int index, int rate) +{ + union rc_pid_event_data evd; + + evd.index = index; + evd.rate = rate; + rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd); +} + +void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf, + s32 pf_sample, s32 prop_err, + s32 int_err, s32 der_err) +{ + union rc_pid_event_data evd; + + evd.pf_sample = pf_sample; + evd.prop_err = prop_err; + evd.int_err = int_err; + evd.der_err = der_err; + rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd); +} + +static int rate_control_pid_events_open(struct inode *inode, struct file *file) +{ + struct rc_pid_sta_info *sinfo = inode->i_private; + struct rc_pid_event_buffer *events = &sinfo->events; + struct rc_pid_events_file_info *file_info; + unsigned int status; + + /* Allocate a state struct */ + file_info = kmalloc(sizeof(*file_info), GFP_KERNEL); + if (file_info == NULL) + return -ENOMEM; + + spin_lock_irqsave(&events->lock, status); + + file_info->next_entry = events->next_entry; + file_info->events = events; + + spin_unlock_irqrestore(&events->lock, status); + + file->private_data = file_info; + + return 0; +} + +static int rate_control_pid_events_release(struct inode *inode, + struct file *file) +{ + struct rc_pid_events_file_info *file_info = file->private_data; + + kfree(file_info); + + return 0; +} + +static unsigned int rate_control_pid_events_poll(struct file *file, + poll_table *wait) +{ + struct rc_pid_events_file_info *file_info = file->private_data; + + poll_wait(file, &file_info->events->waitqueue, wait); + + return POLLIN | POLLRDNORM; +} + +#define RC_PID_PRINT_BUF_SIZE 64 + +static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, + size_t length, loff_t *offset) +{ + struct rc_pid_events_file_info *file_info = file->private_data; + struct rc_pid_event_buffer *events = file_info->events; + struct rc_pid_event *ev; + char pb[RC_PID_PRINT_BUF_SIZE]; + int ret; + int p; + unsigned int status; + + /* Check if there is something to read. */ + if (events->next_entry == file_info->next_entry) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* Wait */ + ret = wait_event_interruptible(events->waitqueue, + events->next_entry != file_info->next_entry); + + if (ret) + return ret; + } + + /* Write out one event per call. I don't care whether it's a little + * inefficient, this is debugging code anyway. */ + spin_lock_irqsave(&events->lock, status); + + /* Get an event */ + ev = &(events->ring[file_info->next_entry]); + file_info->next_entry = (file_info->next_entry + 1) % + RC_PID_EVENT_RING_SIZE; + + /* Print information about the event. Note that userpace needs to + * provide large enough buffers. */ + length = length < RC_PID_PRINT_BUF_SIZE ? + length : RC_PID_PRINT_BUF_SIZE; + p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); + switch (ev->type) { + case RC_PID_EVENT_TYPE_TX_STATUS: + p += snprintf(pb + p, length - p, "tx_status %u %u", + ev->data.tx_status.excessive_retries, + ev->data.tx_status.retry_count); + break; + case RC_PID_EVENT_TYPE_RATE_CHANGE: + p += snprintf(pb + p, length - p, "rate_change %d %d", + ev->data.index, ev->data.rate); + break; + case RC_PID_EVENT_TYPE_TX_RATE: + p += snprintf(pb + p, length - p, "tx_rate %d %d", + ev->data.index, ev->data.rate); + break; + case RC_PID_EVENT_TYPE_PF_SAMPLE: + p += snprintf(pb + p, length - p, + "pf_sample %d %d %d %d", + ev->data.pf_sample, ev->data.prop_err, + ev->data.int_err, ev->data.der_err); + break; + } + p += snprintf(pb + p, length - p, "\n"); + + spin_unlock_irqrestore(&events->lock, status); + + if (copy_to_user(buf, pb, p)) + return -EFAULT; + + return p; +} + +#undef RC_PID_PRINT_BUF_SIZE + +static struct file_operations rc_pid_fop_events = { + .owner = THIS_MODULE, + .read = rate_control_pid_events_read, + .poll = rate_control_pid_events_poll, + .open = rate_control_pid_events_open, + .release = rate_control_pid_events_release, +}; + +void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct rc_pid_sta_info *spinfo = priv_sta; + + spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO, + dir, spinfo, + &rc_pid_fop_events); +} + +void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta) +{ + struct rc_pid_sta_info *spinfo = priv_sta; + + debugfs_remove(spinfo->events_entry); +} diff --git a/net/mac80211/rc80211_simple.c b/net/mac80211/rc80211_simple.c index da72737364e..934676d687d 100644 --- a/net/mac80211/rc80211_simple.c +++ b/net/mac80211/rc80211_simple.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/skbuff.h> #include <linux/compiler.h> +#include <linux/module.h> #include <net/mac80211.h> #include "ieee80211_i.h" @@ -23,6 +24,8 @@ /* This is a minimal implementation of TX rate controlling that can be used * as the default when no improved mechanisms are available. */ +#define RATE_CONTROL_NUM_DOWN 20 +#define RATE_CONTROL_NUM_UP 15 #define RATE_CONTROL_EMERG_DEC 2 #define RATE_CONTROL_INTERVAL (HZ / 20) @@ -87,26 +90,6 @@ static void rate_control_rate_dec(struct ieee80211_local *local, } } - -static struct ieee80211_rate * -rate_control_lowest_rate(struct ieee80211_local *local, - struct ieee80211_hw_mode *mode) -{ - int i; - - for (i = 0; i < mode->num_rates; i++) { - struct ieee80211_rate *rate = &mode->rates[i]; - - if (rate->flags & IEEE80211_RATE_SUPPORTED) - return rate; - } - - printk(KERN_DEBUG "rate_control_lowest_rate - no supported rates " - "found\n"); - return &mode->rates[0]; -} - - struct global_rate_control { int dummy; }; @@ -216,35 +199,33 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev, } -static struct ieee80211_rate * +static void rate_control_simple_get_rate(void *priv, struct net_device *dev, + struct ieee80211_hw_mode *mode, struct sk_buff *skb, - struct rate_control_extra *extra) + struct rate_selection *sel) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct ieee80211_hw_mode *mode = extra->mode; + struct ieee80211_sub_if_data *sdata; struct sta_info *sta; - int rateidx, nonerp_idx; + int rateidx; u16 fc; - memset(extra, 0, sizeof(*extra)); + sta = sta_info_get(local, hdr->addr1); + /* Send management frames and broadcast/multicast data using lowest + * rate. */ fc = le16_to_cpu(hdr->frame_control); if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || - (hdr->addr1[0] & 0x01)) { - /* Send management frames and broadcast/multicast data using - * lowest rate. */ - /* TODO: this could probably be improved.. */ - return rate_control_lowest_rate(local, mode); + is_multicast_ether_addr(hdr->addr1) || !sta) { + sel->rate = rate_lowest(local, mode, sta); + if (sta) + sta_info_put(sta); + return; } - sta = sta_info_get(local, hdr->addr1); - - if (!sta) - return rate_control_lowest_rate(local, mode); - + /* If a forced rate is in effect, select it. */ sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) sta->txrate = sdata->bss->force_unicast_rateidx; @@ -255,17 +236,10 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev, rateidx = mode->num_rates - 1; sta->last_txrate = rateidx; - nonerp_idx = rateidx; - while (nonerp_idx > 0 && - ((mode->rates[nonerp_idx].flags & IEEE80211_RATE_ERP) || - !(mode->rates[nonerp_idx].flags & IEEE80211_RATE_SUPPORTED) || - !(sta->supp_rates & BIT(nonerp_idx)))) - nonerp_idx--; - extra->nonerp = &mode->rates[nonerp_idx]; sta_info_put(sta); - return &mode->rates[rateidx]; + sel->rate = &mode->rates[rateidx]; } @@ -391,7 +365,7 @@ static void rate_control_simple_remove_sta_debugfs(void *priv, void *priv_sta) } #endif -struct rate_control_ops mac80211_rcsimple = { +static struct rate_control_ops mac80211_rcsimple = { .name = "simple", .tx_status = rate_control_simple_tx_status, .get_rate = rate_control_simple_get_rate, @@ -406,3 +380,21 @@ struct rate_control_ops mac80211_rcsimple = { .remove_sta_debugfs = rate_control_simple_remove_sta_debugfs, #endif }; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple rate control algorithm"); + +int __init rc80211_simple_init(void) +{ + return ieee80211_rate_control_register(&mac80211_rcsimple); +} + +void __exit rc80211_simple_exit(void) +{ + ieee80211_rate_control_unregister(&mac80211_rcsimple); +} + +#ifdef CONFIG_MAC80211_RC_SIMPLE_MODULE +module_init(rc80211_simple_init); +module_exit(rc80211_simple_exit); +#endif diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a7263fc476b..89e1e3070ec 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -24,6 +24,10 @@ #include "tkip.h" #include "wme.h" +u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, + struct tid_ampdu_rx *tid_agg_rx, + struct sk_buff *skb, u16 mpdu_seq_num, + int bar_req); /* * monitor mode reception * @@ -61,8 +65,12 @@ static inline int should_drop_frame(struct ieee80211_rx_status *status, return 1; if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len)) return 1; - if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == - cpu_to_le16(IEEE80211_FTYPE_CTL)) + if (((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL)) && + ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) != + cpu_to_le16(IEEE80211_STYPE_PSPOLL)) && + ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) != + cpu_to_le16(IEEE80211_STYPE_BACK_REQ))) return 1; return 0; } @@ -79,8 +87,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, struct ieee80211_sub_if_data *sdata; struct ieee80211_rate *rate; int needed_headroom = 0; - struct ieee80211_rtap_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header *rthdr; + __le64 *rttsft = NULL; + struct ieee80211_rtap_fixed_data { u8 flags; u8 rate; __le16 chan_freq; @@ -88,7 +97,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, u8 antsignal; u8 padding_for_rxflags; __le16 rx_flags; - } __attribute__ ((packed)) *rthdr; + } __attribute__ ((packed)) *rtfixed; struct sk_buff *skb, *skb2; struct net_device *prev_dev = NULL; int present_fcs_len = 0; @@ -105,7 +114,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (status->flag & RX_FLAG_RADIOTAP) rtap_len = ieee80211_get_radiotap_len(origskb->data); else - needed_headroom = sizeof(*rthdr); + /* room for radiotap header, always present fields and TSFT */ + needed_headroom = sizeof(*rthdr) + sizeof(*rtfixed) + 8; if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) present_fcs_len = FCS_LEN; @@ -133,7 +143,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * them allocate enough headroom to start with. */ if (skb_headroom(skb) < needed_headroom && - pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) { + pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) { dev_kfree_skb(skb); return NULL; } @@ -152,45 +162,59 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, /* if necessary, prepend radiotap information */ if (!(status->flag & RX_FLAG_RADIOTAP)) { + rtfixed = (void *) skb_push(skb, sizeof(*rtfixed)); + rtap_len = sizeof(*rthdr) + sizeof(*rtfixed); + if (status->flag & RX_FLAG_TSFT) { + rttsft = (void *) skb_push(skb, sizeof(*rttsft)); + rtap_len += 8; + } rthdr = (void *) skb_push(skb, sizeof(*rthdr)); memset(rthdr, 0, sizeof(*rthdr)); - rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); - rthdr->hdr.it_present = + memset(rtfixed, 0, sizeof(*rtfixed)); + rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | (1 << IEEE80211_RADIOTAP_RX_FLAGS)); - rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ? - IEEE80211_RADIOTAP_F_FCS : 0; + rtfixed->flags = 0; + if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) + rtfixed->flags |= IEEE80211_RADIOTAP_F_FCS; + + if (rttsft) { + *rttsft = cpu_to_le64(status->mactime); + rthdr->it_present |= + cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); + } /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */ - rthdr->rx_flags = 0; + rtfixed->rx_flags = 0; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) - rthdr->rx_flags |= + rtfixed->rx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS); rate = ieee80211_get_rate(local, status->phymode, status->rate); if (rate) - rthdr->rate = rate->rate / 5; + rtfixed->rate = rate->rate / 5; - rthdr->chan_freq = cpu_to_le16(status->freq); + rtfixed->chan_freq = cpu_to_le16(status->freq); if (status->phymode == MODE_IEEE80211A) - rthdr->chan_flags = + rtfixed->chan_flags = cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ); else - rthdr->chan_flags = + rtfixed->chan_flags = cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ); - rthdr->antsignal = status->ssi; + rtfixed->antsignal = status->ssi; + rthdr->it_len = cpu_to_le16(rtap_len); } - skb_set_mac_header(skb, 0); + skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); @@ -199,7 +223,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (!netif_running(sdata->dev)) continue; - if (sdata->type != IEEE80211_IF_TYPE_MNTR) + if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR) continue; if (prev_dev) { @@ -243,6 +267,10 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; /* frame has qos control */ tid = qc[0] & QOS_CONTROL_TID_MASK; + if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + rx->flags |= IEEE80211_TXRXD_RX_AMSDU; + else + rx->flags &= ~IEEE80211_TXRXD_RX_AMSDU; } else { if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { /* Separate TID for management frames */ @@ -266,11 +294,11 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) return TXRX_CONTINUE; } -static ieee80211_txrx_result -ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) + +static u32 ieee80211_rx_load_stats(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_rx_status *status) { - struct ieee80211_local *local = rx->local; - struct sk_buff *skb = rx->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u32 load = 0, hdrtime; struct ieee80211_rate *rate; @@ -284,7 +312,7 @@ ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) rate = &mode->rates[0]; for (i = 0; i < mode->num_rates; i++) { - if (mode->rates[i].val == rx->u.rx.status->rate) { + if (mode->rates[i].val == status->rate) { rate = &mode->rates[i]; break; } @@ -308,16 +336,13 @@ ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) /* Divide channel_use by 8 to avoid wrapping around the counter */ load >>= CHAN_UTIL_SHIFT; - local->channel_use_raw += load; - rx->u.rx.load = load; - return TXRX_CONTINUE; + return load; } ieee80211_rx_handler ieee80211_rx_pre_handlers[] = { ieee80211_rx_h_parse_qos, - ieee80211_rx_h_load_stats, NULL }; @@ -338,8 +363,14 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx) struct ieee80211_local *local = rx->local; struct sk_buff *skb = rx->skb; - if (unlikely(local->sta_scanning != 0)) { - ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status); + if (unlikely(local->sta_hw_scanning)) + return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status); + + if (unlikely(local->sta_sw_scanning)) { + /* drop all the other packets during a software scan anyway */ + if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status) + != TXRX_QUEUED) + dev_kfree_skb(skb); return TXRX_QUEUED; } @@ -377,18 +408,6 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) return TXRX_DROP; } - if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) - rx->skb->pkt_type = PACKET_OTHERHOST; - else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0) - rx->skb->pkt_type = PACKET_HOST; - else if (is_multicast_ether_addr(hdr->addr1)) { - if (is_broadcast_ether_addr(hdr->addr1)) - rx->skb->pkt_type = PACKET_BROADCAST; - else - rx->skb->pkt_type = PACKET_MULTICAST; - } else - rx->skb->pkt_type = PACKET_OTHERHOST; - /* Drop disallowed frame classes based on STA auth/assoc state; * IEEE 802.11, Chap 5.5. * @@ -400,7 +419,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA || ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL && (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) && - rx->sdata->type != IEEE80211_IF_TYPE_IBSS && + rx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS && (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) { if ((!(rx->fc & IEEE80211_FCTL_FROMDS) && !(rx->fc & IEEE80211_FCTL_TODS) && @@ -620,13 +639,14 @@ ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx) /* Update last_rx only for IBSS packets which are for the current * BSSID to avoid keeping the current IBSS network alive in cases where * other STAs are using different BSSID. */ - if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) { - u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len); + if (rx->sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { + u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, + IEEE80211_IF_TYPE_IBSS); if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0) sta->last_rx = jiffies; } else if (!is_multicast_ether_addr(hdr->addr1) || - rx->sdata->type == IEEE80211_IF_TYPE_STA) { + rx->sdata->vif.type == IEEE80211_IF_TYPE_STA) { /* Update last_rx only for unicast frames in order to prevent * the Probe Request frames (the only broadcast frames from a * STA in infrastructure mode) from keeping a connection alive. @@ -870,6 +890,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx) static ieee80211_txrx_result ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx) { + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); struct sk_buff *skb; int no_pending_pkts; DECLARE_MAC_BUF(mac); @@ -880,6 +901,10 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx) !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))) return TXRX_CONTINUE; + if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) && + (sdata->vif.type != IEEE80211_IF_TYPE_VLAN)) + return TXRX_DROP; + skb = skb_dequeue(&rx->sta->tx_filtered); if (!skb) { skb = skb_dequeue(&rx->sta->ps_tx_buf); @@ -956,68 +981,54 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx) return TXRX_CONTINUE; } -static ieee80211_txrx_result -ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx) +static int +ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx) { - if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) && - rx->sdata->type != IEEE80211_IF_TYPE_STA && - (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) - return TXRX_CONTINUE; - - if (unlikely(rx->sdata->ieee802_1x && - (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && - (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC && - (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) && - !ieee80211_is_eapol(rx->skb))) { + if (unlikely(rx->sdata->ieee802_1x_pac && + (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)))) { #ifdef CONFIG_MAC80211_DEBUG - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) rx->skb->data; - DECLARE_MAC_BUF(mac); - printk(KERN_DEBUG "%s: dropped frame from %s" - " (unauthorized port)\n", rx->dev->name, - print_mac(mac, hdr->addr2)); + printk(KERN_DEBUG "%s: dropped frame " + "(unauthorized port)\n", rx->dev->name); #endif /* CONFIG_MAC80211_DEBUG */ - return TXRX_DROP; + return -EACCES; } - return TXRX_CONTINUE; + return 0; } -static ieee80211_txrx_result -ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx) +static int +ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx) { /* * Pass through unencrypted frames if the hardware has * decrypted them already. */ if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED) - return TXRX_CONTINUE; + return 0; /* Drop unencrypted frames if key is set. */ if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) && (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC && - (rx->key || rx->sdata->drop_unencrypted) && - (rx->sdata->eapol == 0 || !ieee80211_is_eapol(rx->skb)))) { + (rx->key || rx->sdata->drop_unencrypted))) { if (net_ratelimit()) printk(KERN_DEBUG "%s: RX non-WEP frame, but expected " "encryption\n", rx->dev->name); - return TXRX_DROP; + return -EACCES; } - return TXRX_CONTINUE; + return 0; } -static ieee80211_txrx_result -ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) +static int +ieee80211_data_to_8023(struct ieee80211_txrx_data *rx) { struct net_device *dev = rx->dev; - struct ieee80211_local *local = rx->local; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; u16 fc, hdrlen, ethertype; u8 *payload; u8 dst[ETH_ALEN]; u8 src[ETH_ALEN]; - struct sk_buff *skb = rx->skb, *skb2; + struct sk_buff *skb = rx->skb; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac2); @@ -1025,11 +1036,9 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) DECLARE_MAC_BUF(mac4); fc = rx->fc; - if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) - return TXRX_CONTINUE; if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) - return TXRX_DROP; + return -1; hdrlen = ieee80211_get_hdrlen(fc); @@ -1049,8 +1058,8 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); - if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP && - sdata->type != IEEE80211_IF_TYPE_VLAN)) { + if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_AP && + sdata->vif.type != IEEE80211_IF_TYPE_VLAN)) { if (net_ratelimit()) printk(KERN_DEBUG "%s: dropped ToDS frame " "(BSSID=%s SA=%s DA=%s)\n", @@ -1058,7 +1067,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) print_mac(mac, hdr->addr1), print_mac(mac2, hdr->addr2), print_mac(mac3, hdr->addr3)); - return TXRX_DROP; + return -1; } break; case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): @@ -1066,7 +1075,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr4, ETH_ALEN); - if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) { + if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS)) { if (net_ratelimit()) printk(KERN_DEBUG "%s: dropped FromDS&ToDS " "frame (RA=%s TA=%s DA=%s SA=%s)\n", @@ -1075,7 +1084,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) print_mac(mac2, hdr->addr2), print_mac(mac3, hdr->addr3), print_mac(mac4, hdr->addr4)); - return TXRX_DROP; + return -1; } break; case IEEE80211_FCTL_FROMDS: @@ -1083,17 +1092,17 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr3, ETH_ALEN); - if (sdata->type != IEEE80211_IF_TYPE_STA || + if (sdata->vif.type != IEEE80211_IF_TYPE_STA || (is_multicast_ether_addr(dst) && !compare_ether_addr(src, dev->dev_addr))) - return TXRX_DROP; + return -1; break; case 0: /* DA SA BSSID */ memcpy(dst, hdr->addr1, ETH_ALEN); memcpy(src, hdr->addr2, ETH_ALEN); - if (sdata->type != IEEE80211_IF_TYPE_IBSS) { + if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: dropped IBSS frame " "(DA=%s SA=%s BSSID=%s)\n", @@ -1102,21 +1111,20 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) print_mac(mac2, hdr->addr2), print_mac(mac3, hdr->addr3)); } - return TXRX_DROP; + return -1; } break; } - payload = skb->data + hdrlen; - if (unlikely(skb->len - hdrlen < 8)) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: RX too short data frame " "payload\n", dev->name); } - return TXRX_DROP; + return -1; } + payload = skb->data + hdrlen; ethertype = (payload[6] << 8) | payload[7]; if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && @@ -1130,6 +1138,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) } else { struct ethhdr *ehdr; __be16 len; + skb_pull(skb, hdrlen); len = htons(skb->len); ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); @@ -1137,36 +1146,72 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) memcpy(ehdr->h_source, src, ETH_ALEN); ehdr->h_proto = len; } - skb->dev = dev; + return 0; +} + +/* + * requires that rx->skb is a frame with ethernet header + */ +static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx) +{ + static const u8 pae_group_addr[ETH_ALEN] + = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 }; + struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; - skb2 = NULL; + /* + * Allow EAPOL frames to us/the PAE group address regardless + * of whether the frame was encrypted or not. + */ + if (ehdr->h_proto == htons(ETH_P_PAE) && + (compare_ether_addr(ehdr->h_dest, rx->dev->dev_addr) == 0 || + compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0)) + return true; - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; + if (ieee80211_802_1x_port_control(rx) || + ieee80211_drop_unencrypted(rx)) + return false; + + return true; +} - if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP - || sdata->type == IEEE80211_IF_TYPE_VLAN) && +/* + * requires that rx->skb is a frame with ethernet header + */ +static void +ieee80211_deliver_skb(struct ieee80211_txrx_data *rx) +{ + struct net_device *dev = rx->dev; + struct ieee80211_local *local = rx->local; + struct sk_buff *skb, *xmit_skb; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; + struct sta_info *dsta; + + skb = rx->skb; + xmit_skb = NULL; + + if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP || + sdata->vif.type == IEEE80211_IF_TYPE_VLAN) && (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) { - if (is_multicast_ether_addr(skb->data)) { - /* send multicast frames both to higher layers in - * local net stack and back to the wireless media */ - skb2 = skb_copy(skb, GFP_ATOMIC); - if (!skb2 && net_ratelimit()) + if (is_multicast_ether_addr(ehdr->h_dest)) { + /* + * send multicast frames both to higher layers in + * local net stack and back to the wireless medium + */ + xmit_skb = skb_copy(skb, GFP_ATOMIC); + if (!xmit_skb && net_ratelimit()) printk(KERN_DEBUG "%s: failed to clone " "multicast frame\n", dev->name); } else { - struct sta_info *dsta; dsta = sta_info_get(local, skb->data); - if (dsta && !dsta->dev) { - if (net_ratelimit()) - printk(KERN_DEBUG "Station with null " - "dev structure!\n"); - } else if (dsta && dsta->dev == dev) { - /* Destination station is associated to this - * AP, so send the frame directly to it and - * do not pass the frame to local net stack. + if (dsta && dsta->dev == dev) { + /* + * The destination station is associated to + * this AP (in this VLAN), so send the frame + * directly to it and do not pass it to local + * net stack. */ - skb2 = skb; + xmit_skb = skb; skb = NULL; } if (dsta) @@ -1181,18 +1226,207 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) netif_rx(skb); } - if (skb2) { + if (xmit_skb) { /* send to wireless media */ - skb2->protocol = __constant_htons(ETH_P_802_3); - skb_set_network_header(skb2, 0); - skb_set_mac_header(skb2, 0); - dev_queue_xmit(skb2); + xmit_skb->protocol = htons(ETH_P_802_3); + skb_reset_network_header(xmit_skb); + skb_reset_mac_header(xmit_skb); + dev_queue_xmit(xmit_skb); } +} + +static ieee80211_txrx_result +ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx) +{ + struct net_device *dev = rx->dev; + struct ieee80211_local *local = rx->local; + u16 fc, ethertype; + u8 *payload; + struct sk_buff *skb = rx->skb, *frame = NULL; + const struct ethhdr *eth; + int remaining, err; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + DECLARE_MAC_BUF(mac); + + fc = rx->fc; + if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) + return TXRX_CONTINUE; + + if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) + return TXRX_DROP; + + if (!(rx->flags & IEEE80211_TXRXD_RX_AMSDU)) + return TXRX_CONTINUE; + + err = ieee80211_data_to_8023(rx); + if (unlikely(err)) + return TXRX_DROP; + + skb->dev = dev; + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* skip the wrapping header */ + eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); + if (!eth) + return TXRX_DROP; + + while (skb != frame) { + u8 padding; + __be16 len = eth->h_proto; + unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); + + remaining = skb->len; + memcpy(dst, eth->h_dest, ETH_ALEN); + memcpy(src, eth->h_source, ETH_ALEN); + + padding = ((4 - subframe_len) & 0x3); + /* the last MSDU has no padding */ + if (subframe_len > remaining) { + printk(KERN_DEBUG "%s: wrong buffer size", dev->name); + return TXRX_DROP; + } + + skb_pull(skb, sizeof(struct ethhdr)); + /* if last subframe reuse skb */ + if (remaining <= subframe_len + padding) + frame = skb; + else { + frame = dev_alloc_skb(local->hw.extra_tx_headroom + + subframe_len); + + if (frame == NULL) + return TXRX_DROP; + + skb_reserve(frame, local->hw.extra_tx_headroom + + sizeof(struct ethhdr)); + memcpy(skb_put(frame, ntohs(len)), skb->data, + ntohs(len)); + + eth = (struct ethhdr *) skb_pull(skb, ntohs(len) + + padding); + if (!eth) { + printk(KERN_DEBUG "%s: wrong buffer size ", + dev->name); + dev_kfree_skb(frame); + return TXRX_DROP; + } + } + + skb_reset_network_header(frame); + frame->dev = dev; + frame->priority = skb->priority; + rx->skb = frame; + + payload = frame->data; + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, + bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel + * encapsulation and replace EtherType */ + skb_pull(frame, 6); + memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); + } else { + memcpy(skb_push(frame, sizeof(__be16)), + &len, sizeof(__be16)); + memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); + } + + if (!ieee80211_frame_allowed(rx)) { + if (skb == frame) /* last frame */ + return TXRX_DROP; + dev_kfree_skb(frame); + continue; + } + + ieee80211_deliver_skb(rx); + } + + return TXRX_QUEUED; +} + +static ieee80211_txrx_result +ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) +{ + struct net_device *dev = rx->dev; + u16 fc; + int err; + + fc = rx->fc; + if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) + return TXRX_CONTINUE; + + if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) + return TXRX_DROP; + + err = ieee80211_data_to_8023(rx); + if (unlikely(err)) + return TXRX_DROP; + + if (!ieee80211_frame_allowed(rx)) + return TXRX_DROP; + + rx->skb->dev = dev; + + dev->stats.rx_packets++; + dev->stats.rx_bytes += rx->skb->len; + + ieee80211_deliver_skb(rx); return TXRX_QUEUED; } static ieee80211_txrx_result +ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_local *local = rx->local; + struct ieee80211_hw *hw = &local->hw; + struct sk_buff *skb = rx->skb; + struct ieee80211_bar *bar = (struct ieee80211_bar *) skb->data; + struct tid_ampdu_rx *tid_agg_rx; + u16 start_seq_num; + u16 tid; + + if (likely((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL)) + return TXRX_CONTINUE; + + if ((rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ) { + if (!rx->sta) + return TXRX_CONTINUE; + tid = le16_to_cpu(bar->control) >> 12; + tid_agg_rx = &(rx->sta->ampdu_mlme.tid_rx[tid]); + if (tid_agg_rx->state != HT_AGG_STATE_OPERATIONAL) + return TXRX_CONTINUE; + + start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4; + + /* reset session timer */ + if (tid_agg_rx->timeout) { + unsigned long expires = + jiffies + (tid_agg_rx->timeout / 1000) * HZ; + mod_timer(&tid_agg_rx->session_timer, expires); + } + + /* manage reordering buffer according to requested */ + /* sequence number */ + rcu_read_lock(); + ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, + start_seq_num, 1); + rcu_read_unlock(); + return TXRX_DROP; + } + + return TXRX_CONTINUE; +} + +static ieee80211_txrx_result ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx) { struct ieee80211_sub_if_data *sdata; @@ -1201,8 +1435,8 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx) return TXRX_DROP; sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); - if ((sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) && + if ((sdata->vif.type == IEEE80211_IF_TYPE_STA || + sdata->vif.type == IEEE80211_IF_TYPE_IBSS) && !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)) ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status); else @@ -1294,7 +1528,7 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev, goto ignore; } - if (rx->sdata->type == IEEE80211_IF_TYPE_AP && keyidx) { + if (rx->sdata->vif.type == IEEE80211_IF_TYPE_AP && keyidx) { /* * APs with pairwise keys should never receive Michael MIC * errors for non-zero keyidx because these are reserved for @@ -1341,9 +1575,9 @@ ieee80211_rx_handler ieee80211_rx_handlers[] = * are not passed to user space by these functions */ ieee80211_rx_h_remove_qos_control, - ieee80211_rx_h_802_1x_pae, - ieee80211_rx_h_drop_unencrypted, + ieee80211_rx_h_amsdu, ieee80211_rx_h_data, + ieee80211_rx_h_ctrl, ieee80211_rx_h_mgmt, NULL }; @@ -1356,7 +1590,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, { int multicast = is_multicast_ether_addr(hdr->addr1); - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_STA: if (!bssid) return 0; @@ -1427,11 +1661,13 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, } /* - * This is the receive path handler. It is called by a low level driver when an - * 802.11 MPDU is received from the hardware. + * This is the actual Rx frames handler. as it blongs to Rx path it must + * be called with rcu_read_lock protection. */ -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status) +static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_rx_status *status, + u32 load) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; @@ -1439,37 +1675,19 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_hdr *hdr; struct ieee80211_txrx_data rx; u16 type; - int prepres; + int prepares; struct ieee80211_sub_if_data *prev = NULL; struct sk_buff *skb_new; u8 *bssid; int hdrlen; - /* - * key references and virtual interfaces are protected using RCU - * and this requires that we are in a read-side RCU section during - * receive processing - */ - rcu_read_lock(); - - /* - * Frames with failed FCS/PLCP checksum are not returned, - * all other frames are returned without radiotap header - * if it was previously present. - * Also, frames with less than 16 bytes are dropped. - */ - skb = ieee80211_rx_monitor(local, skb, status); - if (!skb) { - rcu_read_unlock(); - return; - } - hdr = (struct ieee80211_hdr *) skb->data; memset(&rx, 0, sizeof(rx)); rx.skb = skb; rx.local = local; rx.u.rx.status = status; + rx.u.rx.load = load; rx.fc = le16_to_cpu(hdr->frame_control); type = rx.fc & IEEE80211_FCTL_FTYPE; @@ -1499,7 +1717,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, goto end; } - if (unlikely(local->sta_scanning)) + if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning)) rx.flags |= IEEE80211_TXRXD_RXIN_SCAN; if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx, @@ -1514,25 +1732,23 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, rx.sta); sta_info_put(sta); - rcu_read_unlock(); return; } - bssid = ieee80211_get_bssid(hdr, skb->len); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { if (!netif_running(sdata->dev)) continue; - if (sdata->type == IEEE80211_IF_TYPE_MNTR) + if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR) continue; + bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); rx.flags |= IEEE80211_TXRXD_RXRA_MATCH; - prepres = prepare_for_handlers(sdata, bssid, &rx, hdr); + prepares = prepare_for_handlers(sdata, bssid, &rx, hdr); /* prepare_for_handlers can change sta */ sta = rx.sta; - if (!prepres) + if (!prepares) continue; /* @@ -1560,6 +1776,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, prev->dev->name); continue; } + rx.fc = le16_to_cpu(hdr->frame_control); rx.skb = skb_new; rx.dev = prev->dev; rx.sdata = prev; @@ -1568,6 +1785,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, prev = sdata; } if (prev) { + rx.fc = le16_to_cpu(hdr->frame_control); rx.skb = skb; rx.dev = prev->dev; rx.sdata = prev; @@ -1577,10 +1795,230 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, dev_kfree_skb(skb); end: - rcu_read_unlock(); + if (sta) + sta_info_put(sta); +} + +#define SEQ_MODULO 0x1000 +#define SEQ_MASK 0xfff + +static inline int seq_less(u16 sq1, u16 sq2) +{ + return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1)); +} + +static inline u16 seq_inc(u16 sq) +{ + return ((sq + 1) & SEQ_MASK); +} + +static inline u16 seq_sub(u16 sq1, u16 sq2) +{ + return ((sq1 - sq2) & SEQ_MASK); +} + + +/* + * As it function blongs to Rx path it must be called with + * the proper rcu_read_lock protection for its flow. + */ +u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, + struct tid_ampdu_rx *tid_agg_rx, + struct sk_buff *skb, u16 mpdu_seq_num, + int bar_req) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_rx_status status; + u16 head_seq_num, buf_size; + int index; + u32 pkt_load; + + buf_size = tid_agg_rx->buf_size; + head_seq_num = tid_agg_rx->head_seq_num; + + /* frame with out of date sequence number */ + if (seq_less(mpdu_seq_num, head_seq_num)) { + dev_kfree_skb(skb); + return 1; + } + + /* if frame sequence number exceeds our buffering window size or + * block Ack Request arrived - release stored frames */ + if ((!seq_less(mpdu_seq_num, head_seq_num + buf_size)) || (bar_req)) { + /* new head to the ordering buffer */ + if (bar_req) + head_seq_num = mpdu_seq_num; + else + head_seq_num = + seq_inc(seq_sub(mpdu_seq_num, buf_size)); + /* release stored frames up to new head to stack */ + while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { + index = seq_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) + % tid_agg_rx->buf_size; + + if (tid_agg_rx->reorder_buf[index]) { + /* release the reordered frames to stack */ + memcpy(&status, + tid_agg_rx->reorder_buf[index]->cb, + sizeof(status)); + pkt_load = ieee80211_rx_load_stats(local, + tid_agg_rx->reorder_buf[index], + &status); + __ieee80211_rx_handle_packet(hw, + tid_agg_rx->reorder_buf[index], + &status, pkt_load); + tid_agg_rx->stored_mpdu_num--; + tid_agg_rx->reorder_buf[index] = NULL; + } + tid_agg_rx->head_seq_num = + seq_inc(tid_agg_rx->head_seq_num); + } + if (bar_req) + return 1; + } + + /* now the new frame is always in the range of the reordering */ + /* buffer window */ + index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) + % tid_agg_rx->buf_size; + /* check if we already stored this frame */ + if (tid_agg_rx->reorder_buf[index]) { + dev_kfree_skb(skb); + return 1; + } + + /* if arrived mpdu is in the right order and nothing else stored */ + /* release it immediately */ + if (mpdu_seq_num == tid_agg_rx->head_seq_num && + tid_agg_rx->stored_mpdu_num == 0) { + tid_agg_rx->head_seq_num = + seq_inc(tid_agg_rx->head_seq_num); + return 0; + } + + /* put the frame in the reordering buffer */ + tid_agg_rx->reorder_buf[index] = skb; + tid_agg_rx->stored_mpdu_num++; + /* release the buffer until next missing frame */ + index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) + % tid_agg_rx->buf_size; + while (tid_agg_rx->reorder_buf[index]) { + /* release the reordered frame back to stack */ + memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, + sizeof(status)); + pkt_load = ieee80211_rx_load_stats(local, + tid_agg_rx->reorder_buf[index], + &status); + __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], + &status, pkt_load); + tid_agg_rx->stored_mpdu_num--; + tid_agg_rx->reorder_buf[index] = NULL; + tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); + index = seq_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) % tid_agg_rx->buf_size; + } + return 1; +} + +static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, + struct sk_buff *skb) +{ + struct ieee80211_hw *hw = &local->hw; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct sta_info *sta; + struct tid_ampdu_rx *tid_agg_rx; + u16 fc, sc; + u16 mpdu_seq_num; + u8 ret = 0, *qc; + int tid; + + sta = sta_info_get(local, hdr->addr2); + if (!sta) + return ret; + + fc = le16_to_cpu(hdr->frame_control); + + /* filter the QoS data rx stream according to + * STA/TID and check if this STA/TID is on aggregation */ + if (!WLAN_FC_IS_QOS_DATA(fc)) + goto end_reorder; + + qc = skb->data + ieee80211_get_hdrlen(fc) - QOS_CONTROL_LEN; + tid = qc[0] & QOS_CONTROL_TID_MASK; + tid_agg_rx = &(sta->ampdu_mlme.tid_rx[tid]); + + if (tid_agg_rx->state != HT_AGG_STATE_OPERATIONAL) + goto end_reorder; + + /* null data frames are excluded */ + if (unlikely(fc & IEEE80211_STYPE_QOS_NULLFUNC)) + goto end_reorder; + + /* new un-ordered ampdu frame - process it */ + + /* reset session timer */ + if (tid_agg_rx->timeout) { + unsigned long expires = + jiffies + (tid_agg_rx->timeout / 1000) * HZ; + mod_timer(&tid_agg_rx->session_timer, expires); + } + + /* if this mpdu is fragmented - terminate rx aggregation session */ + sc = le16_to_cpu(hdr->seq_ctrl); + if (sc & IEEE80211_SCTL_FRAG) { + ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, + tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP); + ret = 1; + goto end_reorder; + } + /* according to mpdu sequence number deal with reordering buffer */ + mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; + ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, + mpdu_seq_num, 0); +end_reorder: if (sta) sta_info_put(sta); + return ret; +} + +/* + * This is the receive path handler. It is called by a low level driver when an + * 802.11 MPDU is received from the hardware. + */ +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_rx_status *status) +{ + struct ieee80211_local *local = hw_to_local(hw); + u32 pkt_load; + + /* + * key references and virtual interfaces are protected using RCU + * and this requires that we are in a read-side RCU section during + * receive processing + */ + rcu_read_lock(); + + /* + * Frames with failed FCS/PLCP checksum are not returned, + * all other frames are returned without radiotap header + * if it was previously present. + * Also, frames with less than 16 bytes are dropped. + */ + skb = ieee80211_rx_monitor(local, skb, status); + if (!skb) { + rcu_read_unlock(); + return; + } + + pkt_load = ieee80211_rx_load_stats(local, skb, status); + local->channel_use_raw += pkt_load; + + if (!ieee80211_rx_reorder_ampdu(local, skb)) + __ieee80211_rx_handle_packet(hw, skb, status, pkt_load); + + rcu_read_unlock(); } EXPORT_SYMBOL(__ieee80211_rx); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index cfd8ee9adad..1f74bd29635 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -104,6 +104,7 @@ static void sta_info_release(struct kref *kref) struct sta_info *sta = container_of(kref, struct sta_info, kref); struct ieee80211_local *local = sta->local; struct sk_buff *skb; + int i; /* free sta structure; it has already been removed from * hash table etc. external structures. Make sure that all @@ -116,6 +117,8 @@ static void sta_info_release(struct kref *kref) while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { dev_kfree_skb_any(skb); } + for (i = 0; i < STA_TID_NUM; i++) + del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer); rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); rate_control_put(sta->rate_ctrl); kfree(sta); @@ -133,6 +136,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, struct net_device *dev, u8 *addr, gfp_t gfp) { struct sta_info *sta; + int i; DECLARE_MAC_BUF(mac); sta = kzalloc(sizeof(*sta), gfp); @@ -152,6 +156,19 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, memcpy(sta->addr, addr, ETH_ALEN); sta->local = local; sta->dev = dev; + spin_lock_init(&sta->ampdu_mlme.ampdu_rx); + for (i = 0; i < STA_TID_NUM; i++) { + /* timer_to_tid must be initialized with identity mapping to + * enable session_timer's data differentiation. refer to + * sta_rx_agg_session_timer_expired for useage */ + sta->timer_to_tid[i] = i; + /* rx timers */ + sta->ampdu_mlme.tid_rx[i].session_timer.function = + sta_rx_agg_session_timer_expired; + sta->ampdu_mlme.tid_rx[i].session_timer.data = + (unsigned long)&sta->timer_to_tid[i]; + init_timer(&sta->ampdu_mlme.tid_rx[i].session_timer); + } skb_queue_head_init(&sta->ps_tx_buf); skb_queue_head_init(&sta->tx_filtered); __sta_info_get(sta); /* sta used by caller, decremented by @@ -160,9 +177,16 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, list_add(&sta->list, &local->sta_list); local->num_sta++; sta_info_hash_add(local, sta); - if (local->ops->sta_notify) - local->ops->sta_notify(local_to_hw(local), dev->ifindex, - STA_NOTIFY_ADD, addr); + if (local->ops->sta_notify) { + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) + sdata = sdata->u.vlan.ap; + + local->ops->sta_notify(local_to_hw(local), &sdata->vif, + STA_NOTIFY_ADD, addr); + } write_unlock_bh(&local->sta_lock); #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -230,9 +254,17 @@ void sta_info_free(struct sta_info *sta) ieee80211_key_free(sta->key); sta->key = NULL; - if (local->ops->sta_notify) - local->ops->sta_notify(local_to_hw(local), sta->dev->ifindex, - STA_NOTIFY_REMOVE, sta->addr); + if (local->ops->sta_notify) { + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + + if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) + sdata = sdata->u.vlan.ap; + + local->ops->sta_notify(local_to_hw(local), &sdata->vif, + STA_NOTIFY_REMOVE, sta->addr); + } rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); @@ -346,11 +378,10 @@ void sta_info_init(struct ieee80211_local *local) rwlock_init(&local->sta_lock); INIT_LIST_HEAD(&local->sta_list); - init_timer(&local->sta_cleanup); + setup_timer(&local->sta_cleanup, sta_info_cleanup, + (unsigned long)local); local->sta_cleanup.expires = round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); - local->sta_cleanup.data = (unsigned long) local; - local->sta_cleanup.function = sta_info_cleanup; #ifdef CONFIG_MAC80211_DEBUGFS INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8f7ebe41c02..96fe3ed9503 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -31,6 +31,51 @@ #define WLAN_STA_WME BIT(9) #define WLAN_STA_WDS BIT(27) +#define STA_TID_NUM 16 +#define ADDBA_RESP_INTERVAL HZ + +#define HT_AGG_STATE_INITIATOR_SHIFT (4) + +#define HT_AGG_STATE_REQ_STOP_BA_MSK BIT(3) + +#define HT_AGG_STATE_IDLE (0x0) +#define HT_AGG_STATE_OPERATIONAL (0x7) + +/** + * struct tid_ampdu_rx - TID aggregation information (Rx). + * + * @state: TID's state in session state machine. + * @dialog_token: dialog token for aggregation session + * @ssn: Starting Sequence Number expected to be aggregated. + * @buf_size: buffer size for incoming A-MPDUs + * @timeout: reset timer value. + * @head_seq_num: head sequence number in reordering buffer. + * @stored_mpdu_num: number of MPDUs in reordering buffer + * @reorder_buf: buffer to reorder incoming aggregated MPDUs + * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) + */ +struct tid_ampdu_rx { + u8 state; + u8 dialog_token; + u16 ssn; + u16 buf_size; + u16 timeout; + u16 head_seq_num; + u16 stored_mpdu_num; + struct sk_buff **reorder_buf; + struct timer_list session_timer; +}; + +/** + * struct sta_ampdu_mlme - STA aggregation information. + * + * @tid_agg_info_rx: aggregation info for Rx per TID + * @ampdu_rx: for locking sections in aggregation Rx flow + */ +struct sta_ampdu_mlme { + struct tid_ampdu_rx tid_rx[STA_TID_NUM]; + spinlock_t ampdu_rx; +}; struct sta_info { struct kref kref; @@ -99,6 +144,11 @@ struct sta_info { u16 listen_interval; + struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities + of this STA */ + struct sta_ampdu_mlme ampdu_mlme; + u8 timer_to_tid[STA_TID_NUM]; /* convert timer id to tid */ + #ifdef CONFIG_MAC80211_DEBUGFS struct sta_info_debugfsdentries { struct dentry *dir; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1a531543bcc..67b509edd43 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -176,7 +176,7 @@ static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr, * to closest integer */ dur = ieee80211_frame_duration(local, 10, rate, erp, - tx->sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE); + tx->sdata->bss_conf.use_short_preamble); if (next_frag_len) { /* Frame is fragmented: duration increases with time needed to @@ -185,8 +185,7 @@ static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr, /* next fragment */ dur += ieee80211_frame_duration(local, next_frag_len, txrate->rate, erp, - tx->sdata->flags & - IEEE80211_SDATA_SHORT_PREAMBLE); + tx->sdata->bss_conf.use_short_preamble); } return dur; @@ -225,7 +224,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED)) return TXRX_CONTINUE; - if (unlikely(tx->local->sta_scanning != 0) && + if (unlikely(tx->local->sta_sw_scanning) && ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ)) return TXRX_DROP; @@ -237,7 +236,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) if (likely(tx->flags & IEEE80211_TXRXD_TXUNICAST)) { if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && - tx->sdata->type != IEEE80211_IF_TYPE_IBSS && + tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS && (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG DECLARE_MAC_BUF(mac); @@ -251,7 +250,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) } else { if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && tx->local->num_sta == 0 && - tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) { + tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS)) { /* * No associated STAs - no need to send multicast * frames. @@ -261,18 +260,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) return TXRX_CONTINUE; } - if (unlikely(/* !injected && */ tx->sdata->ieee802_1x && - !(sta_flags & WLAN_STA_AUTHORIZED))) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - DECLARE_MAC_BUF(mac); - printk(KERN_DEBUG "%s: dropped frame to %s" - " (unauthorized port)\n", tx->dev->name, - print_mac(mac, hdr->addr1)); -#endif - I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port); - return TXRX_DROP; - } - return TXRX_CONTINUE; } @@ -306,7 +293,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) list_for_each_entry_rcu(sdata, &local->interfaces, list) { struct ieee80211_if_ap *ap; if (sdata->dev == local->mdev || - sdata->type != IEEE80211_IF_TYPE_AP) + sdata->vif.type != IEEE80211_IF_TYPE_AP) continue; ap = &sdata->u.ap; skb = skb_dequeue(&ap->ps_bc_buf); @@ -334,16 +321,27 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) wiphy_name(local->hw.wiphy), purged); } -static inline ieee80211_txrx_result +static ieee80211_txrx_result ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) { - /* broadcast/multicast frame */ - /* If any of the associated stations is in power save mode, - * the frame is buffered to be sent after DTIM beacon frame */ - if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) && - tx->sdata->type != IEEE80211_IF_TYPE_WDS && - tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) && - !(tx->fc & IEEE80211_FCTL_ORDER)) { + /* + * broadcast/multicast frame + * + * If any of the associated stations is in power save mode, + * the frame is buffered to be sent after DTIM beacon frame. + * This is done either by the hardware or us. + */ + + /* not AP/IBSS or ordered frame */ + if (!tx->sdata->bss || (tx->fc & IEEE80211_FCTL_ORDER)) + return TXRX_CONTINUE; + + /* no stations in PS mode */ + if (!atomic_read(&tx->sdata->bss->num_sta_ps)) + return TXRX_CONTINUE; + + /* buffered in mac80211 */ + if (tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) { if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= @@ -360,10 +358,13 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) return TXRX_QUEUED; } + /* buffered in hardware */ + tx->u.tx.control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM; + return TXRX_CONTINUE; } -static inline ieee80211_txrx_result +static ieee80211_txrx_result ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx) { struct sta_info *sta = tx->sta; @@ -420,7 +421,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx) return TXRX_CONTINUE; } - static ieee80211_txrx_result ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx) { @@ -433,13 +433,11 @@ ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx) return ieee80211_tx_h_multicast_ps_buf(tx); } - - - static ieee80211_txrx_result ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) { struct ieee80211_key *key; + u16 fc = tx->fc; if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) tx->key = NULL; @@ -448,19 +446,38 @@ ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) else if ((key = rcu_dereference(tx->sdata->default_key))) tx->key = key; else if (tx->sdata->drop_unencrypted && - !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) { + !(tx->u.tx.control->flags & IEEE80211_TXCTL_EAPOL_FRAME) && + !(tx->flags & IEEE80211_TXRXD_TX_INJECTED)) { I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); return TXRX_DROP; - } else { + } else tx->key = NULL; - tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; - } if (tx->key) { + u16 ftype, stype; + tx->key->tx_rx_count++; /* TODO: add threshold stuff again */ + + switch (tx->key->conf.alg) { + case ALG_WEP: + ftype = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + + if (ftype == IEEE80211_FTYPE_MGMT && + stype == IEEE80211_STYPE_AUTH) + break; + case ALG_TKIP: + case ALG_CCMP: + if (!WLAN_FC_DATA_PRESENT(fc)) + tx->key = NULL; + break; + } } + if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) + tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; + return TXRX_CONTINUE; } @@ -567,21 +584,17 @@ ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx) static ieee80211_txrx_result ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx) { - struct rate_control_extra extra; + struct rate_selection rsel; if (likely(!tx->u.tx.rate)) { - memset(&extra, 0, sizeof(extra)); - extra.mode = tx->u.tx.mode; - extra.ethertype = tx->ethertype; - - tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, - tx->skb, &extra); - if (unlikely(extra.probe != NULL)) { + rate_control_get_rate(tx->dev, tx->u.tx.mode, tx->skb, &rsel); + tx->u.tx.rate = rsel.rate; + if (unlikely(rsel.probe != NULL)) { tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE; tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG; tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val; - tx->u.tx.rate = extra.probe; + tx->u.tx.rate = rsel.probe; } else tx->u.tx.control->alt_retry_rate = -1; @@ -591,15 +604,15 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx) tx->u.tx.control->alt_retry_rate = -1; if (tx->u.tx.mode->mode == MODE_IEEE80211G && - (tx->sdata->flags & IEEE80211_SDATA_USE_PROTECTION) && - (tx->flags & IEEE80211_TXRXD_FRAGMENTED) && extra.nonerp) { + tx->sdata->bss_conf.use_cts_prot && + (tx->flags & IEEE80211_TXRXD_FRAGMENTED) && rsel.nonerp) { tx->u.tx.last_frag_rate = tx->u.tx.rate; - if (extra.probe) + if (rsel.probe) tx->flags &= ~IEEE80211_TXRXD_TXPROBE_LAST_FRAG; else tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG; - tx->u.tx.rate = extra.nonerp; - tx->u.tx.control->rate = extra.nonerp; + tx->u.tx.rate = rsel.nonerp; + tx->u.tx.control->rate = rsel.nonerp; tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE; } else { tx->u.tx.last_frag_rate = tx->u.tx.rate; @@ -653,7 +666,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx) if (mode->mode == MODE_IEEE80211G && (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) && (tx->flags & IEEE80211_TXRXD_TXUNICAST) && - (tx->sdata->flags & IEEE80211_SDATA_USE_PROTECTION) && + tx->sdata->bss_conf.use_cts_prot && !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS)) control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT; @@ -662,7 +675,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx) * available on the network at the current point in time. */ if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && (tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) && - (tx->sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) && + tx->sdata->bss_conf.use_short_preamble && (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) { tx->u.tx.control->tx_rate = tx->u.tx.rate->val2; } @@ -706,15 +719,6 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx) } } - /* - * Tell hardware to not encrypt when we had sw crypto. - * Because we use the same flag to internally indicate that - * no (software) encryption should be done, we have to set it - * after all crypto handlers. - */ - if (tx->key && !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) - tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; - return TXRX_CONTINUE; } @@ -927,7 +931,6 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hdr *hdr; struct ieee80211_sub_if_data *sdata; - ieee80211_txrx_result res = TXRX_CONTINUE; int hdrlen; @@ -945,7 +948,7 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, /* process and remove the injection radiotap header */ sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) { + if (unlikely(sdata->vif.type == IEEE80211_IF_TYPE_MNTR)) { if (__ieee80211_parse_tx_radiotap(tx, skb) == TXRX_DROP) return TXRX_DROP; @@ -992,12 +995,10 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, } control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT; - return res; + return TXRX_CONTINUE; } -/* Device in tx->dev has a reference added; use dev_put(tx->dev) when - * finished with it. - * +/* * NB: @tx is uninitialised when passed in here */ static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, @@ -1018,6 +1019,7 @@ static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, return -ENODEV; /* initialises tx with control */ __ieee80211_tx_prepare(tx, skb, dev, control); + dev_put(dev); return 0; } @@ -1248,14 +1250,16 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, } } - control.ifindex = odev->ifindex; - control.type = osdata->type; + control.vif = &osdata->vif; + control.type = osdata->vif.type; if (pkt_data->flags & IEEE80211_TXPD_REQ_TX_STATUS) control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS; if (pkt_data->flags & IEEE80211_TXPD_DO_NOT_ENCRYPT) control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) control.flags |= IEEE80211_TXCTL_REQUEUE; + if (pkt_data->flags & IEEE80211_TXPD_EAPOL_FRAME) + control.flags |= IEEE80211_TXCTL_EAPOL_FRAME; control.queue = pkt_data->queue; ret = ieee80211_tx(odev, skb, &control); @@ -1348,6 +1352,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, int encaps_len, skip_header_bytes; int nh_pos, h_pos; struct sta_info *sta; + u32 sta_flags = 0; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (unlikely(skb->len < ETH_HLEN)) { @@ -1363,10 +1368,9 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; - /* TODO: handling for 802.1x authorized/unauthorized port */ fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; - switch (sdata->type) { + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_AP: case IEEE80211_IF_TYPE_VLAN: fc |= IEEE80211_FCTL_FROMDS; @@ -1405,16 +1409,42 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } - /* receiver is QoS enabled, use a QoS type frame */ sta = sta_info_get(local, hdr.addr1); if (sta) { - if (sta->flags & WLAN_STA_WME) { - fc |= IEEE80211_STYPE_QOS_DATA; - hdrlen += 2; - } + sta_flags = sta->flags; sta_info_put(sta); } + /* receiver is QoS enabled, use a QoS type frame */ + if (sta_flags & WLAN_STA_WME) { + fc |= IEEE80211_STYPE_QOS_DATA; + hdrlen += 2; + } + + /* + * If port access control is enabled, drop frames to unauthorised + * stations unless they are EAPOL frames from the local station. + */ + if (unlikely(sdata->ieee802_1x_pac && + !(sta_flags & WLAN_STA_AUTHORIZED) && + !(ethertype == ETH_P_PAE && + compare_ether_addr(dev->dev_addr, + skb->data + ETH_ALEN) == 0))) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + DECLARE_MAC_BUF(mac); + + if (net_ratelimit()) + printk(KERN_DEBUG "%s: dropped frame to %s" + " (unauthorized port)\n", dev->name, + print_mac(mac, hdr.addr1)); +#endif + + I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); + + ret = 0; + goto fail; + } + hdr.frame_control = cpu_to_le16(fc); hdr.duration_id = 0; hdr.seq_ctrl = 0; @@ -1503,6 +1533,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = dev->ifindex; + if (ethertype == ETH_P_PAE) + pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME; skb->dev = local->mdev; dev->stats.tx_packets++; @@ -1527,64 +1559,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, return ret; } -/* - * This is the transmit routine for the 802.11 type interfaces - * called by upper layers of the linux networking - * stack when it has a frame to transmit - */ -int ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ieee80211_sub_if_data *sdata; - struct ieee80211_tx_packet_data *pkt_data; - struct ieee80211_hdr *hdr; - u16 fc; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (skb->len < 10) { - dev_kfree_skb(skb); - return 0; - } - - if (skb_headroom(skb) < sdata->local->tx_headroom) { - if (pskb_expand_head(skb, sdata->local->tx_headroom, - 0, GFP_ATOMIC)) { - dev_kfree_skb(skb); - return 0; - } - } - - hdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - - pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; - memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); - pkt_data->ifindex = sdata->dev->ifindex; - - skb->priority = 20; /* use hardcoded priority for mgmt TX queue */ - skb->dev = sdata->local->mdev; - - /* - * We're using the protocol field of the the frame control header - * to request TX callback for hostapd. BIT(1) is checked. - */ - if ((fc & BIT(1)) == BIT(1)) { - pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS; - fc &= ~BIT(1); - hdr->frame_control = cpu_to_le16(fc); - } - - if (!(fc & IEEE80211_FCTL_PROTECTED)) - pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT; - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - dev_queue_xmit(skb); - - return 0; -} - /* helper functions for pending packets for when queues are stopped */ void ieee80211_clear_tx_pending(struct ieee80211_local *local) @@ -1653,7 +1627,8 @@ void ieee80211_tx_pending(unsigned long data) static void ieee80211_beacon_add_tim(struct ieee80211_local *local, struct ieee80211_if_ap *bss, - struct sk_buff *skb) + struct sk_buff *skb, + struct beacon_data *beacon) { u8 *pos, *tim; int aid0 = 0; @@ -1669,7 +1644,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) - bss->dtim_count = bss->dtim_period - 1; + bss->dtim_count = beacon->dtim_period - 1; else bss->dtim_count--; @@ -1677,7 +1652,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; - *pos++ = bss->dtim_period; + *pos++ = beacon->dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; @@ -1715,7 +1690,8 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, read_unlock_bh(&local->sta_lock); } -struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, +struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct ieee80211_tx_control *control) { struct ieee80211_local *local = hw_to_local(hw); @@ -1723,68 +1699,64 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, struct net_device *bdev; struct ieee80211_sub_if_data *sdata = NULL; struct ieee80211_if_ap *ap = NULL; - struct ieee80211_rate *rate; - struct rate_control_extra extra; - u8 *b_head, *b_tail; - int bh_len, bt_len; - - bdev = dev_get_by_index(&init_net, if_id); - if (bdev) { - sdata = IEEE80211_DEV_TO_SUB_IF(bdev); - ap = &sdata->u.ap; - dev_put(bdev); - } + struct rate_selection rsel; + struct beacon_data *beacon; + + rcu_read_lock(); - if (!ap || sdata->type != IEEE80211_IF_TYPE_AP || - !ap->beacon_head) { + sdata = vif_to_sdata(vif); + bdev = sdata->dev; + ap = &sdata->u.ap; + + beacon = rcu_dereference(ap->beacon); + + if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) - printk(KERN_DEBUG "no beacon data avail for idx=%d " - "(%s)\n", if_id, bdev ? bdev->name : "N/A"); + printk(KERN_DEBUG "no beacon data avail for %s\n", + bdev->name); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - return NULL; + skb = NULL; + goto out; } - /* Assume we are generating the normal beacon locally */ - b_head = ap->beacon_head; - b_tail = ap->beacon_tail; - bh_len = ap->beacon_head_len; - bt_len = ap->beacon_tail_len; - - skb = dev_alloc_skb(local->tx_headroom + - bh_len + bt_len + 256 /* maximum TIM len */); + /* headroom, head length, tail length and maximum TIM length */ + skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + + beacon->tail_len + 256); if (!skb) - return NULL; + goto out; skb_reserve(skb, local->tx_headroom); - memcpy(skb_put(skb, bh_len), b_head, bh_len); + memcpy(skb_put(skb, beacon->head_len), beacon->head, + beacon->head_len); ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data); - ieee80211_beacon_add_tim(local, ap, skb); + ieee80211_beacon_add_tim(local, ap, skb, beacon); - if (b_tail) { - memcpy(skb_put(skb, bt_len), b_tail, bt_len); - } + if (beacon->tail) + memcpy(skb_put(skb, beacon->tail_len), beacon->tail, + beacon->tail_len); if (control) { - memset(&extra, 0, sizeof(extra)); - extra.mode = local->oper_hw_mode; - - rate = rate_control_get_rate(local, local->mdev, skb, &extra); - if (!rate) { + rate_control_get_rate(local->mdev, local->oper_hw_mode, skb, + &rsel); + if (!rsel.rate) { if (net_ratelimit()) { - printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " - "found\n", wiphy_name(local->hw.wiphy)); + printk(KERN_DEBUG "%s: ieee80211_beacon_get: " + "no rate found\n", + wiphy_name(local->hw.wiphy)); } dev_kfree_skb(skb); - return NULL; + skb = NULL; + goto out; } + control->vif = vif; control->tx_rate = - ((sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) && - (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? - rate->val2 : rate->val; + (sdata->bss_conf.use_short_preamble && + (rsel.rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rsel.rate->val2 : rsel.rate->val; control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; control->power_level = local->hw.conf.power_level; control->flags |= IEEE80211_TXCTL_NO_ACK; @@ -1793,11 +1765,14 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, } ap->num_beacons++; + + out: + rcu_read_unlock(); return skb; } EXPORT_SYMBOL(ieee80211_beacon_get); -void ieee80211_rts_get(struct ieee80211_hw *hw, int if_id, +void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_control *frame_txctl, struct ieee80211_rts *rts) @@ -1807,13 +1782,14 @@ void ieee80211_rts_get(struct ieee80211_hw *hw, int if_id, fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS; rts->frame_control = cpu_to_le16(fctl); - rts->duration = ieee80211_rts_duration(hw, if_id, frame_len, frame_txctl); + rts->duration = ieee80211_rts_duration(hw, vif, frame_len, + frame_txctl); memcpy(rts->ra, hdr->addr1, sizeof(rts->ra)); memcpy(rts->ta, hdr->addr2, sizeof(rts->ta)); } EXPORT_SYMBOL(ieee80211_rts_get); -void ieee80211_ctstoself_get(struct ieee80211_hw *hw, int if_id, +void ieee80211_ctstoself_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_control *frame_txctl, struct ieee80211_cts *cts) @@ -1823,13 +1799,15 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw, int if_id, fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS; cts->frame_control = cpu_to_le16(fctl); - cts->duration = ieee80211_ctstoself_duration(hw, if_id, frame_len, frame_txctl); + cts->duration = ieee80211_ctstoself_duration(hw, vif, + frame_len, frame_txctl); memcpy(cts->ra, hdr->addr1, sizeof(cts->ra)); } EXPORT_SYMBOL(ieee80211_ctstoself_get); struct sk_buff * -ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id, +ieee80211_get_buffered_bc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct ieee80211_tx_control *control) { struct ieee80211_local *local = hw_to_local(hw); @@ -1841,16 +1819,25 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id, struct net_device *bdev; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_ap *bss = NULL; + struct beacon_data *beacon; - bdev = dev_get_by_index(&init_net, if_id); - if (bdev) { - sdata = IEEE80211_DEV_TO_SUB_IF(bdev); - bss = &sdata->u.ap; - dev_put(bdev); - } - if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head) + sdata = vif_to_sdata(vif); + bdev = sdata->dev; + + + if (!bss) return NULL; + rcu_read_lock(); + beacon = rcu_dereference(bss->beacon); + + if (sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon || + !beacon->head) { + rcu_read_unlock(); + return NULL; + } + rcu_read_unlock(); + if (bss->dtim_count != 0) return NULL; /* send buffered bc/mc only after DTIM beacon */ memset(control, 0, sizeof(*control)); @@ -1883,7 +1870,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id, if (res == TXRX_DROP || res == TXRX_QUEUED) break; } - dev_put(tx.dev); skb = tx.skb; /* handlers are allowed to change skb */ if (res == TXRX_DROP) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5a0564e1dbd..5e631ce98d7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -22,6 +22,7 @@ #include <linux/bitmap.h> #include <net/net_namespace.h> #include <net/cfg80211.h> +#include <net/rtnetlink.h> #include "ieee80211_i.h" #include "ieee80211_rate.h" @@ -39,10 +40,6 @@ const unsigned char rfc1042_header[] = const unsigned char bridge_tunnel_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -/* No encapsulation header if EtherType < 0x600 (=length) */ -static const unsigned char eapol_header[] = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e }; - static int rate_list_match(const int *rate_list, int rate) { @@ -130,17 +127,21 @@ void ieee80211_prepare_rates(struct ieee80211_local *local, } } -u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len) +u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, + enum ieee80211_if_types type) { u16 fc; - if (len < 24) + /* drop ACK/CTS frames and incorrect hdr len (ctrl) */ + if (len < 16) return NULL; fc = le16_to_cpu(hdr->frame_control); switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_DATA: + if (len < 24) /* drop incorrect hdr len (data) */ + return NULL; switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { case IEEE80211_FCTL_TODS: return hdr->addr1; @@ -153,10 +154,24 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len) } break; case IEEE80211_FTYPE_MGMT: + if (len < 24) /* drop incorrect hdr len (mgmt) */ + return NULL; return hdr->addr3; case IEEE80211_FTYPE_CTL: if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL) return hdr->addr1; + else if ((fc & IEEE80211_FCTL_STYPE) == + IEEE80211_STYPE_BACK_REQ) { + switch (type) { + case IEEE80211_IF_TYPE_STA: + return hdr->addr2; + case IEEE80211_IF_TYPE_AP: + case IEEE80211_IF_TYPE_VLAN: + return hdr->addr1; + default: + return NULL; + } + } else return NULL; } @@ -217,31 +232,6 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); -int ieee80211_is_eapol(const struct sk_buff *skb) -{ - const struct ieee80211_hdr *hdr; - u16 fc; - int hdrlen; - - if (unlikely(skb->len < 10)) - return 0; - - hdr = (const struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - - if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) - return 0; - - hdrlen = ieee80211_get_hdrlen(fc); - - if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) && - memcmp(skb->data + hdrlen, eapol_header, - sizeof(eapol_header)) == 0)) - return 1; - - return 0; -} - void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; @@ -312,45 +302,35 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, } /* Exported duration function for driver use */ -__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, int if_id, +__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, size_t frame_len, int rate) { struct ieee80211_local *local = hw_to_local(hw); - struct net_device *bdev = dev_get_by_index(&init_net, if_id); - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); u16 dur; int erp; - if (unlikely(!bdev)) - return 0; - - sdata = IEEE80211_DEV_TO_SUB_IF(bdev); erp = ieee80211_is_erp_rate(hw->conf.phymode, rate); - dur = ieee80211_frame_duration(local, frame_len, rate, - erp, sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE); + dur = ieee80211_frame_duration(local, frame_len, rate, erp, + sdata->bss_conf.use_short_preamble); - dev_put(bdev); return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_generic_frame_duration); -__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, int if_id, - size_t frame_len, +__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_control *frame_txctl) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate; - struct net_device *bdev = dev_get_by_index(&init_net, if_id); - struct ieee80211_sub_if_data *sdata; - int short_preamble; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + bool short_preamble; int erp; u16 dur; - if (unlikely(!bdev)) - return 0; - - sdata = IEEE80211_DEV_TO_SUB_IF(bdev); - short_preamble = sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE; + short_preamble = sdata->bss_conf.use_short_preamble; rate = frame_txctl->rts_rate; erp = !!(rate->flags & IEEE80211_RATE_ERP); @@ -365,28 +345,23 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, int if_id, dur += ieee80211_frame_duration(local, 10, rate->rate, erp, short_preamble); - dev_put(bdev); return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_rts_duration); -__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, int if_id, +__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, size_t frame_len, const struct ieee80211_tx_control *frame_txctl) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate; - struct net_device *bdev = dev_get_by_index(&init_net, if_id); - struct ieee80211_sub_if_data *sdata; - int short_preamble; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + bool short_preamble; int erp; u16 dur; - if (unlikely(!bdev)) - return 0; - - sdata = IEEE80211_DEV_TO_SUB_IF(bdev); - short_preamble = sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE; + short_preamble = sdata->bss_conf.use_short_preamble; rate = frame_txctl->rts_rate; erp = !!(rate->flags & IEEE80211_RATE_ERP); @@ -400,7 +375,6 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, int if_id, erp, short_preamble); } - dev_put(bdev); return cpu_to_le16(dur); } EXPORT_SYMBOL(ieee80211_ctstoself_duration); @@ -484,3 +458,37 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) ieee80211_wake_queue(hw, i); } EXPORT_SYMBOL(ieee80211_wake_queues); + +void ieee80211_iterate_active_interfaces( + struct ieee80211_hw *hw, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata; + + rcu_read_lock(); + + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + switch (sdata->vif.type) { + case IEEE80211_IF_TYPE_INVALID: + case IEEE80211_IF_TYPE_MNTR: + case IEEE80211_IF_TYPE_VLAN: + continue; + case IEEE80211_IF_TYPE_AP: + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + case IEEE80211_IF_TYPE_WDS: + break; + } + if (sdata->dev == local->mdev) + continue; + if (netif_running(sdata->dev)) + iterator(data, sdata->dev->dev_addr, + &sdata->vif); + } + + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index b5f3413403b..a0cff72a580 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -349,16 +349,6 @@ static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb) ieee80211_txrx_result ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - u16 fc; - - fc = le16_to_cpu(hdr->frame_control); - - if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA && - ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || - (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))) - return TXRX_CONTINUE; - tx->u.tx.control->iv_len = WEP_IV_LEN; tx->u.tx.control->icv_len = WEP_ICV_LEN; ieee80211_tx_set_iswep(tx); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 5b8a157975a..4e236599dd3 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -28,6 +28,7 @@ struct ieee80211_sched_data struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; }; +static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; /* given a data frame determine the 802.1p/1d tag to use */ static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) @@ -54,12 +55,12 @@ static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) return skb->priority - 256; /* check there is a valid IP header present */ - offset = ieee80211_get_hdrlen_from_skb(skb) + 8 /* LLC + proto */; - if (skb->protocol != __constant_htons(ETH_P_IP) || - skb->len < offset + sizeof(*ip)) + offset = ieee80211_get_hdrlen_from_skb(skb); + if (skb->len < offset + sizeof(llc_ip_hdr) + sizeof(*ip) || + memcmp(skb->data + offset, llc_ip_hdr, sizeof(llc_ip_hdr))) return 0; - ip = (struct iphdr *) (skb->data + offset); + ip = (struct iphdr *) (skb->data + offset + sizeof(llc_ip_hdr)); dscp = ip->tos & 0xfc; if (dscp & 0x1c) @@ -296,16 +297,16 @@ static void wme_qdiscop_destroy(struct Qdisc* qd) /* called whenever parameters are updated on existing qdisc */ -static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) +static int wme_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt) { /* struct ieee80211_sched_data *q = qdisc_priv(qd); */ /* check our options block is the right size */ /* copy any options to our local structure */ /* Ignore options block for now - always use static mapping - struct tc_ieee80211_qopt *qopt = RTA_DATA(opt); + struct tc_ieee80211_qopt *qopt = nla_data(opt); - if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) + if (opt->nla_len < nla_attr_size(sizeof(*qopt))) return -EINVAL; memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue)); */ @@ -314,7 +315,7 @@ static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) /* called during initial creation of qdisc on device */ -static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) +static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) { struct ieee80211_sched_data *q = qdisc_priv(qd); struct net_device *dev = qd->dev; @@ -369,10 +370,10 @@ static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb) struct tc_ieee80211_qopt opt; memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1); - RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); + NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); */ return skb->len; /* -rtattr_failure: +nla_put_failure: skb_trim(skb, p - skb->data);*/ return -1; } @@ -443,7 +444,7 @@ static void wme_classop_put(struct Qdisc *q, unsigned long cl) static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent, - struct rtattr **tca, unsigned long *arg) + struct nlattr **tca, unsigned long *arg) { unsigned long cl = *arg; struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); @@ -527,7 +528,7 @@ static struct tcf_proto ** wme_classop_find_tcf(struct Qdisc *qd, /* this qdisc is classful (i.e. has classes, some of which may have leaf qdiscs attached) * - these are the operations on the classes */ -static struct Qdisc_class_ops class_ops = +static const struct Qdisc_class_ops class_ops = { .graft = wme_classop_graft, .leaf = wme_classop_leaf, @@ -547,7 +548,7 @@ static struct Qdisc_class_ops class_ops = /* queueing discipline operations */ -static struct Qdisc_ops wme_qdisc_ops = +static struct Qdisc_ops wme_qdisc_ops __read_mostly = { .next = NULL, .cl_ops = &class_ops, diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 20cec1cb956..6f04311cf0a 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -245,16 +245,9 @@ static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx, ieee80211_txrx_result ieee80211_crypto_tkip_encrypt(struct ieee80211_txrx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - u16 fc; struct sk_buff *skb = tx->skb; int wpa_test = 0, test = 0; - fc = le16_to_cpu(hdr->frame_control); - - if (!WLAN_FC_DATA_PRESENT(fc)) - return TXRX_CONTINUE; - tx->u.tx.control->icv_len = TKIP_ICV_LEN; tx->u.tx.control->iv_len = TKIP_IV_LEN; ieee80211_tx_set_iswep(tx); @@ -501,16 +494,9 @@ static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx, ieee80211_txrx_result ieee80211_crypto_ccmp_encrypt(struct ieee80211_txrx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - u16 fc; struct sk_buff *skb = tx->skb; int test = 0; - fc = le16_to_cpu(hdr->frame_control); - - if (!WLAN_FC_DATA_PRESENT(fc)) - return TXRX_CONTINUE; - tx->u.tx.control->icv_len = CCMP_MIC_LEN; tx->u.tx.control->iv_len = CCMP_HDR_LEN; ieee80211_tx_set_iswep(tx); |