Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1674 commits)
  qlcnic: adding co maintainer
  ixgbe: add support for active DA cables
  ixgbe: dcb, do not tag tc_prio_control frames
  ixgbe: fix ixgbe_tx_is_paused logic
  ixgbe: always enable vlan strip/insert when DCB is enabled
  ixgbe: remove some redundant code in setting FCoE FIP filter
  ixgbe: fix wrong offset to fc_frame_header in ixgbe_fcoe_ddp
  ixgbe: fix header len when unsplit packet overflows to data buffer
  ipv6: Never schedule DAD timer on dead address
  ipv6: Use POSTDAD state
  ipv6: Use state_lock to protect ifa state
  ipv6: Replace inet6_ifaddr->dead with state
  cxgb4: notify upper drivers if the device is already up when they load
  cxgb4: keep interrupts available when the ports are brought down
  cxgb4: fix initial addition of MAC address
  cnic: Return SPQ credit to bnx2x after ring setup and shutdown.
  cnic: Convert cnic_local_flags to atomic ops.
  can: Fix SJA1000 command register writes on SMP systems
  bridge: fix build for CONFIG_SYSFS disabled
  ARCNET: Limit com20020 PCI ID matches for SOHARD cards
  ...

Fix up various conflicts with pcmcia tree drivers/net/
{pcmcia/3c589_cs.c, wireless/orinoco/orinoco_cs.c and
wireless/orinoco/spectrum_cs.c} and feature removal
(Documentation/feature-removal-schedule.txt).

Also fix a non-content conflict due to pm_qos_requirement getting
renamed in the PM tree (now pm_qos_request) in net/mac80211/scan.c
This commit is contained in:
Linus Torvalds 2010-05-20 21:04:44 -07:00
commit f8965467f3
1455 changed files with 95973 additions and 48342 deletions

View file

@ -15,8 +15,12 @@ comment "CFG80211 needs to be enabled for MAC80211"
if MAC80211 != n
config MAC80211_HAS_RC
def_bool n
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
select MAC80211_HAS_RC
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
@ -24,12 +28,14 @@ config MAC80211_RC_PID
config MAC80211_RC_MINSTREL
bool "Minstrel" if EMBEDDED
select MAC80211_HAS_RC
default y
---help---
This option enables the 'minstrel' TX rate control algorithm
choice
prompt "Default rate control algorithm"
depends on MAC80211_HAS_RC
default MAC80211_RC_DEFAULT_MINSTREL
---help---
This option selects the default rate control algorithm
@ -62,6 +68,9 @@ config MAC80211_RC_DEFAULT
endif
comment "Some wireless drivers require a rate control algorithm"
depends on MAC80211_HAS_RC=n
config MAC80211_MESH
bool "Enable mac80211 mesh networking (pre-802.11s) support"
depends on MAC80211 && EXPERIMENTAL
@ -212,8 +221,8 @@ config MAC80211_DRIVER_API_TRACER
depends on EVENT_TRACING
help
Say Y here to make mac80211 register with the ftrace
framework for the driver API -- you can see which
driver methods it is calling then by looking at the
trace.
framework for the driver API -- you can then see which
driver methods it is calling and which API functions
drivers are calling by looking at the trace.
If unsure, say N.
If unsure, say Y.

View file

@ -23,7 +23,8 @@ mac80211-y := \
key.o \
util.o \
wme.o \
event.o
event.o \
chan.o
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \

View file

@ -19,23 +19,25 @@
#include "ieee80211_i.h"
#include "driver-ops.h"
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason)
static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason,
bool from_timer)
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_rx *tid_rx;
int i;
/* check if TID is in operational state */
spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) {
/* check if TID is in operational state */
if (!sta->ampdu_mlme.tid_active_rx[tid]) {
spin_unlock_bh(&sta->lock);
return;
}
sta->ampdu_mlme.tid_state_rx[tid] =
HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
spin_unlock_bh(&sta->lock);
sta->ampdu_mlme.tid_active_rx[tid] = false;
tid_rx = sta->ampdu_mlme.tid_rx[tid];
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
@ -47,61 +49,42 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
printk(KERN_DEBUG "HW problem - can not stop rx "
"aggregation 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)
if (initiator == WLAN_BACK_RECIPIENT)
ieee80211_send_delba(sta->sdata, sta->sta.addr,
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]) {
for (i = 0; i < tid_rx->buf_size; i++) {
if (tid_rx->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;
dev_kfree_skb(tid_rx->reorder_buf[i]);
tid_rx->stored_mpdu_num--;
tid_rx->reorder_buf[i] = NULL;
}
}
spin_lock_bh(&sta->lock);
/* free resources */
kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_time);
kfree(tid_rx->reorder_buf);
kfree(tid_rx->reorder_time);
sta->ampdu_mlme.tid_rx[tid] = NULL;
if (!sta->ampdu_mlme.tid_rx[tid]->shutdown) {
kfree(sta->ampdu_mlme.tid_rx[tid]);
sta->ampdu_mlme.tid_rx[tid] = NULL;
}
sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
spin_unlock_bh(&sta->lock);
if (!from_timer)
del_timer_sync(&tid_rx->session_timer);
kfree(tid_rx);
}
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
u16 initiator, u16 reason)
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason)
{
struct sta_info *sta;
rcu_read_lock();
sta = sta_info_get(sdata, ra);
if (!sta) {
rcu_read_unlock();
return;
}
__ieee80211_stop_rx_ba_session(sta, tid, initiator, reason);
rcu_read_unlock();
___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, false);
}
/*
* After accepting the AddBA Request we activated a timer,
* resetting it after each frame that arrives from the originator.
* if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
*/
static void sta_rx_agg_session_timer_expired(unsigned long data)
{
@ -117,9 +100,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
#endif
ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
(u16)*ptid, WLAN_BACK_TIMER,
WLAN_REASON_QSTA_TIMEOUT);
___ieee80211_stop_rx_ba_session(sta, *ptid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_TIMEOUT, true);
}
static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
@ -194,7 +176,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
status = WLAN_STATUS_REQUEST_DECLINED;
if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Suspend in progress. "
"Denying ADDBA request\n");
@ -232,7 +214,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* examine state machine */
spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
if (sta->ampdu_mlme.tid_active_rx[tid]) {
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "unexpected AddBA Req from "
@ -294,7 +276,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
}
/* change state and send addba resp */
sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
sta->ampdu_mlme.tid_active_rx[tid] = true;
tid_agg_rx->dialog_token = dialog_token;
tid_agg_rx->ssn = start_seq_num;
tid_agg_rx->head_seq_num = start_seq_num;

View file

@ -186,7 +186,7 @@ static void sta_addba_resp_timer_expired(unsigned long data)
spin_unlock_bh(&sta->lock);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "timer expired on tid %d but we are not "
"(or no longer) expecting addBA response there",
"(or no longer) expecting addBA response there\n",
tid);
#endif
return;
@ -214,6 +214,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
int ret = 0;
u16 start_seq_num;
trace_api_start_tx_ba_session(pubsta, tid);
if (WARN_ON(!local->ops->ampdu_action))
return -EINVAL;
@ -245,7 +247,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
return -EINVAL;
}
if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Suspend in progress. "
"Denying BA session request\n");
@ -414,7 +416,7 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid);
#endif
spin_lock(&local->ampdu_lock);
@ -440,6 +442,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
struct sta_info *sta;
u8 *state;
trace_api_start_tx_ba_cb(sdata, ra, tid);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
@ -541,6 +545,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
trace_api_stop_tx_ba_session(pubsta, tid, initiator);
if (!local->ops->ampdu_action)
return -EINVAL;
@ -558,6 +564,8 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
struct sta_info *sta;
u8 *state;
trace_api_stop_tx_ba_cb(sdata, ra, tid);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
@ -674,7 +682,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
del_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)

View file

