Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
commit
9b4e9e7565
|
@ -523,7 +523,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1445,7 +1445,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
|
|||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
||||
if (priv->ah->sw_mgmt_crypto &&
|
||||
key->cipher == WLAN_CIPHER_SUITE_CCMP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1406,7 +1406,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
|
|||
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
||||
if (sc->sc_ah->sw_mgmt_crypto &&
|
||||
key->cipher == WLAN_CIPHER_SUITE_CCMP)
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
||||
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -2056,7 +2056,7 @@ failed:
|
|||
mac80211_hwsim_free();
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(init_mac80211_hwsim);
|
||||
|
||||
static void __exit exit_mac80211_hwsim(void)
|
||||
{
|
||||
|
@ -2067,7 +2067,4 @@ static void __exit exit_mac80211_hwsim(void)
|
|||
mac80211_hwsim_free();
|
||||
unregister_netdev(hwsim_mon);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_mac80211_hwsim);
|
||||
module_exit(exit_mac80211_hwsim);
|
||||
|
|
|
@ -1934,36 +1934,6 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
|
|||
return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_fhss_chan_to_freq - get channel frequency
|
||||
* @channel: the FHSS channel
|
||||
*
|
||||
* Convert IEEE802.11 FHSS channel to frequency (MHz)
|
||||
* Ref IEEE 802.11-2007 section 14.6
|
||||
*/
|
||||
static inline int ieee80211_fhss_chan_to_freq(int channel)
|
||||
{
|
||||
if ((channel > 1) && (channel < 96))
|
||||
return channel + 2400;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_freq_to_fhss_chan - get channel
|
||||
* @freq: the channels frequency
|
||||
*
|
||||
* Convert frequency (MHz) to IEEE802.11 FHSS channel
|
||||
* Ref IEEE 802.11-2007 section 14.6
|
||||
*/
|
||||
static inline int ieee80211_freq_to_fhss_chan(int freq)
|
||||
{
|
||||
if ((freq > 2401) && (freq < 2496))
|
||||
return freq - 2400;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_dsss_chan_to_freq - get channel center frequency
|
||||
* @channel: the DSSS channel
|
||||
|
@ -2000,56 +1970,6 @@ static inline int ieee80211_freq_to_dsss_chan(int freq)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back
|
||||
* Ref IEEE 802.11-2007 section 18.4.6.2
|
||||
*
|
||||
* The channels and frequencies are the same as those defined for DSSS
|
||||
*/
|
||||
#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan)
|
||||
#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq)
|
||||
|
||||
/* Convert IEEE802.11 ERP channel to frequency (MHz) and back
|
||||
* Ref IEEE 802.11-2007 section 19.4.2
|
||||
*/
|
||||
#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan)
|
||||
#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq)
|
||||
|
||||
/**
|
||||
* ieee80211_ofdm_chan_to_freq - get channel center frequency
|
||||
* @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
|
||||
* @channel: the OFDM channel
|
||||
*
|
||||
* Convert IEEE802.11 OFDM channel to center frequency (MHz)
|
||||
* Ref IEEE 802.11-2007 section 17.3.8.3.2
|
||||
*/
|
||||
static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel)
|
||||
{
|
||||
if ((channel > 0) && (channel <= 200) &&
|
||||
(s_freq >= 4000))
|
||||
return s_freq + (channel * 5);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_freq_to_ofdm_channel - get channel
|
||||
* @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
|
||||
* @freq: the frequency
|
||||
*
|
||||
* Convert frequency (MHz) to IEEE802.11 OFDM channel
|
||||
* Ref IEEE 802.11-2007 section 17.3.8.3.2
|
||||
*
|
||||
* This routine selects the channel with the closest center frequency.
|
||||
*/
|
||||
static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq)
|
||||
{
|
||||
if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) &&
|
||||
(s_freq >= 4000))
|
||||
return (freq + 2 - s_freq) / 5;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_tu_to_usec - convert time units (TU) to microseconds
|
||||
* @tu: the TUs
|
||||
|
|
|
@ -973,21 +973,29 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
|
|||
* generation in software.
|
||||
* @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
|
||||
* that the key is pairwise rather then a shared key.
|
||||
* @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a
|
||||
* @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
|
||||
* CCMP key if it requires CCMP encryption of management frames (MFP) to
|
||||
* be done in software.
|
||||
* @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
|
||||
* if space should be prepared for the IV, but the IV
|
||||
* itself should not be generated. Do not set together with
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_IV on the same key.
|
||||
* @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received
|
||||
* management frames. The flag can help drivers that have a hardware
|
||||
* crypto implementation that doesn't deal with management frames
|
||||
* properly by allowing them to not upload the keys to hardware and
|
||||
* fall back to software crypto. Note that this flag deals only with
|
||||
* RX, if your crypto engine can't deal with TX you can also set the
|
||||
* %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
|
||||
*/
|
||||
enum ieee80211_key_flags {
|
||||
IEEE80211_KEY_FLAG_WMM_STA = 1<<0,
|
||||
IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1,
|
||||
IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
|
||||
IEEE80211_KEY_FLAG_PAIRWISE = 1<<3,
|
||||
IEEE80211_KEY_FLAG_SW_MGMT = 1<<4,
|
||||
IEEE80211_KEY_FLAG_SW_MGMT_TX = 1<<4,
|
||||
IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5,
|
||||
IEEE80211_KEY_FLAG_RX_MGMT = 1<<6,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -170,6 +170,38 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
|
||||
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
/* Keys without a station are used for TX only */
|
||||
if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP))
|
||||
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
/* no MFP (yet) */
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
|
||||
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
||||
break;
|
||||
#endif
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
/* shouldn't happen */
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
}
|
||||
|
||||
err = ieee80211_key_link(key, sdata, sta);
|
||||
if (err)
|
||||
ieee80211_key_free(sdata->local, key);
|
||||
|
@ -2038,9 +2070,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
|||
*/
|
||||
if (!sdata->u.mgd.associated ||
|
||||
sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
ieee80211_recalc_smps(sdata->local);
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,16 +68,14 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
|
|||
return mode;
|
||||
}
|
||||
|
||||
bool ieee80211_set_channel_type(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum nl80211_channel_type chantype)
|
||||
static enum nl80211_channel_type
|
||||
ieee80211_get_superchan(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_sub_if_data *tmp;
|
||||
enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
|
||||
bool result;
|
||||
struct ieee80211_sub_if_data *tmp;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
||||
list_for_each_entry(tmp, &local->interfaces, list) {
|
||||
if (tmp == sdata)
|
||||
continue;
|
||||
|
@ -103,39 +101,70 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
|
|||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
switch (superchan) {
|
||||
return superchan;
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
|
||||
enum nl80211_channel_type chantype2,
|
||||
enum nl80211_channel_type *compat)
|
||||
{
|
||||
/*
|
||||
* start out with chantype1 being the result,
|
||||
* overwriting later if needed
|
||||
*/
|
||||
if (compat)
|
||||
*compat = chantype1;
|
||||
|
||||
switch (chantype1) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
if (compat)
|
||||
*compat = chantype2;
|
||||
break;
|
||||
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)
|
||||
if (chantype2 == NL80211_CHAN_NO_HT)
|
||||
break;
|
||||
superchan = chantype;
|
||||
if (compat)
|
||||
*compat = chantype2;
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
/* allow smaller bandwidth and same */
|
||||
if (chantype == NL80211_CHAN_NO_HT)
|
||||
if (chantype2 == NL80211_CHAN_NO_HT)
|
||||
break;
|
||||
if (chantype == NL80211_CHAN_HT20)
|
||||
if (chantype2 == NL80211_CHAN_HT20)
|
||||
break;
|
||||
if (superchan == chantype)
|
||||
if (chantype2 == chantype1)
|
||||
break;
|
||||
result = false;
|
||||
goto out;
|
||||
return false;
|
||||
}
|
||||
|
||||
local->_oper_channel_type = superchan;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ieee80211_set_channel_type(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum nl80211_channel_type chantype)
|
||||
{
|
||||
enum nl80211_channel_type superchan;
|
||||
enum nl80211_channel_type compatchan;
|
||||
|
||||
superchan = ieee80211_get_superchan(local, sdata);
|
||||
if (!ieee80211_channel_types_are_compatible(superchan, chantype,
|
||||
&compatchan))
|
||||
return false;
|
||||
|
||||
local->_oper_channel_type = compatchan;
|
||||
|
||||
if (sdata)
|
||||
sdata->vif.bss_conf.channel_type = chantype;
|
||||
|
||||
result = true;
|
||||
out:
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
return true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -332,11 +332,27 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
|
|||
return ieee80211_ibss_finish_sta(sta, auth);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code);
|
||||
|
||||
if (len < IEEE80211_DEAUTH_FRAME_LEN)
|
||||
return;
|
||||
|
||||
ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n",
|
||||
mgmt->sa, mgmt->da, mgmt->bssid, reason);
|
||||
sta_info_destroy_addr(sdata, mgmt->sa);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
u16 auth_alg, auth_transaction;
|
||||
struct sta_info *sta;
|
||||
u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
lockdep_assert_held(&sdata->u.ibss.mtx);
|
||||
|
||||
|
@ -352,9 +368,21 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
|
|||
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
|
||||
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
|
||||
sta_info_destroy_addr(sdata, mgmt->sa);
|
||||
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
|
||||
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
|
||||
rcu_read_unlock();
|
||||
|
||||
/*
|
||||
* if we have any problem in allocating the new station, we reply with a
|
||||
* DEAUTH frame to tell the other end that we had a problem
|
||||
*/
|
||||
if (!sta) {
|
||||
ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_UNSPECIFIED, true,
|
||||
deauth_frame_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* IEEE 802.11 standard does not require authentication in IBSS
|
||||
* networks and most implementations do not seem to use it.
|
||||
|
@ -902,6 +930,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|||
case IEEE80211_STYPE_AUTH:
|
||||
ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
|
||||
break;
|
||||
case IEEE80211_STYPE_DEAUTH:
|
||||
ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
|
||||
break;
|
||||
}
|
||||
|
||||
mgmt_out:
|
||||
|
|
|
@ -68,6 +68,8 @@ struct ieee80211_local;
|
|||
#define IEEE80211_DEFAULT_MAX_SP_LEN \
|
||||
IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
|
||||
|
||||
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
|
||||
|
||||
struct ieee80211_fragment_entry {
|
||||
unsigned long first_frag_time;
|
||||
unsigned int seq;
|
||||
|
@ -411,6 +413,7 @@ struct ieee80211_if_managed {
|
|||
struct work_struct monitor_work;
|
||||
struct work_struct chswitch_work;
|
||||
struct work_struct beacon_connection_loss_work;
|
||||
struct work_struct csa_connection_drop_work;
|
||||
|
||||
unsigned long beacon_timeout;
|
||||
unsigned long probe_timeout;
|
||||
|
@ -970,7 +973,6 @@ struct ieee80211_local {
|
|||
int scan_channel_idx;
|
||||
int scan_ies_len;
|
||||
|
||||
struct ieee80211_sched_scan_ies sched_scan_ies;
|
||||
struct work_struct sched_scan_stopped_work;
|
||||
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
|
||||
|
||||
|
@ -1057,7 +1059,7 @@ struct ieee80211_local {
|
|||
bool disable_dynamic_ps;
|
||||
|
||||
int user_power_level; /* in dBm */
|
||||
int power_constr_level; /* in dBm */
|
||||
int ap_power_level; /* in dBm */
|
||||
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
|
@ -1165,7 +1167,6 @@ struct ieee802_11_elems {
|
|||
u8 prep_len;
|
||||
u8 perr_len;
|
||||
u8 country_elem_len;
|
||||
u8 pwr_constr_elem_len;
|
||||
u8 quiet_elem_len;
|
||||
u8 num_of_quiet_elem; /* can be more the one */
|
||||
u8 timeout_int_len;
|
||||
|
@ -1367,7 +1368,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
|||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
void ieee80211_stop_device(struct ieee80211_local *local);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int __ieee80211_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan);
|
||||
|
||||
|
@ -1381,18 +1381,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
|||
|
||||
return ieee80211_reconfig(hw_to_local(hw));
|
||||
}
|
||||
#else
|
||||
static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* utility functions/constants */
|
||||
extern void *mac80211_wiphy_privid; /* for wiphy privid */
|
||||
|
@ -1459,6 +1447,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
|||
u16 transaction, u16 auth_alg,
|
||||
u8 *extra, size_t extra_len, const u8 *bssid,
|
||||
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf);
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
|
|
|
@ -793,11 +793,20 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
|||
flush_work(&sdata->work);
|
||||
/*
|
||||
* When we get here, the interface is marked down.
|
||||
* Call synchronize_rcu() to wait for the RX path
|
||||
* Call rcu_barrier() to wait both for the RX path
|
||||
* should it be using the interface and enqueuing
|
||||
* frames at this very time on another CPU.
|
||||
* frames at this very time on another CPU, and
|
||||
* for the sta free call_rcu callbacks.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
rcu_barrier();
|
||||
|
||||
/*
|
||||
* free_sta_rcu() enqueues a work for the actual
|
||||
* sta cleanup, so we need to flush it while
|
||||
* sdata is still valid.
|
||||
*/
|
||||
flush_workqueue(local->workqueue);
|
||||
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
/*
|
||||
|
|
|
@ -402,7 +402,7 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
|
|||
* Synchronize so the TX path can no longer be using
|
||||
* this key before we free/remove it.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
synchronize_net();
|
||||
|
||||
if (key->local)
|
||||
ieee80211_key_disable_hw_accel(key);
|
||||
|
|
|
@ -150,13 +150,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
|
|||
|
||||
if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_HW_SCANNING, &local->scanning))
|
||||
test_bit(SCAN_HW_SCANNING, &local->scanning) ||
|
||||
!local->ap_power_level)
|
||||
power = chan->max_power;
|
||||
else
|
||||
power = local->power_constr_level ?
|
||||
min(chan->max_power,
|
||||
(chan->max_reg_power - local->power_constr_level)) :
|
||||
chan->max_power;
|
||||
power = min(chan->max_power, local->ap_power_level);
|
||||
|
||||
if (local->user_power_level >= 0)
|
||||
power = min(power, local->user_power_level);
|
||||
|
@ -366,9 +364,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
|
|||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, recalc_smps);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_smps(local);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INET
|
||||
|
|
|
@ -537,7 +537,8 @@ int mesh_plink_open(struct sta_info *sta)
|
|||
spin_lock_bh(&sta->lock);
|
||||
get_random_bytes(&llid, 2);
|
||||
sta->llid = llid;
|
||||
if (sta->plink_state != NL80211_PLINK_LISTEN) {
|
||||
if (sta->plink_state != NL80211_PLINK_LISTEN &&
|
||||
sta->plink_state != NL80211_PLINK_BLOCKED) {
|
||||
spin_unlock_bh(&sta->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
|
@ -88,8 +88,6 @@ MODULE_PARM_DESC(probe_wait_ms,
|
|||
#define TMR_RUNNING_TIMER 0
|
||||
#define TMR_RUNNING_CHANSW 1
|
||||
|
||||
#define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */)
|
||||
|
||||
/*
|
||||
* All cfg80211 functions have to be called outside a locked
|
||||
* section so that they can acquire a lock themselves... This
|
||||
|
@ -574,46 +572,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, u16 stype,
|
||||
u16 reason, bool send_frame,
|
||||
u8 *frame_buf)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt = (void *)frame_buf;
|
||||
|
||||
/* build frame */
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
|
||||
mgmt->duration = 0; /* initialize only */
|
||||
mgmt->seq_ctrl = 0; /* initialize only */
|
||||
memcpy(mgmt->da, bssid, ETH_ALEN);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
||||
/* u.deauth.reason_code == u.disassoc.reason_code */
|
||||
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
|
||||
|
||||
if (send_frame) {
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
DEAUTH_DISASSOC_LEN);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, local->hw.extra_tx_headroom);
|
||||
|
||||
/* copy in frame */
|
||||
memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN),
|
||||
mgmt, DEAUTH_DISASSOC_LEN);
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
|
||||
IEEE80211_SKB_CB(skb)->flags |=
|
||||
IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_send_pspoll(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
|
@ -730,16 +688,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
|||
|
||||
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;
|
||||
sdata_info(sdata,
|
||||
"driver channel switch failed, disconnecting\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
} else {
|
||||
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
||||
}
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_chswitch_done);
|
||||
|
||||
|
@ -784,8 +739,14 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
|
||||
new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
|
||||
if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid, new_freq);
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
|
||||
sdata->local->csa_channel = new_ch;
|
||||
|
||||
|
@ -818,23 +779,71 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
|
||||
static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
u16 capab_info, u8 *pwr_constr_elem,
|
||||
u8 pwr_constr_elem_len)
|
||||
struct ieee80211_channel *channel,
|
||||
const u8 *country_ie, u8 country_ie_len,
|
||||
const u8 *pwr_constr_elem)
|
||||
{
|
||||
struct ieee80211_conf *conf = &sdata->local->hw.conf;
|
||||
struct ieee80211_country_ie_triplet *triplet;
|
||||
int chan = ieee80211_frequency_to_channel(channel->center_freq);
|
||||
int i, chan_pwr, chan_increment, new_ap_level;
|
||||
bool have_chan_pwr = false;
|
||||
|
||||
if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
|
||||
/* Invalid IE */
|
||||
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
|
||||
return;
|
||||
|
||||
/* Power constraint IE length should be 1 octet */
|
||||
if (pwr_constr_elem_len != 1)
|
||||
return;
|
||||
triplet = (void *)(country_ie + 3);
|
||||
country_ie_len -= 3;
|
||||
|
||||
if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
|
||||
(*pwr_constr_elem != sdata->local->power_constr_level)) {
|
||||
sdata->local->power_constr_level = *pwr_constr_elem;
|
||||
ieee80211_hw_config(sdata->local, 0);
|
||||
switch (channel->band) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
/* fall through */
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
case IEEE80211_BAND_60GHZ:
|
||||
chan_increment = 1;
|
||||
break;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
chan_increment = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
/* find channel */
|
||||
while (country_ie_len >= 3) {
|
||||
u8 first_channel = triplet->chans.first_channel;
|
||||
|
||||
if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID)
|
||||
goto next;
|
||||
|
||||
for (i = 0; i < triplet->chans.num_channels; i++) {
|
||||
if (first_channel + i * chan_increment == chan) {
|
||||
have_chan_pwr = true;
|
||||
chan_pwr = triplet->chans.max_power;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (have_chan_pwr)
|
||||
break;
|
||||
|
||||
next:
|
||||
triplet++;
|
||||
country_ie_len -= 3;
|
||||
}
|
||||
|
||||
if (!have_chan_pwr)
|
||||
return;
|
||||
|
||||
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
|
||||
|
||||
if (sdata->local->ap_power_level == new_ap_level)
|
||||
return;
|
||||
|
||||
sdata_info(sdata,
|
||||
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
|
||||
new_ap_level, chan_pwr, *pwr_constr_elem,
|
||||
sdata->u.mgd.bssid);
|
||||
sdata->local->ap_power_level = new_ap_level;
|
||||
ieee80211_hw_config(sdata->local, 0);
|
||||
}
|
||||
|
||||
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
|
||||
|
@ -1339,9 +1348,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
ieee80211_recalc_smps(local);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
ieee80211_recalc_smps(local);
|
||||
ieee80211_recalc_ps_vif(sdata);
|
||||
|
||||
netif_tx_start_all_queues(sdata->dev);
|
||||
|
@ -1438,7 +1447,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
|
||||
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
||||
|
||||
local->power_constr_level = 0;
|
||||
local->ap_power_level = 0;
|
||||
|
||||
del_timer_sync(&local->dynamic_ps_timer);
|
||||
cancel_work_sync(&local->dynamic_ps_enable_work);
|
||||
|
@ -1692,11 +1701,12 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
|||
}
|
||||
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
|
||||
|
||||
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
|
||||
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
|
||||
bool transmit_frame)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u8 frame_buf[DEAUTH_DISASSOC_LEN];
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
if (!ifmgd->associated) {
|
||||
|
@ -1704,19 +1714,17 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
|
|||
return;
|
||||
}
|
||||
|
||||
sdata_info(sdata, "Connection to AP %pM lost\n",
|
||||
ifmgd->associated->bssid);
|
||||
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
false, frame_buf);
|
||||
transmit_frame, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
/*
|
||||
* must be outside lock due to cfg80211,
|
||||
* but that's not a problem.
|
||||
*/
|
||||
cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
|
||||
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
ieee80211_recalc_idle(local);
|
||||
|
@ -1739,10 +1747,24 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
|||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
||||
__ieee80211_connection_loss(sdata);
|
||||
else
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
|
||||
sdata_info(sdata, "Connection to AP %pM lost\n",
|
||||
ifmgd->bssid);
|
||||
__ieee80211_disconnect(sdata, false);
|
||||
} else {
|
||||
ieee80211_mgd_probe_ap(sdata, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.mgd.csa_connection_drop_work);
|
||||
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
__ieee80211_disconnect(sdata, true);
|
||||
}
|
||||
|
||||
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
||||
|
@ -2530,15 +2552,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
bssid, true);
|
||||
}
|
||||
|
||||
/* Note: country IE parsing is done for us by cfg80211 */
|
||||
if (elems.country_elem) {
|
||||
/* TODO: IBSS also needs this */
|
||||
if (elems.pwr_constr_elem)
|
||||
ieee80211_handle_pwr_constr(sdata,
|
||||
le16_to_cpu(mgmt->u.probe_resp.capab_info),
|
||||
elems.pwr_constr_elem,
|
||||
elems.pwr_constr_elem_len);
|
||||
}
|
||||
if (elems.country_elem && elems.pwr_constr_elem &&
|
||||
mgmt->u.probe_resp.capab_info &
|
||||
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
|
||||
ieee80211_handle_pwr_constr(sdata, local->oper_channel,
|
||||
elems.country_elem,
|
||||
elems.country_elem_len,
|
||||
elems.pwr_constr_elem);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
}
|
||||
|
@ -2635,7 +2655,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 frame_buf[DEAUTH_DISASSOC_LEN];
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
|
||||
false, frame_buf);
|
||||
|
@ -2645,7 +2665,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|||
* must be outside lock due to cfg80211,
|
||||
* but that's not a problem.
|
||||
*/
|
||||
cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
|
||||
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
ieee80211_recalc_idle(local);
|
||||
|
@ -2929,6 +2949,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
cancel_work_sync(&ifmgd->monitor_work);
|
||||
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
|
||||
cancel_work_sync(&ifmgd->csa_connection_drop_work);
|
||||
if (del_timer_sync(&ifmgd->timer))
|
||||
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
|
||||
|
||||
|
@ -2985,6 +3006,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
|
||||
INIT_WORK(&ifmgd->beacon_connection_loss_work,
|
||||
ieee80211_beacon_connection_loss_work);
|
||||
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
|
||||
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
||||
(unsigned long) sdata);
|
||||
|
@ -3525,7 +3548,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|||
struct cfg80211_deauth_request *req)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 frame_buf[DEAUTH_DISASSOC_LEN];
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
|
||||
|
@ -3553,7 +3576,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
__cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
|
||||
__cfg80211_send_deauth(sdata->dev, frame_buf,
|
||||
IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
mutex_lock(&sdata->local->mtx);
|
||||
ieee80211_recalc_idle(sdata->local);
|
||||
|
@ -3567,7 +3591,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 bssid[ETH_ALEN];
|
||||
u8 frame_buf[DEAUTH_DISASSOC_LEN];
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
|
||||
|
@ -3592,7 +3616,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
frame_buf);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
__cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
|
||||
__cfg80211_send_disassoc(sdata->dev, frame_buf,
|
||||
IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
mutex_lock(&sdata->local->mtx);
|
||||
ieee80211_recalc_idle(sdata->local);
|
||||
|
|
|
@ -233,8 +233,7 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
|
|||
u32 dur = dep->duration;
|
||||
dep->duration = dur - roc->duration;
|
||||
roc->duration = dur;
|
||||
list_del(&dep->list);
|
||||
list_add(&dep->list, &roc->list);
|
||||
list_move(&dep->list, &roc->list);
|
||||
}
|
||||
}
|
||||
out_unlock:
|
||||
|
|
|
@ -407,7 +407,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
|
|||
enum ieee80211_band band = local->hw.conf.channel->band;
|
||||
|
||||
sdata = rcu_dereference_protected(local->scan_sdata,
|
||||
lockdep_is_held(&local->mtx));;
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
for (i = 0; i < local->scan_req->n_ssids; i++)
|
||||
ieee80211_send_probe_req(
|
||||
|
@ -917,6 +917,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
struct cfg80211_sched_scan_request *req)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_sched_scan_ies sched_scan_ies;
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
|
@ -935,33 +936,28 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
if (!local->hw.wiphy->bands[i])
|
||||
continue;
|
||||
|
||||
local->sched_scan_ies.ie[i] = kzalloc(2 +
|
||||
IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len +
|
||||
req->ie_len,
|
||||
GFP_KERNEL);
|
||||
if (!local->sched_scan_ies.ie[i]) {
|
||||
sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len +
|
||||
req->ie_len,
|
||||
GFP_KERNEL);
|
||||
if (!sched_scan_ies.ie[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
local->sched_scan_ies.len[i] =
|
||||
ieee80211_build_preq_ies(local,
|
||||
local->sched_scan_ies.ie[i],
|
||||
sched_scan_ies.len[i] =
|
||||
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
|
||||
req->ie, req->ie_len, i,
|
||||
(u32) -1, 0);
|
||||
}
|
||||
|
||||
ret = drv_sched_scan_start(local, sdata, req,
|
||||
&local->sched_scan_ies);
|
||||
if (ret == 0) {
|
||||
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
|
||||
if (ret == 0)
|
||||
rcu_assign_pointer(local->sched_scan_sdata, sdata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_free:
|
||||
while (i > 0)
|
||||
kfree(local->sched_scan_ies.ie[--i]);
|
||||
kfree(sched_scan_ies.ie[--i]);
|
||||
out:
|
||||
mutex_unlock(&local->mtx);
|
||||
return ret;
|
||||
|
@ -970,7 +966,7 @@ out:
|
|||
int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret = 0, i;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
|
||||
|
@ -979,12 +975,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (rcu_access_pointer(local->sched_scan_sdata)) {
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
|
||||
kfree(local->sched_scan_ies.ie[i]);
|
||||
|
||||
if (rcu_access_pointer(local->sched_scan_sdata))
|
||||
drv_sched_scan_stop(local, sdata);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
|
@ -1006,7 +999,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
|
|||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local,
|
||||
sched_scan_stopped_work);
|
||||
int i;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
|
||||
|
@ -1015,9 +1007,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
|
|||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
|
||||
kfree(local->sched_scan_ies.ie[i]);
|
||||
|
||||
rcu_assign_pointer(local->sched_scan_sdata, NULL);
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
|
|
|
@ -91,6 +91,70 @@ static int sta_info_hash_del(struct ieee80211_local *local,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void free_sta_work(struct work_struct *wk)
|
||||
{
|
||||
struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
|
||||
int ac, i;
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
/*
|
||||
* At this point, when being called as call_rcu callback,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
BUG_ON(!sdata->bss);
|
||||
|
||||
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
|
||||
atomic_dec(&sdata->bss->num_sta_ps);
|
||||
sta_info_recalc_tim(sta);
|
||||
}
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
|
||||
__skb_queue_purge(&sta->ps_tx_buf[ac]);
|
||||
__skb_queue_purge(&sta->tx_filtered[ac]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
mesh_accept_plinks_update(sdata);
|
||||
mesh_plink_deactivate(sta);
|
||||
del_timer_sync(&sta->plink_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
cancel_work_sync(&sta->drv_unblock_wk);
|
||||
|
||||
/*
|
||||
* Destroy aggregation state here. It would be nice to wait for the
|
||||
* driver to finish aggregation stop and then clean up, but for now
|
||||
* drivers have to handle aggregation stop being requested, followed
|
||||
* directly by station destruction.
|
||||
*/
|
||||
for (i = 0; i < STA_TID_NUM; i++) {
|
||||
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
|
||||
if (!tid_tx)
|
||||
continue;
|
||||
__skb_queue_purge(&tid_tx->pending);
|
||||
kfree(tid_tx);
|
||||
}
|
||||
|
||||
sta_info_free(local, sta);
|
||||
}
|
||||
|
||||
static void free_sta_rcu(struct rcu_head *h)
|
||||
{
|
||||
struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
|
||||
|
||||
ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
|
||||
}
|
||||
|
||||
/* protected by RCU */
|
||||
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *addr)
|
||||
|
@ -241,6 +305,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
spin_lock_init(&sta->lock);
|
||||
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
|
||||
INIT_WORK(&sta->free_sta_wk, free_sta_work);
|
||||
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||||
mutex_init(&sta->ampdu_mlme.mtx);
|
||||
|
||||
|
@ -654,8 +719,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
|
|||
{
|
||||
struct ieee80211_local *local;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
int ret, i, ac;
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
int ret, i;
|
||||
|
||||
might_sleep();
|
||||
|
||||
|
@ -711,65 +775,14 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
|
|||
WARN_ON_ONCE(ret != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
BUG_ON(!sdata->bss);
|
||||
|
||||
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
|
||||
atomic_dec(&sdata->bss->num_sta_ps);
|
||||
sta_info_recalc_tim(sta);
|
||||
}
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
|
||||
__skb_queue_purge(&sta->ps_tx_buf[ac]);
|
||||
__skb_queue_purge(&sta->tx_filtered[ac]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
mesh_accept_plinks_update(sdata);
|
||||
#endif
|
||||
|
||||
sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
|
||||
|
||||
cancel_work_sync(&sta->drv_unblock_wk);
|
||||
|
||||
cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
|
||||
|
||||
rate_control_remove_sta_debugfs(sta);
|
||||
ieee80211_sta_debugfs_remove(sta);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
||||
mesh_plink_deactivate(sta);
|
||||
del_timer_sync(&sta->plink_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Destroy aggregation state here. It would be nice to wait for the
|
||||
* driver to finish aggregation stop and then clean up, but for now
|
||||
* drivers have to handle aggregation stop being requested, followed
|
||||
* directly by station destruction.
|
||||
*/
|
||||
for (i = 0; i < STA_TID_NUM; i++) {
|
||||
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
|
||||
if (!tid_tx)
|
||||
continue;
|
||||
__skb_queue_purge(&tid_tx->pending);
|
||||
kfree(tid_tx);
|
||||
}
|
||||
|
||||
sta_info_free(local, sta);
|
||||
call_rcu(&sta->rcu_head, free_sta_rcu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -287,6 +287,7 @@ struct sta_ampdu_mlme {
|
|||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
struct list_head list;
|
||||
struct rcu_head rcu_head;
|
||||
struct sta_info __rcu *hnext;
|
||||
struct ieee80211_local *local;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
@ -297,6 +298,7 @@ struct sta_info {
|
|||
spinlock_t lock;
|
||||
|
||||
struct work_struct drv_unblock_wk;
|
||||
struct work_struct free_sta_wk;
|
||||
|
||||
u16 listen_interval;
|
||||
|
||||
|
|
|
@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
|
||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||
u64 cookie = (unsigned long)skb;
|
||||
bool found = false;
|
||||
|
||||
acked = info->flags & IEEE80211_TX_STAT_ACK;
|
||||
|
||||
if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(skb->dev, hdr->addr1,
|
||||
cookie, acked, GFP_ATOMIC);
|
||||
} else if (skb->dev) {
|
||||
cfg80211_mgmt_tx_status(
|
||||
skb->dev->ieee80211_ptr, cookie, skb->data,
|
||||
skb->len, acked, GFP_ATOMIC);
|
||||
} else {
|
||||
struct ieee80211_sub_if_data *p2p_sdata;
|
||||
rcu_read_lock();
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (!sdata->dev)
|
||||
continue;
|
||||
|
||||
p2p_sdata = rcu_dereference(local->p2p_sdata);
|
||||
if (p2p_sdata) {
|
||||
cfg80211_mgmt_tx_status(
|
||||
&p2p_sdata->wdev, cookie, skb->data,
|
||||
skb->len, acked, GFP_ATOMIC);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (skb->dev != sdata->dev)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skb->dev) {
|
||||
sdata = rcu_dereference(local->p2p_sdata);
|
||||
if (sdata)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
skb->dev = NULL;
|
||||
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||
cookie, acked, GFP_ATOMIC);
|
||||
} else {
|
||||
cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
|
||||
skb->len, acked, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (unlikely(info->ack_frame_id)) {
|
||||
|
|
|
@ -580,7 +580,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
|
|||
tx->key = NULL;
|
||||
else
|
||||
skip_hw = (tx->key->conf.flags &
|
||||
IEEE80211_KEY_FLAG_SW_MGMT) &&
|
||||
IEEE80211_KEY_FLAG_SW_MGMT_TX) &&
|
||||
ieee80211_is_mgmt(hdr->frame_control);
|
||||
break;
|
||||
case WLAN_CIPHER_SUITE_AES_CMAC:
|
||||
|
|
|
@ -792,8 +792,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
|||
elems->country_elem_len = elen;
|
||||
break;
|
||||
case WLAN_EID_PWR_CONSTRAINT:
|
||||
if (elen != 1) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->pwr_constr_elem = pos;
|
||||
elems->pwr_constr_elem_len = elen;
|
||||
break;
|
||||
case WLAN_EID_TIMEOUT_INTERVAL:
|
||||
elems->timeout_int = pos;
|
||||
|
@ -1004,6 +1007,45 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
|||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt = (void *)frame_buf;
|
||||
|
||||
/* build frame */
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
|
||||
mgmt->duration = 0; /* initialize only */
|
||||
mgmt->seq_ctrl = 0; /* initialize only */
|
||||
memcpy(mgmt->da, bssid, ETH_ALEN);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
||||
/* u.deauth.reason_code == u.disassoc.reason_code */
|
||||
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
|
||||
|
||||
if (send_frame) {
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
IEEE80211_DEAUTH_FRAME_LEN);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
skb_reserve(skb, local->hw.extra_tx_headroom);
|
||||
|
||||
/* copy in frame */
|
||||
memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
|
||||
mgmt, IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION ||
|
||||
!(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
|
||||
IEEE80211_SKB_CB(skb)->flags |=
|
||||
IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
}
|
||||
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
|
@ -1564,14 +1606,13 @@ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* must hold iflist_mtx */
|
||||
void ieee80211_recalc_smps(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
|
||||
int count = 0;
|
||||
|
||||
lockdep_assert_held(&local->iflist_mtx);
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
||||
/*
|
||||
* This function could be improved to handle multiple
|
||||
|
@ -1600,12 +1641,14 @@ void ieee80211_recalc_smps(struct ieee80211_local *local)
|
|||
}
|
||||
|
||||
if (smps_mode == local->smps_mode)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
set:
|
||||
local->smps_mode = smps_mode;
|
||||
/* changed flag is auto-detected for this */
|
||||
ieee80211_hw_config(local, 0);
|
||||
unlock:
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
|
||||
|
|
Loading…
Reference in New Issue