@ -97,9 +97,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
params->mesh_id_len,
params->mesh_id);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
return 0;
if (type == NL80211_IFTYPE_AP_VLAN &&
params && params->use_4addr == 0)
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
@ -107,7 +104,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
params && params->use_4addr >= 0)
sdata->u.mgd.use_4addr = params->use_4addr;
sdata->u.mntr_flags = *flags;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags)
sdata->u.mntr_flags = *flags;
return 0;
}
@ -411,6 +410,17 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
int idx, struct survey_info *survey)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
if (!local->ops->get_survey)
return -EOPNOTSUPP;
return drv_get_survey(local, idx, survey);
}
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_info *sinfo)
{
@ -1104,6 +1114,13 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
changed |= BSS_CHANGED_BASIC_RATES;
}
if (params->ap_isolate >= 0) {
if (params->ap_isolate)
sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
else
sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
}
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
@ -1137,19 +1154,47 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
return -EINVAL;
}
/* enable WMM or activate new settings */
local->hw.conf.flags |= IEEE80211_CONF_QOS;
drv_config(local, IEEE80211_CONF_CHANGE_QOS);
return 0;
}
static int ieee80211_set_channel(struct wiphy *wiphy,
struct net_device *netdev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = NULL;
if (netdev)
sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
switch (ieee80211_get_channel_mode(local, NULL)) {
case CHAN_MODE_HOPPING:
return -EBUSY;
case CHAN_MODE_FIXED:
if (local->oper_channel != chan)
return -EBUSY;
if (!sdata && local->_oper_channel_type == channel_type)
return 0;
break;
case CHAN_MODE_UNDEFINED:
break;
}
local->oper_channel = chan;
local->oper_channel_type = channel_type;
return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
if (!ieee80211_set_channel_type(local, sdata, channel_type))
return -EBUSY;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
return 0;
}
#ifdef CONFIG_PM
@ -1193,6 +1238,20 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (ieee80211_get_channel_mode(local, sdata)) {
case CHAN_MODE_HOPPING:
return -EBUSY;
case CHAN_MODE_FIXED:
if (local->oper_channel == req->bss->channel)
break;
return -EBUSY;
case CHAN_MODE_UNDEFINED:
break;
}
return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
@ -1215,8 +1274,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (ieee80211_get_channel_mode(local, sdata)) {
case CHAN_MODE_HOPPING:
return -EBUSY;
case CHAN_MODE_FIXED:
if (!params->channel_fixed)
return -EBUSY;
if (local->oper_channel == params->channel)
break;
return -EBUSY;
case CHAN_MODE_UNDEFINED:
break;
}
return ieee80211_ibss_join(sdata, params);
}
@ -1345,7 +1418,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
* association, there's no need to send an action frame.
*/
if (!sdata->u.mgd.associated ||
sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
mutex_lock(&sdata->local->iflist_mtx);
ieee80211_recalc_smps(sdata->local, sdata);
mutex_unlock(&sdata->local->iflist_mtx);
@ -1384,11 +1457,11 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
return -EOPNOTSUPP;
if (enabled == sdata->u.mgd.powersave &&
timeout == conf->dynamic_ps_timeout)
timeout == conf->dynamic_ps_forced_timeout)
return 0;
sdata->u.mgd.powersave = enabled;
conf->dynamic_ps_timeout = timeout;
conf->dynamic_ps_forced_timeout = timeout;
/* no change, but if automatic follow powersave */
mutex_lock(&sdata->u.mgd.mtx);
@ -1403,6 +1476,35 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_vif *vif = &sdata->vif;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
if (rssi_thold == bss_conf->cqm_rssi_thold &&
rssi_hyst == bss_conf->cqm_rssi_hyst)
return 0;
bss_conf->cqm_rssi_thold = rssi_thold;
bss_conf->cqm_rssi_hyst = rssi_hyst;
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
return 0;
}
/* tell the driver upon association, unless already associated */
if (sdata->u.mgd.associated)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
return 0;
}
static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr,
@ -1475,6 +1577,7 @@ struct cfg80211_ops mac80211_config_ops = {
.change_station = ieee80211_change_station,
.get_station = ieee80211_get_station,
.dump_station = ieee80211_dump_station,
.dump_survey = ieee80211_dump_survey,
#ifdef CONFIG_MAC80211_MESH
.add_mpath = ieee80211_add_mpath,
.del_mpath = ieee80211_del_mpath,
@ -1507,4 +1610,5 @@ struct cfg80211_ops mac80211_config_ops = {
.remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.action = ieee80211_action,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
};

127
net/mac80211/chan.c Normal file
View file

@ -0,0 +1,127 @@
/*
* mac80211 - channel management
*/
#include <linux/nl80211.h>
#include "ieee80211_i.h"
enum ieee80211_chan_mode
__ieee80211_get_channel_mode(struct ieee80211_local *local,
struct ieee80211_sub_if_data *ignore)
{
struct ieee80211_sub_if_data *sdata;
WARN_ON(!mutex_is_locked(&local->iflist_mtx));
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata == ignore)
continue;
if (!ieee80211_sdata_running(sdata))
continue;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
continue;
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!sdata->u.mgd.associated)
continue;
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
if (!sdata->u.ibss.ssid_len)
continue;
if (!sdata->u.ibss.fixed_channel)
return CHAN_MODE_HOPPING;
}
if (sdata->vif.type == NL80211_IFTYPE_AP &&
!sdata->u.ap.beacon)
continue;
return CHAN_MODE_FIXED;
}
return CHAN_MODE_UNDEFINED;
}
enum ieee80211_chan_mode
ieee80211_get_channel_mode(struct ieee80211_local *local,
struct ieee80211_sub_if_data *ignore)
{
enum ieee80211_chan_mode mode;
mutex_lock(&local->iflist_mtx);
mode = __ieee80211_get_channel_mode(local, ignore);
mutex_unlock(&local->iflist_mtx);
return mode;
}
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype)
{
struct ieee80211_sub_if_data *tmp;
enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
bool result;
mutex_lock(&local->iflist_mtx);
list_for_each_entry(tmp, &local->interfaces, list) {
if (tmp == sdata)
continue;
if (!ieee80211_sdata_running(tmp))
continue;
switch (tmp->vif.bss_conf.channel_type) {
case NL80211_CHAN_NO_HT:
case NL80211_CHAN_HT20:
superchan = tmp->vif.bss_conf.channel_type;
break;
case NL80211_CHAN_HT40PLUS:
WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
superchan = NL80211_CHAN_HT40PLUS;
break;
case NL80211_CHAN_HT40MINUS:
WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
superchan = NL80211_CHAN_HT40MINUS;
break;
}
}
switch (superchan) {
case NL80211_CHAN_NO_HT:
case NL80211_CHAN_HT20:
/*
* allow any change that doesn't go to no-HT
* (if it already is no-HT no change is needed)
*/
if (chantype == NL80211_CHAN_NO_HT)
break;
superchan = chantype;
break;
case NL80211_CHAN_HT40PLUS:
case NL80211_CHAN_HT40MINUS:
/* allow smaller bandwidth and same */
if (chantype == NL80211_CHAN_NO_HT)
break;
if (chantype == NL80211_CHAN_HT20)
break;
if (superchan == chantype)
break;
result = false;
goto out;
}
local->_oper_channel_type = superchan;
if (sdata)
sdata->vif.bss_conf.channel_type = chantype;
result = true;
out:
mutex_unlock(&local->iflist_mtx);
return result;
}

View file

@ -7,7 +7,6 @@ extern int mac80211_open_file_generic(struct inode *inode, struct file *file);
#else
static inline void debugfs_hw_add(struct ieee80211_local *local)
{
return;
}
#endif

View file

@ -100,6 +100,14 @@ static ssize_t ieee80211_if_fmt_##name( \
return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
#define IEEE80211_IF_FMT_DEC_DIV_16(name, field) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, \
char *buf, int buflen) \
{ \
return scnprintf(buf, buflen, "%d\n", sdata->field / 16); \
}
#define __IEEE80211_IF_FILE(name, _write) \
static ssize_t ieee80211_if_read_##name(struct file *file, \
char __user *userbuf, \
@ -140,6 +148,8 @@ IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC);
IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16);
static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode)
@ -276,6 +286,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(bssid);
DEBUGFS_ADD(aid);
DEBUGFS_ADD(last_beacon);
DEBUGFS_ADD(ave_beacon);
DEBUGFS_ADD_MODE(smps, 0600);
}

View file

@ -39,6 +39,13 @@ static const struct file_operations sta_ ##name## _ops = { \
.open = mac80211_open_file_generic, \
}
#define STA_OPS_RW(name) \
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
.write = sta_##name##_write, \
.open = mac80211_open_file_generic, \
}
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
@ -57,7 +64,6 @@ STA_FILE(tx_filtered, tx_filtered_count, LU);
STA_FILE(tx_retry_failed, tx_retry_failed, LU);
STA_FILE(tx_retry_count, tx_retry_count, LU);
STA_FILE(last_signal, last_signal, D);
STA_FILE(last_noise, last_noise, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
@ -120,7 +126,7 @@ STA_OPS(last_seq_ctrl);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[64 + STA_TID_NUM * 40], *p = buf;
char buf[71 + STA_TID_NUM * 40], *p = buf;
int i;
struct sta_info *sta = file->private_data;
@ -128,16 +134,16 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",
sta->ampdu_mlme.dialog_token_allocator + 1);
p += scnprintf(p, sizeof(buf) + buf - p,
"TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n");
"TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n");
for (i = 0; i < STA_TID_NUM; i++) {
p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i);
p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x",
sta->ampdu_mlme.tid_state_rx[i]);
sta->ampdu_mlme.tid_active_rx[i]);
p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
sta->ampdu_mlme.tid_state_rx[i] ?
sta->ampdu_mlme.tid_active_rx[i] ?
sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x",
sta->ampdu_mlme.tid_state_rx[i] ?
sta->ampdu_mlme.tid_active_rx[i] ?
sta->ampdu_mlme.tid_rx[i]->ssn : 0);
p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x",
@ -157,7 +163,63 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
STA_OPS(agg_status);
static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
char _buf[12], *buf = _buf;
struct sta_info *sta = file->private_data;
bool start, tx;
unsigned long tid;
int ret;
if (count > sizeof(_buf))
return -EINVAL;
if (copy_from_user(buf, userbuf, count))
return -EFAULT;
buf[sizeof(_buf) - 1] = '\0';
if (strncmp(buf, "tx ", 3) == 0) {
buf += 3;
tx = true;
} else if (strncmp(buf, "rx ", 3) == 0) {
buf += 3;
tx = false;
} else
return -EINVAL;
if (strncmp(buf, "start ", 6) == 0) {
buf += 6;
start = true;
if (!tx)
return -EINVAL;
} else if (strncmp(buf, "stop ", 5) == 0) {
buf += 5;
start = false;
} else
return -EINVAL;
tid = simple_strtoul(buf, NULL, 0);
if (tid >= STA_TID_NUM)
return -EINVAL;
if (tx) {
if (start)
ret = ieee80211_start_tx_ba_session(&sta->sta, tid);
else
ret = ieee80211_stop_tx_ba_session(&sta->sta, tid,
WLAN_BACK_RECIPIENT);
} else {
__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, 3);
ret = 0;
}
return ret ?: count;
}
STA_OPS_RW(agg_status);
static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@ -177,7 +239,7 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
if (htc->ht_supported) {
p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.4x\n", htc->cap);
PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDCP");
PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDPC");
PRINT_HT_CAP((htc->cap & BIT(1)), "HT20/HT40");
PRINT_HT_CAP(!(htc->cap & BIT(1)), "HT20");
@ -289,7 +351,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(tx_retry_failed);
DEBUGFS_ADD(tx_retry_count);
DEBUGFS_ADD(last_signal);
DEBUGFS_ADD(last_noise);
DEBUGFS_ADD(wep_weak_iv_count);
DEBUGFS_ADD(ht_capa);
}

View file

@ -84,16 +84,14 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
}
static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
int mc_count,
struct dev_addr_list *mc_list)
struct netdev_hw_addr_list *mc_list)
{
u64 ret = 0;
if (local->ops->prepare_multicast)
ret = local->ops->prepare_multicast(&local->hw, mc_count,
mc_list);
ret = local->ops->prepare_multicast(&local->hw, mc_list);
trace_drv_prepare_multicast(local, mc_count, ret);
trace_drv_prepare_multicast(local, mc_list->count, ret);
return ret;
}
@ -154,14 +152,15 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
}
static inline int drv_hw_scan(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req)
{
int ret;
might_sleep();
ret = local->ops->hw_scan(&local->hw, req);
trace_drv_hw_scan(local, req, ret);
ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
trace_drv_hw_scan(local, sdata, req, ret);
return ret;
}
@ -346,6 +345,15 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
return ret;
}
static inline int drv_get_survey(struct ieee80211_local *local, int idx,
struct survey_info *survey)
{
int ret = -EOPNOTSUPP;
if (local->ops->conf_tx)
ret = local->ops->get_survey(&local->hw, idx, survey);
/* trace_drv_get_survey(local, idx, survey, ret); */
return ret;
}
static inline void drv_rfkill_poll(struct ieee80211_local *local)
{
@ -363,4 +371,15 @@ static inline void drv_flush(struct ieee80211_local *local, bool drop)
if (local->ops->flush)
local->ops->flush(&local->hw, drop);
}
static inline void drv_channel_switch(struct ieee80211_local *local,
struct ieee80211_channel_switch *ch_switch)
{
might_sleep();
local->ops->channel_switch(&local->hw, ch_switch);
trace_drv_channel_switch(local, ch_switch);
}
#endif /* __MAC80211_DRIVER_OPS */

View file

@ -32,6 +32,10 @@ static inline void trace_ ## name(proto) {}
#define VIF_PR_FMT " vif:%s(%d)"
#define VIF_PR_ARG __get_str(vif_name), __entry->vif_type
/*
* Tracing for driver callbacks.
*/
TRACE_EVENT(drv_start,
TP_PROTO(struct ieee80211_local *local, int ret),
@ -359,23 +363,26 @@ TRACE_EVENT(drv_update_tkip_key,
TRACE_EVENT(drv_hw_scan,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req, int ret),
TP_ARGS(local, req, ret),
TP_ARGS(local, sdata, req, ret),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(int, ret)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->ret = ret;
),
TP_printk(
LOCAL_PR_FMT " ret:%d",
LOCAL_PR_ARG, __entry->ret
LOCAL_PR_FMT VIF_PR_FMT " ret:%d",
LOCAL_PR_ARG,VIF_PR_ARG, __entry->ret
)
);
@ -766,6 +773,326 @@ TRACE_EVENT(drv_flush,
LOCAL_PR_ARG, __entry->drop
)
);
TRACE_EVENT(drv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_channel_switch *ch_switch),
TP_ARGS(local, ch_switch),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(u64, timestamp)
__field(bool, block_tx)
__field(u16, freq)
__field(u8, count)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->timestamp = ch_switch->timestamp;
__entry->block_tx = ch_switch->block_tx;
__entry->freq = ch_switch->channel->center_freq;
__entry->count = ch_switch->count;
),
TP_printk(
LOCAL_PR_FMT " new freq:%u count:%d",
LOCAL_PR_ARG, __entry->freq, __entry->count
)
);
/*
* Tracing for API calls that drivers call.
*/
TRACE_EVENT(api_start_tx_ba_session,
TP_PROTO(struct ieee80211_sta *sta, u16 tid),
TP_ARGS(sta, tid),
TP_STRUCT__entry(
STA_ENTRY
__field(u16, tid)
),
TP_fast_assign(
STA_ASSIGN;
__entry->tid = tid;
),
TP_printk(
STA_PR_FMT " tid:%d",
STA_PR_ARG, __entry->tid
)
);
TRACE_EVENT(api_start_tx_ba_cb,
TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid),
TP_ARGS(sdata, ra, tid),
TP_STRUCT__entry(
VIF_ENTRY
__array(u8, ra, ETH_ALEN)
__field(u16, tid)
),
TP_fast_assign(
VIF_ASSIGN;
memcpy(__entry->ra, ra, ETH_ALEN);
__entry->tid = tid;
),
TP_printk(
VIF_PR_FMT " ra:%pM tid:%d",
VIF_PR_ARG, __entry->ra, __entry->tid
)
);
TRACE_EVENT(api_stop_tx_ba_session,
TP_PROTO(struct ieee80211_sta *sta, u16 tid, u16 initiator),
TP_ARGS(sta, tid, initiator),
TP_STRUCT__entry(
STA_ENTRY
__field(u16, tid)
__field(u16, initiator)
),
TP_fast_assign(
STA_ASSIGN;
__entry->tid = tid;
__entry->initiator = initiator;
),
TP_printk(
STA_PR_FMT " tid:%d initiator:%d",
STA_PR_ARG, __entry->tid, __entry->initiator
)
);
TRACE_EVENT(api_stop_tx_ba_cb,
TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid),
TP_ARGS(sdata, ra, tid),
TP_STRUCT__entry(
VIF_ENTRY
__array(u8, ra, ETH_ALEN)
__field(u16, tid)
),
TP_fast_assign(
VIF_ASSIGN;
memcpy(__entry->ra, ra, ETH_ALEN);
__entry->tid = tid;
),
TP_printk(
VIF_PR_FMT " ra:%pM tid:%d",
VIF_PR_ARG, __entry->ra, __entry->tid
)
);
TRACE_EVENT(api_restart_hw,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local),
TP_STRUCT__entry(
LOCAL_ENTRY
),
TP_fast_assign(
LOCAL_ASSIGN;
),
TP_printk(
LOCAL_PR_FMT,
LOCAL_PR_ARG
)
);
TRACE_EVENT(api_beacon_loss,
TP_PROTO(struct ieee80211_sub_if_data *sdata),
TP_ARGS(sdata),
TP_STRUCT__entry(
VIF_ENTRY
),
TP_fast_assign(
VIF_ASSIGN;
),
TP_printk(
VIF_PR_FMT,
VIF_PR_ARG
)
);
TRACE_EVENT(api_connection_loss,
TP_PROTO(struct ieee80211_sub_if_data *sdata),
TP_ARGS(sdata),
TP_STRUCT__entry(
VIF_ENTRY
),
TP_fast_assign(
VIF_ASSIGN;
),
TP_printk(
VIF_PR_FMT,
VIF_PR_ARG
)
);
TRACE_EVENT(api_cqm_rssi_notify,
TP_PROTO(struct ieee80211_sub_if_data *sdata,
enum nl80211_cqm_rssi_threshold_event rssi_event),
TP_ARGS(sdata, rssi_event),
TP_STRUCT__entry(
VIF_ENTRY
__field(u32, rssi_event)
),
TP_fast_assign(
VIF_ASSIGN;
__entry->rssi_event = rssi_event;
),
TP_printk(
VIF_PR_FMT " event:%d",
VIF_PR_ARG, __entry->rssi_event
)
);
TRACE_EVENT(api_scan_completed,
TP_PROTO(struct ieee80211_local *local, bool aborted),
TP_ARGS(local, aborted),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(bool, aborted)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->aborted = aborted;
),
TP_printk(
LOCAL_PR_FMT " aborted:%d",
LOCAL_PR_ARG, __entry->aborted
)
);
TRACE_EVENT(api_sta_block_awake,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta, bool block),
TP_ARGS(local, sta, block),
TP_STRUCT__entry(
LOCAL_ENTRY
STA_ENTRY
__field(bool, block)
),
TP_fast_assign(
LOCAL_ASSIGN;
STA_ASSIGN;
__entry->block = block;
),
TP_printk(
LOCAL_PR_FMT STA_PR_FMT " block:%d",
LOCAL_PR_ARG, STA_PR_FMT, __entry->block
)
);
TRACE_EVENT(api_chswitch_done,
TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success),
TP_ARGS(sdata, success),
TP_STRUCT__entry(
VIF_ENTRY
__field(bool, success)
),
TP_fast_assign(
VIF_ASSIGN;
__entry->success = success;
),
TP_printk(
VIF_PR_FMT " success=%d",
VIF_PR_ARG, __entry->success
)
);
/*
* Tracing for internal functions
* (which may also be called in response to driver calls)
*/
TRACE_EVENT(wake_queue,
TP_PROTO(struct ieee80211_local *local, u16 queue,
enum queue_stop_reason reason),
TP_ARGS(local, queue, reason),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(u16, queue)
__field(u32, reason)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->queue = queue;
__entry->reason = reason;
),
TP_printk(
LOCAL_PR_FMT " queue:%d, reason:%d",
LOCAL_PR_ARG, __entry->queue, __entry->reason
)
);
TRACE_EVENT(stop_queue,
TP_PROTO(struct ieee80211_local *local, u16 queue,
enum queue_stop_reason reason),
TP_ARGS(local, queue, reason),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(u16, queue)
__field(u32, reason)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->queue = queue;
__entry->reason = reason;
),
TP_printk(
LOCAL_PR_FMT " queue:%d, reason:%d",
LOCAL_PR_ARG, __entry->queue, __entry->reason
)
);
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH

View file

@ -175,8 +175,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (initiator == WLAN_BACK_INITIATOR)
ieee80211_sta_stop_rx_ba_session(sdata, sta->sta.addr, tid,
WLAN_BACK_INITIATOR, 0);
__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0);
else { /* WLAN_BACK_RECIPIENT */
spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK)

View file

@ -92,12 +92,18 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
if (memcmp(ifibss->bssid, bssid, ETH_ALEN))
sta_info_flush(sdata->local, sdata);
/* if merging, indicate to driver that we leave the old IBSS */
if (sdata->vif.bss_conf.ibss_joined) {
sdata->vif.bss_conf.ibss_joined = false;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
}
memcpy(ifibss->bssid, bssid, ETH_ALEN);
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
local->oper_channel = chan;
local->oper_channel_type = NL80211_CHAN_NO_HT;
WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
sband = local->hw.wiphy->bands[chan->band];
@ -171,6 +177,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss_change |= BSS_CHANGED_BSSID;
bss_change |= BSS_CHANGED_BEACON;
bss_change |= BSS_CHANGED_BEACON_ENABLED;
bss_change |= BSS_CHANGED_IBSS;
sdata->vif.bss_conf.ibss_joined = true;
ieee80211_bss_info_change_notify(sdata, bss_change);
ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
@ -265,17 +273,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band);
if (sta->sta.supp_rates[band] != prev_rates) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (sta->sta.supp_rates[band] != prev_rates)
printk(KERN_DEBUG "%s: updated supp_rates set "
"for %pM based on beacon info (0x%llx | "
"0x%llx -> 0x%llx)\n",
sdata->name,
sta->sta.addr,
(unsigned long long) prev_rates,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
"for %pM based on beacon/probe_response "
"(0x%x -> 0x%x)\n",
sdata->name, sta->sta.addr,
prev_rates, sta->sta.supp_rates[band]);
#endif
rate_control_rate_init(sta);
}
rcu_read_unlock();
} else {
rcu_read_unlock();
@ -371,6 +378,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
}
@ -481,7 +489,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
"IBSS networks with same SSID (merge)\n", sdata->name);
ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len);
ieee80211_request_internal_scan(sdata,
ifibss->ssid, ifibss->ssid_len,
ifibss->fixed_channel ? ifibss->channel : NULL);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@ -588,8 +598,9 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
"join\n", sdata->name);
ieee80211_request_internal_scan(sdata, ifibss->ssid,
ifibss->ssid_len);
ieee80211_request_internal_scan(sdata,
ifibss->ssid, ifibss->ssid_len,
ifibss->fixed_channel ? ifibss->channel : NULL);
} else {
int interval = IEEE80211_SCAN_INTERVAL;
@ -897,6 +908,13 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.channel = params->channel;
sdata->u.ibss.fixed_channel = params->channel_fixed;
/* fix ourselves to that channel now already */
if (params->channel_fixed) {
sdata->local->oper_channel = params->channel;
WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
NL80211_CHAN_NO_HT));
}
if (params->ie) {
sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
GFP_KERNEL);
@ -951,7 +969,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
kfree(sdata->u.ibss.ie);
skb = sdata->u.ibss.presp;
rcu_assign_pointer(sdata->u.ibss.presp, NULL);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
sdata->vif.bss_conf.ibss_joined = false;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
synchronize_rcu();
kfree_skb(skb);

View file

@ -317,6 +317,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
};
struct ieee80211_if_managed {
@ -327,7 +328,7 @@ struct ieee80211_if_managed {
struct work_struct work;
struct work_struct monitor_work;
struct work_struct chswitch_work;
struct work_struct beacon_loss_work;
struct work_struct beacon_connection_loss_work;
unsigned long probe_timeout;
int probe_send_count;
@ -359,6 +360,24 @@ struct ieee80211_if_managed {
int wmm_last_param_set;
u8 use_4addr;
/* Signal strength from the last Beacon frame in the current BSS. */
int last_beacon_signal;
/*
* Weighted average of the signal strength from Beacon frames in the
* current BSS. This is in units of 1/16 of the signal unit to maintain
* accuracy and to speed up calculations, i.e., the value need to be
* divided by 16 to get the actual value.
*/
int ave_beacon_signal;
/*
* Last Beacon frame signal strength average (ave_beacon_signal / 16)
* that triggered a cqm event. 0 indicates that no event has been
* generated for the current association.
*/
int last_cqm_event_signal;
};
enum ieee80211_ibss_request {
@ -646,8 +665,7 @@ struct ieee80211_local {
struct work_struct recalc_smps;
/* aggregated multicast list */
struct dev_addr_list *mc_list;
int mc_count;
struct netdev_hw_addr_list mc_list;
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
@ -745,10 +763,11 @@ struct ieee80211_local {
int scan_channel_idx;
int scan_ies_len;
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
enum nl80211_channel_type oper_channel_type;
enum nl80211_channel_type _oper_channel_type;
struct ieee80211_channel *oper_channel, *csa_channel;
/* Temporary remain-on-channel for off-channel operations */
@ -979,7 +998,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
unsigned long data, void *dummy);
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss);
struct ieee80211_bss *bss,
u64 timestamp);
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
@ -1000,7 +1020,8 @@ void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
/* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len);
const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
void ieee80211_scan_cancel(struct ieee80211_local *local);
@ -1078,8 +1099,6 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps, const u8 *da,
const u8 *bssid);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
u16 tid, u16 initiator, u16 reason);
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
u16 initiator, u16 reason);
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta);
@ -1155,7 +1174,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
int powersave);
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr);
void ieee80211_beacon_loss_work(struct work_struct *work);
void ieee80211_beacon_connection_loss_work(struct work_struct *work);
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason);
@ -1210,6 +1229,20 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
int ieee80211_wk_cancel_remain_on_channel(
struct ieee80211_sub_if_data *sdata, u64 cookie);
/* channel management */
enum ieee80211_chan_mode {
CHAN_MODE_UNDEFINED,
CHAN_MODE_HOPPING,
CHAN_MODE_FIXED,
};
enum ieee80211_chan_mode
ieee80211_get_channel_mode(struct ieee80211_local *local,
struct ieee80211_sub_if_data *ignore);
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else

View file

@ -413,8 +413,7 @@ static int ieee80211_stop(struct net_device *dev)
netif_addr_lock_bh(dev);
spin_lock_bh(&local->filter_lock);
__dev_addr_unsync(&local->mc_list, &local->mc_count,
&dev->mc_list, &dev->mc_count);
__hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len);
spin_unlock_bh(&local->filter_lock);
netif_addr_unlock_bh(dev);
@ -487,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
cancel_work_sync(&sdata->u.mgd.work);
cancel_work_sync(&sdata->u.mgd.chswitch_work);
cancel_work_sync(&sdata->u.mgd.monitor_work);
cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
/*
* When we get here, the interface is marked down.
@ -597,8 +596,7 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
sdata->flags ^= IEEE80211_SDATA_PROMISC;
}
spin_lock_bh(&local->filter_lock);
__dev_addr_sync(&local->mc_list, &local->mc_count,
&dev->mc_list, &dev->mc_count);
__hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
spin_unlock_bh(&local->filter_lock);
ieee80211_queue_work(&local->hw, &local->reconfig_filter);
}
@ -816,6 +814,118 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
return 0;
}
static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
struct net_device *dev,
enum nl80211_iftype type)
{
struct ieee80211_sub_if_data *sdata;
u64 mask, start, addr, val, inc;
u8 *m;
u8 tmp_addr[ETH_ALEN];
int i;
/* default ... something at least */
memcpy(dev->perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
if (is_zero_ether_addr(local->hw.wiphy->addr_mask) &&
local->hw.wiphy->n_addresses <= 1)
return;
mutex_lock(&local->iflist_mtx);
switch (type) {
case NL80211_IFTYPE_MONITOR:
/* doesn't matter */
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
/* match up with an AP interface */
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP)
continue;
memcpy(dev->perm_addr, sdata->vif.addr, ETH_ALEN);
break;
}
/* keep default if no AP interface present */
break;
default:
/* assign a new address if possible -- try n_addresses first */
for (i = 0; i < local->hw.wiphy->n_addresses; i++) {
bool used = false;
list_for_each_entry(sdata, &local->interfaces, list) {
if (memcmp(local->hw.wiphy->addresses[i].addr,
sdata->vif.addr, ETH_ALEN) == 0) {
used = true;
break;
}
}
if (!used) {
memcpy(dev->perm_addr,
local->hw.wiphy->addresses[i].addr,
ETH_ALEN);
break;
}
}
/* try mask if available */
if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
break;
m = local->hw.wiphy->addr_mask;
mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {
/* not a contiguous mask ... not handled now! */
printk(KERN_DEBUG "not contiguous\n");
break;
}
m = local->hw.wiphy->perm_addr;
start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
inc = 1ULL<<__ffs64(mask);
val = (start & mask);
addr = (start & ~mask) | (val & mask);
do {
bool used = false;
tmp_addr[5] = addr >> 0*8;
tmp_addr[4] = addr >> 1*8;
tmp_addr[3] = addr >> 2*8;
tmp_addr[2] = addr >> 3*8;
tmp_addr[1] = addr >> 4*8;
tmp_addr[0] = addr >> 5*8;
val += inc;
list_for_each_entry(sdata, &local->interfaces, list) {
if (memcmp(tmp_addr, sdata->vif.addr,
ETH_ALEN) == 0) {
used = true;
break;
}
}
if (!used) {
memcpy(dev->perm_addr, tmp_addr, ETH_ALEN);
break;
}
addr = (start & ~mask) | (val & mask);
} while (addr != start);
break;
}
mutex_unlock(&local->iflist_mtx);
}
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct net_device **new_dev, enum nl80211_iftype type,
struct vif_params *params)
@ -845,8 +955,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
if (ret < 0)
goto fail;
memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN);
ieee80211_assign_perm_addr(local, ndev, type);
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
/* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */

View file

@ -140,6 +140,7 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
struct ieee80211_sub_if_data,
u.ap);
key->conf.ap_addr = sdata->dev->dev_addr;
ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
if (!ret) {

View file

@ -71,7 +71,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
spin_lock_bh(&local->filter_lock);
changed_flags = local->filter_flags ^ new_flags;
mc = drv_prepare_multicast(local, local->mc_count, local->mc_list);
mc = drv_prepare_multicast(local, &local->mc_list);
spin_unlock_bh(&local->filter_lock);
/* be a bit nasty */
@ -111,7 +111,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
channel_type = local->oper_channel_type;
channel_type = local->_oper_channel_type;
}
if (chan != local->hw.conf.channel ||
@ -309,6 +309,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
trace_api_restart_hw(local);
/* use this reason, __ieee80211_resume will unblock it */
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
@ -388,6 +390,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
INIT_LIST_HEAD(&local->interfaces);
__hw_addr_init(&local->mc_list);
mutex_init(&local->iflist_mtx);
mutex_init(&local->scan_mtx);
@ -437,7 +442,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
struct ieee80211_local *local = hw_to_local(hw);
int result;
enum ieee80211_band band;
int channels, i, j, max_bitrates;
int channels, max_bitrates;
bool supp_ht;
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
@ -567,6 +572,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->hw.conf.listen_interval = local->hw.max_listen_interval;
local->hw.conf.dynamic_ps_forced_timeout = -1;
result = sta_info_start(local);
if (result < 0)
goto fail_sta_info;
@ -601,21 +608,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
ieee80211_led_init(local);
/* alloc internal scan request */
i = 0;
local->int_scan_req->ssids = &local->scan_ssid;
local->int_scan_req->n_ssids = 1;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!hw->wiphy->bands[band])
continue;
for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) {
local->int_scan_req->channels[i] =
&hw->wiphy->bands[band]->channels[j];
i++;
}
}
local->int_scan_req->n_channels = i;
local->network_latency_notifier.notifier_call =
ieee80211_max_network_latency;
result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,

View file

@ -287,8 +287,6 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos++ |= sdata->u.mesh.accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return;
}
u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl)
@ -601,10 +599,10 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
switch (mgmt->u.action.category) {
case MESH_PLINK_CATEGORY:
case WLAN_CATEGORY_MESH_PLINK:
mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
break;
case MESH_PATH_SEL_CATEGORY:
case WLAN_CATEGORY_MESH_PATH_SEL:
mesh_rx_path_sel_frame(sdata, mgmt, len);
break;
}

View file

@ -209,8 +209,6 @@ struct mesh_rmc {
#define MESH_MAX_MPATHS 1024
/* Pending ANA approval */
#define MESH_PLINK_CATEGORY 30
#define MESH_PATH_SEL_CATEGORY 32
#define MESH_PATH_SEL_ACTION 0
/* PERR reason codes */

View file

@ -132,7 +132,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
/* BSSID == SA */
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL;
mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
switch (action) {
@ -225,7 +225,7 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
memcpy(mgmt->da, ra, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL;
mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
ie_len = 15;
pos = skb_put(skb, 2 + ie_len);
@ -624,7 +624,6 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
fail:
rcu_read_unlock();
sdata->u.mesh.mshstats.dropped_frames_no_route++;
return;
}
static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,

View file

@ -172,7 +172,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PLINK_CATEGORY;
mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK;
mgmt->u.action.u.plink_action.action_code = action;
if (action == PLINK_CLOSE)

View file

@ -47,6 +47,13 @@
*/
#define IEEE80211_PROBE_WAIT (HZ / 2)
/*
* Weight given to the latest Beacon frame when calculating average signal
* strength for Beacon frames received in the current BSS. This must be
* between 1 and 15.
*/
#define IEEE80211_SIGNAL_AVE_WEIGHT 3
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
@ -130,11 +137,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
bool enable_ht = true, ht_changed;
bool enable_ht = true;
enum nl80211_channel_type prev_chantype;
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
prev_chantype = sdata->vif.bss_conf.channel_type;
/* HT is not supported */
if (!sband->ht_cap.ht_supported)
enable_ht = false;
@ -165,38 +175,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
}
}
ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
channel_type != local->hw.conf.channel_type;
if (local->tmp_channel)
local->tmp_channel_type = channel_type;
local->oper_channel_type = channel_type;
if (ht_changed) {
/* channel_type change automatically detected */
ieee80211_hw_config(local, 0);
if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
/* can only fail due to HT40+/- mismatch */
channel_type = NL80211_CHAN_HT20;
WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
}
/* channel_type change automatically detected */
ieee80211_hw_config(local, 0);
if (prev_chantype != channel_type) {
rcu_read_lock();
sta = sta_info_get(sdata, bssid);
if (sta)
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_HT_CHANGED,
local->oper_channel_type);
channel_type);
rcu_read_unlock();
}
/* disable HT */
if (!enable_ht)
return 0;
}
ht_opmode = le16_to_cpu(hti->operation_mode);
/* if bss configuration changed store the new one */
if (!sdata->ht_opmode_valid ||
sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
if (sdata->ht_opmode_valid != enable_ht ||
sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
prev_chantype != channel_type) {
changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
sdata->ht_opmode_valid = true;
sdata->ht_opmode_valid = enable_ht;
}
return changed;
@ -206,7 +215,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason,
void *cookie)
void *cookie, bool send_frame)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@ -243,7 +252,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
ieee80211_tx_skb(sdata, skb);
if (send_frame)
ieee80211_tx_skb(sdata, skb);
else
kfree_skb(skb);
}
void ieee80211_send_pspoll(struct ieee80211_local *local,
@ -329,7 +342,11 @@ static void ieee80211_chswitch_work(struct work_struct *work)
goto out;
sdata->local->oper_channel = sdata->local->csa_channel;
ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(sdata->local,
IEEE80211_CONF_CHANGE_CHANNEL);
}
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->local->oper_channel;
@ -341,6 +358,29 @@ static void ieee80211_chswitch_work(struct work_struct *work)
mutex_unlock(&ifmgd->mtx);
}
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_managed *ifmgd;
sdata = vif_to_sdata(vif);
ifmgd = &sdata->u.mgd;
trace_api_chswitch_done(sdata, success);
if (!success) {
/*
* If the channel switch was not successful, stay
* around on the old channel. We currently lack
* good handling of this situation, possibly we
* should just drop the association.
*/
sdata->local->csa_channel = sdata->local->oper_channel;
}
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
EXPORT_SYMBOL(ieee80211_chswitch_done);
static void ieee80211_chswitch_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
@ -357,7 +397,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss)
struct ieee80211_bss *bss,
u64 timestamp)
{
struct cfg80211_bss *cbss =
container_of((void *)bss, struct cfg80211_bss, priv);
@ -385,10 +426,29 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata->local->csa_channel = new_ch;
if (sdata->local->ops->channel_switch) {
/* use driver's channel switch callback */
struct ieee80211_channel_switch ch_switch;
memset(&ch_switch, 0, sizeof(ch_switch));
ch_switch.timestamp = timestamp;
if (sw_elem->mode) {
ch_switch.block_tx = true;
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
}
ch_switch.channel = new_ch;
ch_switch.count = sw_elem->count;
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
drv_channel_switch(sdata->local, &ch_switch);
return;
}
/* channel switch handled in software */
if (sw_elem->count <= 1) {
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
} else {
ieee80211_stop_queues_by_reason(&sdata->local->hw,
if (sw_elem->mode)
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
mod_timer(&ifmgd->chswitch_timer,
@ -467,6 +527,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
{
struct ieee80211_sub_if_data *sdata, *found = NULL;
int count = 0;
int timeout;
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
local->ps_sdata = NULL;
@ -500,6 +561,26 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
beaconint_us = ieee80211_tu_to_usec(
found->vif.bss_conf.beacon_int);
timeout = local->hw.conf.dynamic_ps_forced_timeout;
if (timeout < 0) {
/*
* The 2 second value is there for compatibility until
* the PM_QOS_NETWORK_LATENCY is configured with real
* values.
*/
if (latency == 2000000000)
timeout = 100;
else if (latency <= 50000)
timeout = 300;
else if (latency <= 100000)
timeout = 100;
else if (latency <= 500000)
timeout = 50;
else
timeout = 0;
}
local->hw.conf.dynamic_ps_timeout = timeout;
if (beaconint_us > latency) {
local->ps_sdata = NULL;
} else {
@ -592,6 +673,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
int count;
u8 *pos, uapsd_queues = 0;
if (!local->ops->conf_tx)
return;
if (local->hw.queues < 4)
return;
@ -666,11 +750,15 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
params.aifs, params.cw_min, params.cw_max, params.txop,
params.uapsd);
#endif
if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
if (drv_conf_tx(local, queue, &params))
printk(KERN_DEBUG "%s: failed to set TX queue "
"parameters for queue %d\n",
wiphy_name(local->hw.wiphy), queue);
}
/* enable WMM or activate new settings */
local->hw.conf.flags |= IEEE80211_CONF_QOS;
drv_config(local, IEEE80211_CONF_CHANGE_QOS);
}
static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
@ -731,6 +819,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.associated = cbss;
memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
/* just to be sure */
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
@ -756,6 +846,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
/* And the BSSID changed - we're associated now */
bss_info_changed |= BSS_CHANGED_BSSID;
/* Tell the driver to monitor connection quality (if supported) */
if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
sdata->vif.bss_conf.cqm_rssi_thold)
bss_info_changed |= BSS_CHANGED_CQM;
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
mutex_lock(&local->iflist_mtx);
@ -767,7 +862,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
netif_carrier_on(sdata->dev);
}
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
bool remove_sta)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@ -819,7 +915,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
ieee80211_set_wmm_default(sdata);
/* channel(_type) changes are handled by ieee80211_hw_config */
local->oper_channel_type = NL80211_CHAN_NO_HT;
WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
/* on the next assoc, re-program HT parameters */
sdata->ht_opmode_valid = false;
@ -836,11 +932,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
ieee80211_hw_config(local, config_changed);
/* And the BSSID changed -- not very interesting here */
changed |= BSS_CHANGED_BSSID;
/* The BSSID (not really interesting) and HT changed */
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
sta_info_destroy_addr(sdata, bssid);
if (remove_sta)
sta_info_destroy_addr(sdata, bssid);
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@ -857,6 +954,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(hdr->addr1))
return;
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
return;
mod_timer(&sdata->u.mgd.conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
}
@ -934,23 +1034,72 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);
}
void ieee80211_beacon_loss_work(struct work_struct *work)
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
u8 bssid[ETH_ALEN];
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated) {
mutex_unlock(&ifmgd->mtx);
return;
}
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(local);
mutex_unlock(&ifmgd->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
NULL, true);
}
void ieee80211_beacon_connection_loss_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data,
u.mgd.beacon_loss_work);
u.mgd.beacon_connection_loss_work);
ieee80211_mgd_probe_ap(sdata, true);
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
__ieee80211_connection_loss(sdata);
else
ieee80211_mgd_probe_ap(sdata, true);
}
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_hw *hw = &sdata->local->hw;
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
trace_api_beacon_loss(sdata);
WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_beacon_loss);
void ieee80211_connection_loss(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_hw *hw = &sdata->local->hw;
trace_api_connection_loss(sdata);
WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_connection_loss);
static enum rx_mgmt_action __must_check
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len)
@ -971,7 +1120,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
sdata->name, bssid, reason_code);
ieee80211_set_disassoc(sdata);
ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(sdata->local);
return RX_MGMT_CFG80211_DEAUTH;
@ -1001,7 +1150,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
sdata->name, mgmt->sa, reason_code);
ieee80211_set_disassoc(sdata);
ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(sdata->local);
return RX_MGMT_CFG80211_DISASSOC;
}
@ -1215,7 +1364,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ETH_ALEN) == 0)) {
struct ieee80211_channel_sw_ie *sw_elem =
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
ieee80211_sta_process_chanswitch(sdata, sw_elem,
bss, rx_status->mactime);
}
}
@ -1254,12 +1404,17 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
mutex_lock(&sdata->local->iflist_mtx);
ieee80211_recalc_ps(sdata->local, -1);
mutex_unlock(&sdata->local->iflist_mtx);
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
return;
/*
* We've received a probe response, but are not sure whether
* we have or will be receiving any beacons or data, so let's
* schedule the timers again, just in case.
*/
mod_beacon_timer(sdata);
mod_timer(&ifmgd->conn_mon_timer,
round_jiffies_up(jiffies +
IEEE80211_CONNECTION_IDLE_TIME));
@ -1293,6 +1448,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
@ -1328,6 +1484,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
/* Track average RSSI from the Beacon frames of the current AP */
ifmgd->last_beacon_signal = rx_status->signal;
if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
ifmgd->ave_beacon_signal = rx_status->signal;
ifmgd->last_cqm_event_signal = 0;
} else {
ifmgd->ave_beacon_signal =
(IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
(16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
ifmgd->ave_beacon_signal) / 16;
}
if (bss_conf->cqm_rssi_thold &&
!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
int sig = ifmgd->ave_beacon_signal / 16;
int last_event = ifmgd->last_cqm_event_signal;
int thold = bss_conf->cqm_rssi_thold;
int hyst = bss_conf->cqm_rssi_hyst;
if (sig < thold &&
(last_event == 0 || sig < last_event - hyst)) {
ifmgd->last_cqm_event_signal = sig;
ieee80211_cqm_rssi_notify(
&sdata->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
GFP_KERNEL);
} else if (sig > thold &&
(last_event == 0 || sig > last_event + hyst)) {
ifmgd->last_cqm_event_signal = sig;
ieee80211_cqm_rssi_notify(
&sdata->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
GFP_KERNEL);
}
}
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
@ -1506,7 +1697,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_process_chanswitch(sdata,
&mgmt->u.action.u.chan_switch.sw_elem,
(void *)ifmgd->associated->priv);
(void *)ifmgd->associated->priv,
rx_status->mactime);
break;
}
mutex_unlock(&ifmgd->mtx);
@ -1613,7 +1805,7 @@ static void ieee80211_sta_work(struct work_struct *work)
printk(KERN_DEBUG "No probe response from AP %pM"
" after %dms, disconnecting.\n",
bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
ieee80211_set_disassoc(sdata);
ieee80211_set_disassoc(sdata, true);
ieee80211_recalc_idle(local);
mutex_unlock(&ifmgd->mtx);
/*
@ -1623,7 +1815,7 @@ static void ieee80211_sta_work(struct work_struct *work)
ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
NULL);
NULL, true);
mutex_lock(&ifmgd->mtx);
}
}
@ -1640,7 +1832,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
if (local->quiescing)
return;
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.beacon_connection_loss_work);
}
static void ieee80211_sta_conn_mon_timer(unsigned long data)
@ -1692,7 +1885,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
*/
cancel_work_sync(&ifmgd->work);
cancel_work_sync(&ifmgd->beacon_loss_work);
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
@ -1726,7 +1919,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->work, ieee80211_sta_work);
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
INIT_WORK(&ifmgd->beacon_connection_loss_work,
ieee80211_beacon_connection_loss_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@ -1805,6 +1999,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_work *wk;
u16 auth_alg;
if (req->local_state_change)
return 0; /* no need to update mac80211 state */
switch (req->auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
auth_alg = WLAN_AUTH_OPEN;
@ -1913,7 +2110,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
/* Trying to reassociate - clear previous association state */
ieee80211_set_disassoc(sdata);
ieee80211_set_disassoc(sdata, true);
}
mutex_unlock(&ifmgd->mtx);
@ -2017,7 +2214,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
if (ifmgd->associated == req->bss) {
bssid = req->bss->bssid;
ieee80211_set_disassoc(sdata);
ieee80211_set_disassoc(sdata, true);
mutex_unlock(&ifmgd->mtx);
} else {
bool not_auth_yet = false;
@ -2061,9 +2258,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
sdata->name, bssid, req->reason_code);
ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH, req->reason_code,
cookie);
ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH,
req->reason_code, cookie,
!req->local_state_change);
ieee80211_recalc_idle(sdata->local);
@ -2075,6 +2272,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
void *cookie)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 bssid[ETH_ALEN];
mutex_lock(&ifmgd->mtx);
@ -2092,13 +2290,15 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
sdata->name, req->bss->bssid, req->reason_code);
ieee80211_set_disassoc(sdata);
memcpy(bssid, req->bss->bssid, ETH_ALEN);
ieee80211_set_disassoc(sdata, false);
mutex_unlock(&ifmgd->mtx);
ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
IEEE80211_STYPE_DISASSOC, req->reason_code,
cookie);
cookie, !req->local_state_change);
sta_info_destroy_addr(sdata, bssid);
ieee80211_recalc_idle(sdata->local);
@ -2118,7 +2318,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
if ((chan != local->tmp_channel ||
channel_type != local->tmp_channel_type) &&
(chan != local->oper_channel ||
channel_type != local->oper_channel_type))
channel_type != local->_oper_channel_type))
return -EBUSY;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
@ -2139,3 +2339,15 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
*cookie = (unsigned long) skb;
return 0;
}
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
enum nl80211_cqm_rssi_threshold_event rssi_event,
gfp_t gfp)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
trace_api_cqm_rssi_notify(sdata, rssi_event);
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);

View file

@ -46,7 +46,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
list_for_each_entry_rcu(sta, &local->sta_list, list) {
set_sta_flags(sta, WLAN_STA_SUSPEND);
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta);
}
}

View file

@ -542,7 +542,7 @@ minstrel_free(void *priv)
kfree(priv);
}
static struct rate_control_ops mac80211_minstrel = {
struct rate_control_ops mac80211_minstrel = {
.name = "minstrel",
.tx_status = minstrel_tx_status,
.get_rate = minstrel_get_rate,

View file

@ -80,7 +80,18 @@ struct minstrel_priv {
unsigned int lookaround_rate_mrr;
};
struct minstrel_debugfs_info {
size_t len;
char buf[];
};
extern struct rate_control_ops mac80211_minstrel;
void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
/* debugfs */
int minstrel_stats_open(struct inode *inode, struct file *file);
ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos);
int minstrel_stats_release(struct inode *inode, struct file *file);
#endif

View file

@ -53,21 +53,15 @@
#include <net/mac80211.h>
#include "rc80211_minstrel.h"
struct minstrel_stats_info {
struct minstrel_sta_info *mi;
char buf[4096];
size_t len;
};
static int
int
minstrel_stats_open(struct inode *inode, struct file *file)
{
struct minstrel_sta_info *mi = inode->i_private;
struct minstrel_stats_info *ms;
struct minstrel_debugfs_info *ms;
unsigned int i, tp, prob, eprob;
char *p;
ms = kmalloc(sizeof(*ms), GFP_KERNEL);
ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL);
if (!ms)
return -ENOMEM;
@ -107,36 +101,19 @@ minstrel_stats_open(struct inode *inode, struct file *file)
return 0;
}
static ssize_t
minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *o)
ssize_t
minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
{
struct minstrel_stats_info *ms;
char *src;
struct minstrel_debugfs_info *ms;
ms = file->private_data;
src = ms->buf;
len = min(len, ms->len);
if (len <= *o)
return 0;
src += *o;
len -= *o;
*o += len;
if (copy_to_user(buf, src, len))
return -EFAULT;
return len;
return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
}
static int
int
minstrel_stats_release(struct inode *inode, struct file *file)
{
struct minstrel_stats_info *ms = file->private_data;
kfree(ms);
kfree(file->private_data);
return 0;
}

View file

@ -39,7 +39,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
{
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
skb_trim(skb, skb->len - FCS_LEN);
__pskb_trim(skb, skb->len - FCS_LEN);
else {
/* driver bug */
WARN_ON(1);
@ -81,8 +81,6 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local,
len += 8;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1;
if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
len += 1;
if (len & 1) /* padding for RX_FLAGS if necessary */
len++;
@ -179,14 +177,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
}
/* IEEE80211_RADIOTAP_DBM_ANTNOISE */
if (local->hw.flags & IEEE80211_HW_NOISE_DBM) {
*pos = status->noise;
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
pos++;
}
/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
/* IEEE80211_RADIOTAP_ANTENNA */
@ -236,6 +226,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
/* make sure hdr->frame_control is on the linear part */
if (!pskb_may_pull(origskb, 2)) {
dev_kfree_skb(origskb);
return NULL;
}
if (!local->monitors) {
if (should_drop_frame(origskb, present_fcs_len)) {
dev_kfree_skb(origskb);
@ -493,7 +489,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_action(hdr->frame_control)) {
mgmt = (struct ieee80211_mgmt *)hdr;
if (mgmt->u.action.category != MESH_PLINK_CATEGORY)
if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@ -723,14 +719,16 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
goto dont_reorder;
spin_lock(&sta->lock);
if (!sta->ampdu_mlme.tid_active_rx[tid])
goto dont_reorder_unlock;
tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
/* qos null data frames are excluded */
if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
goto dont_reorder;
goto dont_reorder_unlock;
/* new, potentially un-ordered, ampdu frame - process it */
@ -742,15 +740,20 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
/* 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->sdata, sta->sta.addr,
tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
spin_unlock(&sta->lock);
__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_REQUIRE_SETUP);
dev_kfree_skb(skb);
return;
}
if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) {
spin_unlock(&sta->lock);
return;
}
dont_reorder_unlock:
spin_unlock(&sta->lock);
dont_reorder:
__skb_queue_tail(frames, skb);
}
@ -897,6 +900,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
rx->key = key;
return RX_CONTINUE;
} else {
u8 keyid;
/*
* The device doesn't give us the IV so we won't be
* able to look up the key. That's ok though, we
@ -919,7 +923,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* no need to call ieee80211_wep_get_keyidx,
* it verifies a bunch of things we've done already
*/
keyidx = rx->skb->data[hdrlen + 3] >> 6;
skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
keyidx = keyid >> 6;
rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
@ -940,6 +945,11 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
}
if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE;
hdr = (struct ieee80211_hdr *)rx->skb->data;
/* Check for weak IVs if possible */
if (rx->sta && rx->key->conf.alg == ALG_WEP &&
ieee80211_is_data(hdr->frame_control) &&
@ -1078,7 +1088,6 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
sta->last_signal = status->signal;
sta->last_noise = status->noise;
/*
* Change STA power saving mode only at the end of a frame
@ -1241,6 +1250,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
}
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE;
/*
* skb_linearize() might change the skb->data and
* previously cached variables (in this case, hdr) need to
* be refreshed with the new data.
*/
hdr = (struct ieee80211_hdr *)rx->skb->data;
seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
if (frag == 0) {
@ -1406,21 +1424,24 @@ static int
ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
__le16 fc = hdr->frame_control;
int res;
res = ieee80211_drop_unencrypted(rx, fc);
if (unlikely(res))
return res;
/*
* Pass through unencrypted frames if the hardware has
* decrypted them already.
*/
if (status->flag & RX_FLAG_DECRYPTED)
return 0;
if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) {
if (unlikely(ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
if (unlikely(!ieee80211_has_protected(fc) &&
ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
rx->key))
return -EACCES;
/* BIP does not use Protected field, so need to check MMIE */
if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
ieee80211_get_mmie_keyidx(rx->skb) < 0 &&
rx->key))
ieee80211_get_mmie_keyidx(rx->skb) < 0))
return -EACCES;
/*
* When using MFP, Action frames are not allowed prior to
@ -1598,6 +1619,9 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
skb->dev = dev;
__skb_queue_head_init(&frame_list);
if (skb_linearize(skb))
return RX_DROP_UNUSABLE;
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
rx->local->hw.extra_tx_headroom);
@ -1796,10 +1820,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
if (ieee80211_is_back_req(bar->frame_control)) {
if (!rx->sta)
return RX_DROP_MONITOR;
spin_lock(&rx->sta->lock);
tid = le16_to_cpu(bar->control) >> 12;
if (rx->sta->ampdu_mlme.tid_state_rx[tid]
!= HT_AGG_STATE_OPERATIONAL)
if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) {
spin_unlock(&rx->sta->lock);
return RX_DROP_MONITOR;
}
tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
@ -1813,6 +1839,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
frames);
kfree_skb(skb);
spin_unlock(&rx->sta->lock);
return RX_QUEUED;
}
@ -1974,8 +2001,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
goto handled;
}
break;
case MESH_PLINK_CATEGORY:
case MESH_PATH_SEL_CATEGORY:
case WLAN_CATEGORY_MESH_PLINK:
case WLAN_CATEGORY_MESH_PATH_SEL:
if (ieee80211_vif_is_mesh(&sdata->vif))
return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
break;
@ -2372,29 +2399,42 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
__le16 fc;
struct ieee80211_rx_data rx;
int prepares;
struct ieee80211_sub_if_data *prev = NULL;
struct sk_buff *skb_new;
struct sta_info *sta, *tmp;
bool found_sta = false;
int err = 0;
hdr = (struct ieee80211_hdr *)skb->data;
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.local = local;
if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_mgmt(hdr->frame_control))
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
local->dot11ReceivedFragmentCount++;
if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
rx.flags |= IEEE80211_RX_IN_SCAN;
if (ieee80211_is_mgmt(fc))
err = skb_linearize(skb);
else
err = !pskb_may_pull(skb, ieee80211_hdrlen(fc));
if (err) {
dev_kfree_skb(skb);
return;
}
hdr = (struct ieee80211_hdr *)skb->data;
ieee80211_parse_qos(&rx);
ieee80211_verify_alignment(&rx);
if (ieee80211_is_data(hdr->frame_control)) {
if (ieee80211_is_data(fc)) {
for_each_sta_info(local, hdr->addr2, sta, tmp) {
rx.sta = sta;
found_sta = true;

View file

@ -14,6 +14,8 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/pm_qos_params.h>
#include <net/sch_generic.h>
#include <linux/slab.h>
#include <net/mac80211.h>
@ -83,7 +85,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
{
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
int clen;
int clen, srlen;
s32 signal = 0;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@ -112,23 +114,24 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->dtim_period = tim_ie->dtim_period;
}
bss->supp_rates_len = 0;
/* replace old supported rates if we get new values */
srlen = 0;
if (elems->supp_rates) {
clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
clen = IEEE80211_MAX_SUPP_RATES;
if (clen > elems->supp_rates_len)
clen = elems->supp_rates_len;
memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
clen);
bss->supp_rates_len += clen;
memcpy(bss->supp_rates, elems->supp_rates, clen);
srlen += clen;
}
if (elems->ext_supp_rates) {
clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
clen = IEEE80211_MAX_SUPP_RATES - srlen;
if (clen > elems->ext_supp_rates_len)
clen = elems->ext_supp_rates_len;
memcpy(&bss->supp_rates[bss->supp_rates_len],
elems->ext_supp_rates, clen);
bss->supp_rates_len += clen;
memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen);
srlen += clen;
}
if (srlen)
bss->supp_rates_len = srlen;
bss->wmm_used = elems->wmm_param || elems->wmm_info;
bss->uapsd_supported = is_uapsd_supported(elems);
@ -246,6 +249,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
struct ieee80211_local *local = hw_to_local(hw);
bool was_hw_scan;
trace_api_scan_completed(local, aborted);
mutex_lock(&local->scan_mtx);
/*
@ -322,6 +327,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_offchannel_stop_beaconing(local);
local->leave_oper_channel_time = 0;
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
@ -406,7 +412,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
rc = drv_hw_scan(local, local->hw_scan_req);
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
} else
rc = ieee80211_start_sw_scan(local);
@ -426,11 +432,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
return rc;
}
static unsigned long
ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
{
/*
* TODO: channel switching also consumes quite some time,
* add that delay as well to get a better estimation
*/
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
return IEEE80211_PASSIVE_CHANNEL_TIME;
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
static int ieee80211_scan_state_decision(struct ieee80211_local *local,
unsigned long *next_delay)
{
bool associated = false;
bool tx_empty = true;
bool bad_latency;
bool listen_int_exceeded;
unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
/* if no more bands/channels left, complete scan and advance to the idle state */
if (local->scan_channel_idx >= local->scan_req->n_channels) {
@ -438,7 +461,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
return 1;
}
/* check if at least one STA interface is associated */
/*
* check if at least one STA interface is associated,
* check if at least one STA interface has pending tx frames
* and grab the lowest used beacon interval
*/
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
@ -447,7 +474,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated) {
associated = true;
break;
if (sdata->vif.bss_conf.beacon_int <
min_beacon_int || min_beacon_int == 0)
min_beacon_int =
sdata->vif.bss_conf.beacon_int;
if (!qdisc_all_tx_empty(sdata->dev)) {
tx_empty = false;
break;
}
}
}
}
@ -456,11 +492,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
if (local->scan_channel) {
/*
* we're currently scanning a different channel, let's
* switch back to the operating channel now if at least
* one interface is associated. Otherwise just scan the
* next channel
* see if we can scan another channel without interfering
* with the current traffic situation.
*
* Since we don't know if the AP has pending frames for us
* we can only check for our tx queues and use the current
* pm_qos requirements for rx. Hence, if no tx traffic occurs
* at all we will scan as many channels in a row as the pm_qos
* latency allows us to. Additionally we also check for the
* currently negotiated listen interval to prevent losing
* frames unnecessarily.
*
* Otherwise switch back to the operating channel.
*/
if (associated)
next_chan = local->scan_req->channels[local->scan_channel_idx];
bad_latency = time_after(jiffies +
ieee80211_scan_get_channel_time(next_chan),
local->leave_oper_channel_time +
usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
listen_int_exceeded = time_after(jiffies +
ieee80211_scan_get_channel_time(next_chan),
local->leave_oper_channel_time +
usecs_to_jiffies(min_beacon_int * 1024) *
local->hw.conf.listen_interval);
if (associated && ( !tx_empty || bad_latency ||
listen_int_exceeded))
local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
else
local->next_scan_state = SCAN_SET_CHANNEL;
@ -492,6 +551,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca
else
*next_delay = HZ / 10;
/* remember when we left the operating channel */
local->leave_oper_channel_time = jiffies;
/* advance to the next channel to be scanned */
local->next_scan_state = SCAN_SET_CHANNEL;
}
@ -594,7 +656,7 @@ void ieee80211_scan_work(struct work_struct *work)
}
if (local->hw_scan_req) {
int rc = drv_hw_scan(local, local->hw_scan_req);
int rc = drv_hw_scan(local, sdata, local->hw_scan_req);
mutex_unlock(&local->scan_mtx);
if (rc)
ieee80211_scan_completed(&local->hw, true);
@ -667,10 +729,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len)
const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan)
{
struct ieee80211_local *local = sdata->local;
int ret = -EBUSY;
enum nl80211_band band;
mutex_lock(&local->scan_mtx);
@ -678,6 +742,30 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
if (local->scan_req)
goto unlock;
/* fill internal scan request */
if (!chan) {
int i, nchan = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band])
continue;
for (i = 0;
i < local->hw.wiphy->bands[band]->n_channels;
i++) {
local->int_scan_req->channels[nchan] =
&local->hw.wiphy->bands[band]->channels[i];
nchan++;
}
}
local->int_scan_req->n_channels = nchan;
} else {
local->int_scan_req->channels[0] = chan;
local->int_scan_req->n_channels = 1;
}
local->int_scan_req->ssids = &local->scan_ssid;
local->int_scan_req->n_ssids = 1;
memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
local->int_scan_req->ssids[0].ssid_len = ssid_len;

View file

@ -250,9 +250,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
* enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i;
/* rx */
sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_rx[i] = NULL;
/* tx */
sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_tx[i] = NULL;
@ -578,7 +575,7 @@ static int sta_info_buffer_expired(struct sta_info *sta,
}
static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
struct sta_info *sta)
{
unsigned long flags;
@ -586,7 +583,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
if (skb_queue_empty(&sta->ps_tx_buf))
return;
return false;
for (;;) {
spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
@ -611,6 +608,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
if (skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
}
return true;
}
static int __must_check __sta_info_destroy(struct sta_info *sta)
@ -619,7 +618,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
unsigned long flags;
int ret, i;
int ret;
might_sleep();
@ -629,6 +628,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
local = sta->local;
sdata = sta->sdata;
/*
* Before removing the station from the driver and
* rate control, it might still start new aggregation
* sessions -- block that to make sure the tear-down
* will be sufficient.
*/
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta);
spin_lock_irqsave(&local->sta_lock, flags);
ret = sta_info_hash_del(local, sta);
/* this might still be the pending list ... which is fine */
@ -645,9 +653,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
* may mean it is removed from hardware which requires that
* the key->sta pointer is still valid, so flush the key todo
* list here.
*
* ieee80211_key_todo() will synchronize_rcu() so after this
* nothing can reference this sta struct any more.
*/
ieee80211_key_todo();
@ -679,11 +684,17 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
sdata = sta->sdata;
}
/*
* At this point, after we wait for an RCU grace period,
* neither mac80211 nor the driver can reference this
* sta struct any more except by still existing timers
* associated with this station that we clean up below.
*/
synchronize_rcu();
#ifdef CONFIG_MAC80211_MESH
if (ieee80211_vif_is_mesh(&sdata->vif)) {
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
del_timer(&sta->plink_timer);
}
#endif
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@ -710,50 +721,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
dev_kfree_skb_any(skb);
for (i = 0; i < STA_TID_NUM; i++) {
struct tid_ampdu_rx *tid_rx;
struct tid_ampdu_tx *tid_tx;
spin_lock_bh(&sta->lock);
tid_rx = sta->ampdu_mlme.tid_rx[i];
/* Make sure timer won't free the tid_rx struct, see below */
if (tid_rx)
tid_rx->shutdown = true;
spin_unlock_bh(&sta->lock);
/*
* Outside spinlock - shutdown is true now so that the timer
* won't free tid_rx, we have to do that now. Can't let the
* timer do it because we have to sync the timer outside the
* lock that it takes itself.
*/
if (tid_rx) {
del_timer_sync(&tid_rx->session_timer);
kfree(tid_rx);
}
/*
* No need to do such complications for TX agg sessions, the
* path leading to freeing the tid_tx struct goes via a call
* from the driver, and thus needs to look up the sta struct
* again, which cannot be found when we get here. Hence, we
* just need to delete the timer and free the aggregation
* info; we won't be telling the peer about it then but that
* doesn't matter if we're not talking to it again anyway.
*/
tid_tx = sta->ampdu_mlme.tid_tx[i];
if (tid_tx) {
del_timer_sync(&tid_tx->addba_resp_timer);
/*
* STA removed while aggregation session being
* started? Bit odd, but purge frames anyway.
*/
skb_queue_purge(&tid_tx->pending);
kfree(tid_tx);
}
}
__sta_info_free(local, sta);
return 0;
@ -790,15 +757,20 @@ static void sta_info_cleanup(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta;
bool timer_needed = false;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list)
sta_info_cleanup_expire_buffered(local, sta);
if (sta_info_cleanup_expire_buffered(local, sta))
timer_needed = true;
rcu_read_unlock();
if (local->quiescing)
return;
if (!timer_needed)
return;
local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
add_timer(&local->sta_cleanup);
@ -883,8 +855,12 @@ struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
struct sta_info *sta, *nxt;
/* Just return a random station ... first in list ... */
for_each_sta_info(hw_to_local(hw), addr, sta, nxt)
for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
if (!sta->uploaded)
return NULL;
return &sta->sta;
}
return NULL;
}
EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
@ -892,14 +868,19 @@ EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
const u8 *addr)
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
if (!vif)
return NULL;
sdata = vif_to_sdata(vif);
sta = sta_info_get_bss(vif_to_sdata(vif), addr);
if (!sta)
return NULL;
return ieee80211_find_sta_by_hw(&sdata->local->hw, addr);
if (!sta->uploaded)
return NULL;
return &sta->sta;
}
EXPORT_SYMBOL(ieee80211_find_sta);
@ -992,6 +973,8 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
trace_api_sta_block_awake(sta->local, pubsta, block);
if (block)
set_sta_flags(sta, WLAN_STA_PS_DRIVER);
else

View file

@ -35,8 +35,8 @@
* IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next
* frame to this station is transmitted.
* @WLAN_STA_MFP: Management frame protection is used with this STA.
* @WLAN_STA_SUSPEND: Set/cleared during a suspend/resume cycle.
* Used to deny ADDBA requests (both TX and RX).
* @WLAN_STA_BLOCK_BA: Used to deny ADDBA requests (both TX and RX)
* during suspend/resume and station removal.
* @WLAN_STA_PS_DRIVER: driver requires keeping this station in
* power-save mode logically to flush frames that might still
* be in the queues
@ -57,7 +57,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_WDS = 1<<7,
WLAN_STA_CLEAR_PS_FILT = 1<<9,
WLAN_STA_MFP = 1<<10,
WLAN_STA_SUSPEND = 1<<11,
WLAN_STA_BLOCK_BA = 1<<11,
WLAN_STA_PS_DRIVER = 1<<12,
WLAN_STA_PSPOLL = 1<<13,
WLAN_STA_DISASSOC = 1<<14,
@ -106,7 +106,6 @@ struct tid_ampdu_tx {
* @buf_size: buffer size for incoming A-MPDUs
* @timeout: reset timer value (in TUs).
* @dialog_token: dialog token for aggregation session
* @shutdown: this session is being shut down due to STA removal
*/
struct tid_ampdu_rx {
struct sk_buff **reorder_buf;
@ -118,7 +117,6 @@ struct tid_ampdu_rx {
u16 buf_size;
u16 timeout;
u8 dialog_token;
bool shutdown;
};
/**
@ -156,7 +154,7 @@ enum plink_state {
*/
struct sta_ampdu_mlme {
/* rx */
u8 tid_state_rx[STA_TID_NUM];
bool tid_active_rx[STA_TID_NUM];
struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
/* tx */
u8 tid_state_tx[STA_TID_NUM];
@ -200,7 +198,6 @@ struct sta_ampdu_mlme {
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
* @last_signal: signal of last received frame from this STA
* @last_noise: noise of last received frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry
@ -267,7 +264,6 @@ struct sta_info {
unsigned long rx_fragments;
unsigned long rx_dropped;
int last_signal;
int last_noise;
__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
/* Updated from TX status path only, no locking requirements */

View file

@ -171,13 +171,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct net_device *prev_dev = NULL;
struct sta_info *sta, *tmp;
int retry_count = -1, i;
bool injected;
int rates_idx = -1;
bool send_to_cooked;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
/* the HW cannot have attempted that rate */
if (i >= hw->max_rates) {
info->status.rates[i].idx = -1;
info->status.rates[i].count = 0;
} else if (info->status.rates[i].idx >= 0) {
rates_idx = i;
}
retry_count += info->status.rates[i].count;
@ -206,6 +209,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
(rates_idx != -1))
sta->last_tx_rate = info->status.rates[rates_idx];
if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
(ieee80211_is_data_qos(fc))) {
u16 tid, ssn;
@ -296,11 +303,15 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
/* Need to make a copy before skb->cb gets cleared */
send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) ||
(type != IEEE80211_FTYPE_DATA);
/*
* This is a bit racy but we can avoid a lot of work
* with this test...
*/
if (!local->monitors && !local->cooked_mntrs) {
if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
dev_kfree_skb(skb);
return;
}
@ -345,9 +356,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
/* for now report the total retry_count */
rthdr->data_retries = retry_count;
/* Need to make a copy before skb->cb gets cleared */
injected = !!(info->flags & IEEE80211_TX_CTL_INJECTED);
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@ -362,8 +370,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
continue;
if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
!injected &&
(type == IEEE80211_FTYPE_DATA))
!send_to_cooked)
continue;
if (prev_dev) {

View file

@ -429,6 +429,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct sta_info *sta = tx->sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
struct ieee80211_local *local = tx->local;
u32 staflags;
if (unlikely(!sta ||
@ -476,6 +477,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
info->control.vif = &tx->sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
skb_queue_tail(&sta->ps_tx_buf, tx->skb);
if (!timer_pending(&local->sta_cleanup))
mod_timer(&local->sta_cleanup,
round_jiffies(jiffies +
STA_INFO_CLEANUP_INTERVAL));
return TX_QUEUED;
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
@ -513,6 +520,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
ieee80211_is_robust_mgmt_frame(hdr) &&
(key = rcu_dereference(tx->sdata->default_mgmt_key)))
tx->key = key;
else if ((key = rcu_dereference(tx->sdata->default_key)))
@ -584,7 +593,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
struct ieee80211_hdr *hdr = (void *)tx->skb->data;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
int i, len;
int i;
u32 len;
bool inval = false, rts = false, short_preamble = false;
struct ieee80211_tx_rate_control txrc;
u32 sta_flags;
@ -593,7 +603,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
sband = tx->local->hw.wiphy->bands[tx->channel->band];
len = min_t(int, tx->skb->len + FCS_LEN,
len = min_t(u32, tx->skb->len + FCS_LEN,
tx->local->hw.wiphy->frag_threshold);
/* set up the tx rate control struct we give the RC algo */
@ -1142,13 +1152,12 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
unsigned long flags;
struct tid_ampdu_tx *tid_tx;
qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
spin_lock_irqsave(&tx->sta->lock, flags);
spin_lock(&tx->sta->lock);
/*
* XXX: This spinlock could be fairly expensive, but see the
* comment in agg-tx.c:ieee80211_agg_tx_operational().
@ -1173,7 +1182,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
__skb_queue_tail(&tid_tx->pending, skb);
}
spin_unlock_irqrestore(&tx->sta->lock, flags);
spin_unlock(&tx->sta->lock);
if (unlikely(queued))
return TX_QUEUED;
@ -2011,14 +2020,12 @@ void ieee80211_tx_pending(unsigned long data)
while (!skb_queue_empty(&local->pending[i])) {
struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sub_if_data *sdata;
if (WARN_ON(!info->control.vif)) {
kfree_skb(skb);
continue;
}
sdata = vif_to_sdata(info->control.vif);
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags);
@ -2244,8 +2251,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
info->control.vif = vif;
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_ASSIGN_SEQ |
IEEE80211_TX_CTL_FIRST_FRAGMENT;
out:
rcu_read_unlock();
return skb;

View file

@ -270,6 +270,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
trace_wake_queue(local, queue, reason);
if (WARN_ON(queue >= hw->queues))
return;
@ -312,6 +314,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
trace_stop_queue(local, queue, reason);
if (WARN_ON(queue >= hw->queues))
return;
@ -796,6 +800,11 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
drv_conf_tx(local, queue, &qparam);
}
/* after reinitialize QoS TX queues setting to default,
* disable QoS at all */
local->hw.conf.flags &= ~IEEE80211_CONF_QOS;
drv_config(local, IEEE80211_CONF_CHANGE_QOS);
}
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
@ -1135,7 +1144,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
list_for_each_entry_rcu(sta, &local->sta_list, list) {
clear_sta_flags(sta, WLAN_STA_SUSPEND);
clear_sta_flags(sta, WLAN_STA_BLOCK_BA);
}
}
@ -1151,18 +1160,33 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* Finally also reconfigure all the BSS information */
list_for_each_entry(sdata, &local->interfaces, list) {
u32 changed = ~0;
u32 changed;
if (!ieee80211_sdata_running(sdata))
continue;
/* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
BSS_CHANGED_ERP_SLOT |
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
/* disable beacon change bits */
changed &= ~(BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED);
/* fall through */
changed |= BSS_CHANGED_ASSOC;
ieee80211_bss_info_change_notify(sdata, changed);
break;
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
/* fall through */
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED;
ieee80211_bss_info_change_notify(sdata, changed);
break;
case NL80211_IFTYPE_WDS:

View file

@ -33,6 +33,7 @@
#define IEEE80211_MAX_PROBE_TRIES 5
enum work_action {
WORK_ACT_MISMATCH,
WORK_ACT_NONE,
WORK_ACT_TIMEOUT,
WORK_ACT_DONE,
@ -213,15 +214,25 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[wk->chan->band];
/*
* Get all rates supported by the device and the AP as
* some APs don't like getting a superset of their rates
* in the association request (e.g. D-Link DAP 1353 in
* b-only mode)...
*/
rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
wk->assoc.supp_rates_len,
sband, &rates);
if (wk->assoc.supp_rates_len) {
/*
* Get all rates supported by the device and the AP as
* some APs don't like getting a superset of their rates
* in the association request (e.g. D-Link DAP 1353 in
* b-only mode)...
*/
rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
wk->assoc.supp_rates_len,
sband, &rates);
} else {
/*
* In case AP not provide any supported rates information
* before association, we send information element(s) with
* all rates that we support.
*/
rates = ~0;
rates_len = sband->n_bitrates;
}
skb = alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + /* bit too much but doesn't matter */
@ -575,7 +586,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
u16 auth_alg, auth_transaction, status_code;
if (wk->type != IEEE80211_WORK_AUTH)
return WORK_ACT_NONE;
return WORK_ACT_MISMATCH;
if (len < 24 + 6)
return WORK_ACT_NONE;
@ -626,6 +637,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
struct ieee802_11_elems elems;
u8 *pos;
if (wk->type != IEEE80211_WORK_ASSOC)
return WORK_ACT_MISMATCH;
/*
* AssocResp and ReassocResp have identical structure, so process both
* of them in this function.
@ -681,6 +695,12 @@ ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
ASSERT_WORK_MTX(local);
if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
return WORK_ACT_MISMATCH;
if (len < 24 + 12)
return WORK_ACT_NONE;
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
if (baselen > len)
return WORK_ACT_NONE;
@ -695,7 +715,7 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
struct ieee80211_rx_status *rx_status;
struct ieee80211_mgmt *mgmt;
struct ieee80211_work *wk;
enum work_action rma = WORK_ACT_NONE;
enum work_action rma;
u16 fc;
rx_status = (struct ieee80211_rx_status *) skb->cb;
@ -742,7 +762,17 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
break;
default:
WARN_ON(1);
rma = WORK_ACT_NONE;
}
/*
* We've either received an unexpected frame, or we have
* multiple work items and need to match the frame to the
* right one.
*/
if (rma == WORK_ACT_MISMATCH)
continue;
/*
* We've processed this frame for that work, so it can't
* belong to another work struct.
@ -752,6 +782,9 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
}
switch (rma) {
case WORK_ACT_MISMATCH:
/* ignore this unmatched frame */
break;
case WORK_ACT_NONE:
break;
case WORK_ACT_DONE:
@ -920,11 +953,16 @@ static void ieee80211_work_work(struct work_struct *work)
run_again(local, jiffies + HZ/2);
}
if (list_empty(&local->work_list) && local->scan_req)
mutex_lock(&local->scan_mtx);
if (list_empty(&local->work_list) && local->scan_req &&
!local->scanning)
ieee80211_queue_delayed_work(&local->hw,
&local->scan_work,
round_jiffies_relative(0));
mutex_unlock(&local->scan_mtx);
mutex_unlock(&local->work_mtx);
ieee80211_recalc_idle(local);