mirror of https://gitee.com/openkylin/linux.git
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
commit
855df36de3
10
MAINTAINERS
10
MAINTAINERS
|
@ -5629,16 +5629,6 @@ F: Documentation/networking/mac80211-injection.txt
|
|||
F: include/net/mac80211.h
|
||||
F: net/mac80211/
|
||||
|
||||
MAC80211 PID RATE CONTROL
|
||||
M: Stefano Brivio <stefano.brivio@polimi.it>
|
||||
M: Mattias Nissler <mattias.nissler@gmx.de>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
|
||||
S: Maintained
|
||||
F: net/mac80211/rc80211_pid*
|
||||
|
||||
MACVLAN DRIVER
|
||||
M: Patrick McHardy <kaber@trash.net>
|
||||
L: netdev@vger.kernel.org
|
||||
|
|
|
@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work)
|
|||
|
||||
static int at76_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct at76_priv *priv = hw->priv;
|
||||
struct at76_req_scan scan;
|
||||
u8 *ssid = NULL;
|
||||
|
|
|
@ -3137,10 +3137,11 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
|
|||
|
||||
static int ath10k_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct wmi_start_scan_arg arg;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
|
|
@ -2409,8 +2409,9 @@ void ath_offchannel_timer(unsigned long data)
|
|||
}
|
||||
|
||||
static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct ath_softc *sc = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
int ret = 0;
|
||||
|
|
|
@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
|
|||
|
||||
int cw1200_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cw1200_common *priv = hw->priv;
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct wsm_template_frame frame = {
|
||||
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ struct cw1200_scan {
|
|||
|
||||
int cw1200_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req);
|
||||
struct ieee80211_scan_request *hw_req);
|
||||
void cw1200_scan_work(struct work_struct *work);
|
||||
void cw1200_scan_timeout(struct work_struct *work);
|
||||
void cw1200_clear_recent_scan_work(struct work_struct *work);
|
||||
|
|
|
@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
|
|||
|
||||
int
|
||||
il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct il_priv *il = hw->priv;
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il);
|
|||
int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
|
||||
void il_force_scan_end(struct il_priv *il);
|
||||
int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req);
|
||||
struct ieee80211_scan_request *hw_req);
|
||||
void il_internal_short_hw_scan(struct il_priv *il);
|
||||
int il_force_reset(struct il_priv *il, bool external);
|
||||
u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
|
||||
|
|
|
@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
|
|||
|
||||
static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
int ret;
|
||||
|
||||
IWL_DEBUG_MAC80211(priv, "enter\n");
|
||||
|
|
|
@ -1537,9 +1537,10 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
|
|||
|
||||
static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
int ret;
|
||||
|
||||
if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
|
||||
|
@ -1827,7 +1828,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
|
|||
static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
|
|
|
@ -854,7 +854,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
|
|||
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
|
||||
struct cfg80211_sched_scan_request *req);
|
||||
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
|
||||
|
|
|
@ -204,7 +204,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
|
|||
*/
|
||||
static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
|
||||
int n_ssids, const u8 *ssid, int ssid_len,
|
||||
const u8 *ie, int ie_len,
|
||||
const u8 *band_ie, int band_ie_len,
|
||||
const u8 *common_ie, int common_ie_len,
|
||||
int left)
|
||||
{
|
||||
int len = 0;
|
||||
|
@ -244,12 +245,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
|
|||
|
||||
len += ssid_len + 2;
|
||||
|
||||
if (WARN_ON(left < ie_len))
|
||||
if (WARN_ON(left < band_ie_len + common_ie_len))
|
||||
return len;
|
||||
|
||||
if (ie && ie_len) {
|
||||
memcpy(pos, ie, ie_len);
|
||||
len += ie_len;
|
||||
if (band_ie && band_ie_len) {
|
||||
memcpy(pos, band_ie, band_ie_len);
|
||||
pos += band_ie_len;
|
||||
len += band_ie_len;
|
||||
}
|
||||
|
||||
if (common_ie && common_ie_len) {
|
||||
memcpy(pos, common_ie, common_ie_len);
|
||||
pos += common_ie_len;
|
||||
len += common_ie_len;
|
||||
}
|
||||
|
||||
return (u16)len;
|
||||
|
@ -382,7 +390,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
|
|||
(struct ieee80211_mgmt *)cmd->data,
|
||||
vif->addr,
|
||||
req->n_ssids, ssid, ssid_len,
|
||||
req->ie, req->ie_len,
|
||||
req->ie, req->ie_len, NULL, 0,
|
||||
mvm->fw->ucode_capa.max_probe_length));
|
||||
|
||||
iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, ¶ms);
|
||||
|
@ -561,7 +569,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
|
|||
|
||||
static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sched_scan_ies *ies,
|
||||
struct ieee80211_scan_ies *ies,
|
||||
enum ieee80211_band band,
|
||||
struct iwl_tx_cmd *cmd,
|
||||
u8 *data)
|
||||
|
@ -577,7 +585,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
|
|||
cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
|
||||
vif->addr,
|
||||
1, NULL, 0,
|
||||
ies->ie[band], ies->len[band],
|
||||
ies->ies[band], ies->len[band],
|
||||
ies->common_ies, ies->common_ie_len,
|
||||
SCAN_OFFLOAD_PROBE_REQ_SIZE);
|
||||
cmd->len = cpu_to_le16(cmd_len);
|
||||
}
|
||||
|
@ -735,7 +744,7 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
|
|||
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
|
||||
int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
|
||||
|
|
|
@ -781,6 +781,36 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
|
|||
netif_rx(skb);
|
||||
}
|
||||
|
||||
struct mac80211_hwsim_addr_match_data {
|
||||
u8 addr[ETH_ALEN];
|
||||
bool ret;
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mac80211_hwsim_addr_match_data *md = data;
|
||||
|
||||
if (memcmp(mac, md->addr, ETH_ALEN) == 0)
|
||||
md->ret = true;
|
||||
}
|
||||
|
||||
static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct mac80211_hwsim_addr_match_data md = {
|
||||
.ret = false,
|
||||
};
|
||||
|
||||
memcpy(md.addr, addr, ETH_ALEN);
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_addr_iter,
|
||||
&md);
|
||||
|
||||
return md.ret;
|
||||
}
|
||||
|
||||
static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
|
||||
struct sk_buff *skb)
|
||||
|
@ -798,8 +828,7 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
|
|||
/* Allow unicast frames to own address if there is a pending
|
||||
* PS-Poll */
|
||||
if (data->ps_poll_pending &&
|
||||
memcmp(data->hw->wiphy->perm_addr, skb->data + 4,
|
||||
ETH_ALEN) == 0) {
|
||||
mac80211_hwsim_addr_match(data, skb->data + 4)) {
|
||||
data->ps_poll_pending = false;
|
||||
return true;
|
||||
}
|
||||
|
@ -809,39 +838,6 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct mac80211_hwsim_addr_match_data {
|
||||
bool ret;
|
||||
const u8 *addr;
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mac80211_hwsim_addr_match_data *md = data;
|
||||
if (memcmp(mac, md->addr, ETH_ALEN) == 0)
|
||||
md->ret = true;
|
||||
}
|
||||
|
||||
|
||||
static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct mac80211_hwsim_addr_match_data md;
|
||||
|
||||
if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0)
|
||||
return true;
|
||||
|
||||
md.ret = false;
|
||||
md.addr = addr;
|
||||
ieee80211_iterate_active_interfaces_atomic(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_addr_iter,
|
||||
&md);
|
||||
|
||||
return md.ret;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||
struct sk_buff *my_skb,
|
||||
int dst_portid)
|
||||
|
@ -1740,9 +1736,10 @@ static void hw_scan_work(struct work_struct *work)
|
|||
|
||||
static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
|
||||
mutex_lock(&hwsim->mutex);
|
||||
if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
|
||||
|
|
|
@ -2609,7 +2609,8 @@ static int
|
|||
mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
|
||||
int ret;
|
||||
|
|
|
@ -991,8 +991,9 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
|
||||
static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct wl1251 *wl = hw->priv;
|
||||
struct sk_buff *skb;
|
||||
size_t ssid_len = 0;
|
||||
|
|
|
@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
cmd->params.role_id, band,
|
||||
wl->scan.ssid, wl->scan.ssid_len,
|
||||
wl->scan.req->ie,
|
||||
wl->scan.req->ie_len, false);
|
||||
wl->scan.req->ie_len, NULL, 0, false);
|
||||
if (ret < 0) {
|
||||
wl1271_error("PROBE request template failed");
|
||||
goto out;
|
||||
|
@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
|
|||
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
struct wl1271_cmd_sched_scan_config *cfg = NULL;
|
||||
struct wlcore_scan_channels *cfg_channels = NULL;
|
||||
|
@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
|||
wlvif->role_id, band,
|
||||
req->ssids[0].ssid,
|
||||
req->ssids[0].ssid_len,
|
||||
ies->ie[band],
|
||||
ies->len[band], true);
|
||||
ies->ies[band],
|
||||
ies->len[band],
|
||||
ies->common_ies,
|
||||
ies->common_ie_len,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
wl1271_error("2.4GHz PROBE request template failed");
|
||||
goto out;
|
||||
|
@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
|||
wlvif->role_id, band,
|
||||
req->ssids[0].ssid,
|
||||
req->ssids[0].ssid_len,
|
||||
ies->ie[band],
|
||||
ies->len[band], true);
|
||||
ies->ies[band],
|
||||
ies->len[band],
|
||||
ies->common_ies,
|
||||
ies->common_ie_len,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
wl1271_error("5GHz PROBE request template failed");
|
||||
goto out;
|
||||
|
@ -449,7 +455,7 @@ int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|||
|
||||
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -135,6 +135,6 @@ int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
|||
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
#endif
|
||||
|
|
|
@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
req->ssids ? req->ssids[0].ssid_len : 0,
|
||||
req->ie,
|
||||
req->ie_len,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
wl1271_error("2.4GHz PROBE request template failed");
|
||||
|
@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
req->ssids ? req->ssids[0].ssid_len : 0,
|
||||
req->ie,
|
||||
req->ie_len,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
wl1271_error("5GHz PROBE request template failed");
|
||||
|
@ -161,7 +165,7 @@ static
|
|||
int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
struct wl18xx_cmd_scan_params *cmd;
|
||||
struct wlcore_scan_channels *cmd_channels = NULL;
|
||||
|
@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
|
|||
cmd->role_id, band,
|
||||
req->ssids ? req->ssids[0].ssid : NULL,
|
||||
req->ssids ? req->ssids[0].ssid_len : 0,
|
||||
ies->ie[band],
|
||||
ies->ies[band],
|
||||
ies->len[band],
|
||||
ies->common_ies,
|
||||
ies->common_ie_len,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
wl1271_error("2.4GHz PROBE request template failed");
|
||||
|
@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
|
|||
cmd->role_id, band,
|
||||
req->ssids ? req->ssids[0].ssid : NULL,
|
||||
req->ssids ? req->ssids[0].ssid_len : 0,
|
||||
ies->ie[band],
|
||||
ies->ies[band],
|
||||
ies->len[band],
|
||||
ies->common_ies,
|
||||
ies->common_ie_len,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
wl1271_error("5GHz PROBE request template failed");
|
||||
|
@ -277,7 +285,7 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
|
|||
|
||||
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
|
||||
}
|
||||
|
|
|
@ -122,6 +122,6 @@ int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
|||
void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
#endif
|
||||
|
|
|
@ -1124,7 +1124,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 role_id, u8 band,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, bool sched_scan)
|
||||
const u8 *ie0, size_t ie0_len, const u8 *ie1,
|
||||
size_t ie1_len, bool sched_scan)
|
||||
{
|
||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
struct sk_buff *skb;
|
||||
|
@ -1136,13 +1137,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
|
||||
ie_len);
|
||||
ie0_len + ie1_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (ie_len)
|
||||
memcpy(skb_put(skb, ie_len), ie, ie_len);
|
||||
if (ie0_len)
|
||||
memcpy(skb_put(skb, ie0_len), ie0, ie0_len);
|
||||
if (ie1_len)
|
||||
memcpy(skb_put(skb, ie1_len), ie1, ie1_len);
|
||||
|
||||
if (sched_scan &&
|
||||
(wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
|
||||
|
|
|
@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
u8 role_id, u8 band,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, bool sched_scan);
|
||||
const u8 *ie, size_t ie_len, const u8 *common_ie,
|
||||
size_t common_ie_len, bool sched_scan);
|
||||
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct sk_buff *skb);
|
||||
|
|
|
@ -3540,8 +3540,9 @@ void wlcore_regdomain_config(struct wl1271 *wl)
|
|||
|
||||
static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int ret;
|
||||
u8 *ssid = NULL;
|
||||
|
@ -3636,7 +3637,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
|
|||
static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
|
|
|
@ -37,7 +37,7 @@ void wl1271_scan_complete_work(struct work_struct *work);
|
|||
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
|
||||
struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
void wlcore_scan_sched_scan_results(struct wl1271 *wl);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ struct wlcore_ops {
|
|||
int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
||||
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
|
||||
int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
|
||||
|
|
|
@ -1621,6 +1621,9 @@ enum ieee80211_reasoncode {
|
|||
WLAN_REASON_INVALID_RSN_IE_CAP = 22,
|
||||
WLAN_REASON_IEEE8021X_FAILED = 23,
|
||||
WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
|
||||
/* TDLS (802.11z) */
|
||||
WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25,
|
||||
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26,
|
||||
/* 802.11e */
|
||||
WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
|
||||
WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
|
||||
|
|
|
@ -2266,10 +2266,6 @@ struct cfg80211_qos_map {
|
|||
*
|
||||
* @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
|
||||
*
|
||||
* @set_ringparam: Set tx and rx ring sizes.
|
||||
*
|
||||
* @get_ringparam: Get tx and rx ring current and maximum sizes.
|
||||
*
|
||||
* @tdls_mgmt: Transmit a TDLS management frame.
|
||||
* @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
|
||||
*
|
||||
|
@ -2278,16 +2274,6 @@ struct cfg80211_qos_map {
|
|||
*
|
||||
* @set_noack_map: Set the NoAck Map for the TIDs.
|
||||
*
|
||||
* @get_et_sset_count: Ethtool API to get string-set count.
|
||||
* See @ethtool_ops.get_sset_count
|
||||
*
|
||||
* @get_et_stats: Ethtool API to get a set of u64 stats.
|
||||
* See @ethtool_ops.get_ethtool_stats
|
||||
*
|
||||
* @get_et_strings: Ethtool API to get a set of strings to describe stats
|
||||
* and perhaps other supported types of ethtool data-sets.
|
||||
* See @ethtool_ops.get_strings
|
||||
*
|
||||
* @get_channel: Get the current operating channel for the virtual interface.
|
||||
* For monitor interfaces, it should return %NULL unless there's a single
|
||||
* current monitoring channel.
|
||||
|
@ -2315,7 +2301,12 @@ struct cfg80211_qos_map {
|
|||
* reliability. This operation can not fail.
|
||||
* @set_coalesce: Set coalesce parameters.
|
||||
*
|
||||
* @channel_switch: initiate channel-switch procedure (with CSA)
|
||||
* @channel_switch: initiate channel-switch procedure (with CSA). Driver is
|
||||
* responsible for veryfing if the switch is possible. Since this is
|
||||
* inherently tricky driver may decide to disconnect an interface later
|
||||
* with cfg80211_stop_iface(). This doesn't mean driver can accept
|
||||
* everything. It should do it's best to verify requests and reject them
|
||||
* as soon as possible.
|
||||
*
|
||||
* @set_qos_map: Set QoS mapping information to the driver
|
||||
*
|
||||
|
@ -2503,10 +2494,6 @@ struct cfg80211_ops {
|
|||
int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
|
||||
int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
|
||||
|
||||
int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
|
||||
void (*get_ringparam)(struct wiphy *wiphy,
|
||||
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
|
||||
|
||||
int (*sched_scan_start)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_sched_scan_request *request);
|
||||
|
@ -2518,7 +2505,7 @@ struct cfg80211_ops {
|
|||
int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *buf, size_t len);
|
||||
bool initiator, const u8 *buf, size_t len);
|
||||
int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, enum nl80211_tdls_operation oper);
|
||||
|
||||
|
@ -2529,13 +2516,6 @@ struct cfg80211_ops {
|
|||
struct net_device *dev,
|
||||
u16 noack_map);
|
||||
|
||||
int (*get_et_sset_count)(struct wiphy *wiphy,
|
||||
struct net_device *dev, int sset);
|
||||
void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ethtool_stats *stats, u64 *data);
|
||||
void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
|
||||
u32 sset, u8 *data);
|
||||
|
||||
int (*get_channel)(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
@ -4843,6 +4823,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|||
*/
|
||||
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
|
||||
|
||||
|
||||
/* ethtool helper */
|
||||
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
|
||||
|
||||
/* Logging, debugging and troubleshooting/diagnostic helpers. */
|
||||
|
||||
/* wiphy_printk helpers, similar to dev_printk */
|
||||
|
|
|
@ -754,20 +754,25 @@ struct ieee80211_tx_info {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_sched_scan_ies - scheduled scan IEs
|
||||
* struct ieee80211_scan_ies - descriptors for different blocks of IEs
|
||||
*
|
||||
* This structure is used to pass the appropriate IEs to be used in scheduled
|
||||
* scans for all bands. It contains both the IEs passed from the userspace
|
||||
* This structure is used to point to different blocks of IEs in HW scan
|
||||
* and scheduled scan. These blocks contain the IEs passed by userspace
|
||||
* and the ones generated by mac80211.
|
||||
*
|
||||
* @ie: array with the IEs for each supported band
|
||||
* @len: array with the total length of the IEs for each band
|
||||
* @ies: pointers to band specific IEs.
|
||||
* @len: lengths of band_specific IEs.
|
||||
* @common_ies: IEs for all bands (especially vendor specific ones)
|
||||
* @common_ie_len: length of the common_ies
|
||||
*/
|
||||
struct ieee80211_sched_scan_ies {
|
||||
u8 *ie[IEEE80211_NUM_BANDS];
|
||||
struct ieee80211_scan_ies {
|
||||
const u8 *ies[IEEE80211_NUM_BANDS];
|
||||
size_t len[IEEE80211_NUM_BANDS];
|
||||
const u8 *common_ies;
|
||||
size_t common_ie_len;
|
||||
};
|
||||
|
||||
|
||||
static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
|
||||
{
|
||||
return (struct ieee80211_tx_info *)skb->cb;
|
||||
|
@ -1601,11 +1606,8 @@ struct ieee80211_tx_control {
|
|||
* is not enabled the default action is to disconnect when getting the
|
||||
* CSA frame.
|
||||
*
|
||||
* @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
|
||||
* channel context on-the-fly. This is needed for channel switch
|
||||
* on single-channel hardware. It can also be used as an
|
||||
* optimization in certain channel switch cases with
|
||||
* multi-channel.
|
||||
* @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
|
||||
* in one command, mac80211 doesn't have to run separate scans per band.
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
|
||||
|
@ -1637,7 +1639,8 @@ enum ieee80211_hw_flags {
|
|||
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
|
||||
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
|
||||
IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
|
||||
IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29,
|
||||
/* bit 29 unused */
|
||||
IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1763,6 +1766,19 @@ struct ieee80211_hw {
|
|||
const struct ieee80211_cipher_scheme *cipher_schemes;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_scan_request - hw scan request
|
||||
*
|
||||
* @ies: pointers different parts of IEs (in req.ie)
|
||||
* @req: cfg80211 request.
|
||||
*/
|
||||
struct ieee80211_scan_request {
|
||||
struct ieee80211_scan_ies ies;
|
||||
|
||||
/* Keep last */
|
||||
struct cfg80211_scan_request req;
|
||||
};
|
||||
|
||||
/**
|
||||
* wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
|
||||
*
|
||||
|
@ -2764,6 +2780,15 @@ enum ieee80211_roc_type {
|
|||
* mac80211 will transmit the frame right away.
|
||||
* The callback is optional and can (should!) sleep.
|
||||
*
|
||||
* @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
|
||||
* a TDLS discovery-request, we expect a reply to arrive on the AP's
|
||||
* channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS
|
||||
* setup-response is a direct packet not buffered by the AP.
|
||||
* mac80211 will call this function just before the transmission of a TDLS
|
||||
* discovery-request. The recommended period of protection is at least
|
||||
* 2 * (DTIM period).
|
||||
* The callback is optional and can sleep.
|
||||
*
|
||||
* @add_chanctx: Notifies device driver about new channel context creation.
|
||||
* @remove_chanctx: Notifies device driver about channel context destruction.
|
||||
* @change_chanctx: Notifies device driver about channel context changes that
|
||||
|
@ -2865,13 +2890,13 @@ struct ieee80211_ops {
|
|||
void (*set_default_unicast_key)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, int idx);
|
||||
int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req);
|
||||
struct ieee80211_scan_request *req);
|
||||
void (*cancel_hw_scan)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
int (*sched_scan_start)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies);
|
||||
struct ieee80211_scan_ies *ies);
|
||||
int (*sched_scan_stop)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
void (*sw_scan_start)(struct ieee80211_hw *hw);
|
||||
|
@ -2981,6 +3006,9 @@ struct ieee80211_ops {
|
|||
void (*mgd_prepare_tx)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
|
||||
void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
|
||||
int (*add_chanctx)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx);
|
||||
void (*remove_chanctx)(struct ieee80211_hw *hw,
|
||||
|
@ -4815,4 +4843,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
|
|||
*/
|
||||
void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
|
||||
|
||||
/**
|
||||
* ieee80211_tdls_oper - request userspace to perform a TDLS operation
|
||||
* @vif: virtual interface
|
||||
* @peer: the peer's destination address
|
||||
* @oper: the requested TDLS operation
|
||||
* @reason_code: reason code for the operation, valid for TDLS teardown
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* See cfg80211_tdls_oper_request().
|
||||
*/
|
||||
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||
enum nl80211_tdls_operation oper,
|
||||
u16 reason_code, gfp_t gfp);
|
||||
#endif /* MAC80211_H */
|
||||
|
|
|
@ -1591,6 +1591,9 @@ enum nl80211_commands {
|
|||
* creation then the new interface will be owned by the netlink socket
|
||||
* that created it and will be destroyed when the socket is closed
|
||||
*
|
||||
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
|
||||
* the TDLS link initiator.
|
||||
*
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
|
@ -1931,6 +1934,8 @@ enum nl80211_attrs {
|
|||
NL80211_ATTR_CSA_C_OFFSETS_TX,
|
||||
NL80211_ATTR_MAX_CSA_COUNTERS,
|
||||
|
||||
NL80211_ATTR_TDLS_INITIATOR,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
|
|
@ -19,14 +19,6 @@ if MAC80211 != n
|
|||
config MAC80211_HAS_RC
|
||||
bool
|
||||
|
||||
config MAC80211_RC_PID
|
||||
bool "PID controller based rate control algorithm" if EXPERT
|
||||
select MAC80211_HAS_RC
|
||||
---help---
|
||||
This option enables a TX rate control algorithm for
|
||||
mac80211 that uses a PID controller to select the TX
|
||||
rate.
|
||||
|
||||
config MAC80211_RC_MINSTREL
|
||||
bool "Minstrel" if EXPERT
|
||||
select MAC80211_HAS_RC
|
||||
|
@ -51,14 +43,6 @@ choice
|
|||
overridden through the ieee80211_default_rc_algo module
|
||||
parameter if different algorithms are available.
|
||||
|
||||
config MAC80211_RC_DEFAULT_PID
|
||||
bool "PID controller based rate control algorithm"
|
||||
depends on MAC80211_RC_PID
|
||||
---help---
|
||||
Select the PID controller based rate control as the
|
||||
default rate control algorithm. You should choose
|
||||
this unless you know what you are doing.
|
||||
|
||||
config MAC80211_RC_DEFAULT_MINSTREL
|
||||
bool "Minstrel"
|
||||
depends on MAC80211_RC_MINSTREL
|
||||
|
@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT
|
|||
string
|
||||
default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT
|
||||
default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
|
||||
default "pid" if MAC80211_RC_DEFAULT_PID
|
||||
default ""
|
||||
|
||||
endif
|
||||
|
|
|
@ -17,6 +17,7 @@ mac80211-y := \
|
|||
aes_ccm.o \
|
||||
aes_cmac.o \
|
||||
cfg.o \
|
||||
ethtool.o \
|
||||
rx.o \
|
||||
spectmgmt.o \
|
||||
tx.o \
|
||||
|
@ -47,17 +48,12 @@ mac80211-$(CONFIG_PM) += pm.o
|
|||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
# objects for PID algorithm
|
||||
rc80211_pid-y := rc80211_pid_algo.o
|
||||
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
|
||||
|
||||
rc80211_minstrel-y := rc80211_minstrel.o
|
||||
rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
|
||||
|
||||
rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
|
||||
rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
|
||||
|
||||
mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
|
||||
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
|
||||
mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
|
||||
|
||||
|
|
|
@ -170,10 +170,13 @@ ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
|
|||
{
|
||||
int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
|
||||
|
||||
/* we do refcounting here, so don't use the queue reason refcounting */
|
||||
|
||||
if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
|
||||
ieee80211_stop_queue_by_reason(
|
||||
&sdata->local->hw, queue,
|
||||
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
|
||||
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
|
||||
false);
|
||||
__acquire(agg_queue);
|
||||
}
|
||||
|
||||
|
@ -185,7 +188,8 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
|
|||
if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
|
||||
ieee80211_wake_queue_by_reason(
|
||||
&sdata->local->hw, queue,
|
||||
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
|
||||
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
|
||||
false);
|
||||
__release(agg_queue);
|
||||
}
|
||||
|
||||
|
|
|
@ -468,327 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
|
|||
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
||||
}
|
||||
|
||||
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct rate_control_ref *ref = local->rate_ctrl;
|
||||
struct timespec uptime;
|
||||
u64 packets = 0;
|
||||
u32 thr = 0;
|
||||
int i, ac;
|
||||
|
||||
sinfo->generation = sdata->local->sta_generation;
|
||||
|
||||
sinfo->filled = STATION_INFO_INACTIVE_TIME |
|
||||
STATION_INFO_RX_BYTES64 |
|
||||
STATION_INFO_TX_BYTES64 |
|
||||
STATION_INFO_RX_PACKETS |
|
||||
STATION_INFO_TX_PACKETS |
|
||||
STATION_INFO_TX_RETRIES |
|
||||
STATION_INFO_TX_FAILED |
|
||||
STATION_INFO_TX_BITRATE |
|
||||
STATION_INFO_RX_BITRATE |
|
||||
STATION_INFO_RX_DROP_MISC |
|
||||
STATION_INFO_BSS_PARAM |
|
||||
STATION_INFO_CONNECTED_TIME |
|
||||
STATION_INFO_STA_FLAGS |
|
||||
STATION_INFO_BEACON_LOSS_COUNT;
|
||||
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
sinfo->connected_time = uptime.tv_sec - sta->last_connected;
|
||||
|
||||
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
|
||||
sinfo->tx_bytes = 0;
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
sinfo->tx_bytes += sta->tx_bytes[ac];
|
||||
packets += sta->tx_packets[ac];
|
||||
}
|
||||
sinfo->tx_packets = packets;
|
||||
sinfo->rx_bytes = sta->rx_bytes;
|
||||
sinfo->rx_packets = sta->rx_packets;
|
||||
sinfo->tx_retries = sta->tx_retry_count;
|
||||
sinfo->tx_failed = sta->tx_retry_failed;
|
||||
sinfo->rx_dropped_misc = sta->rx_dropped;
|
||||
sinfo->beacon_loss_count = sta->beacon_loss_count;
|
||||
|
||||
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
|
||||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
|
||||
sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
|
||||
if (!local->ops->get_rssi ||
|
||||
drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
|
||||
sinfo->signal = (s8)sta->last_signal;
|
||||
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
|
||||
}
|
||||
if (sta->chains) {
|
||||
sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
|
||||
STATION_INFO_CHAIN_SIGNAL_AVG;
|
||||
|
||||
sinfo->chains = sta->chains;
|
||||
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
||||
sinfo->chain_signal[i] = sta->chain_signal_last[i];
|
||||
sinfo->chain_signal_avg[i] =
|
||||
(s8) -ewma_read(&sta->chain_signal_avg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
|
||||
sta_set_rate_info_rx(sta, &sinfo->rxrate);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
sinfo->filled |= STATION_INFO_LLID |
|
||||
STATION_INFO_PLID |
|
||||
STATION_INFO_PLINK_STATE |
|
||||
STATION_INFO_LOCAL_PM |
|
||||
STATION_INFO_PEER_PM |
|
||||
STATION_INFO_NONPEER_PM;
|
||||
|
||||
sinfo->llid = sta->llid;
|
||||
sinfo->plid = sta->plid;
|
||||
sinfo->plink_state = sta->plink_state;
|
||||
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
||||
sinfo->filled |= STATION_INFO_T_OFFSET;
|
||||
sinfo->t_offset = sta->t_offset;
|
||||
}
|
||||
sinfo->local_pm = sta->local_pm;
|
||||
sinfo->peer_pm = sta->peer_pm;
|
||||
sinfo->nonpeer_pm = sta->nonpeer_pm;
|
||||
#endif
|
||||
}
|
||||
|
||||
sinfo->bss_param.flags = 0;
|
||||
if (sdata->vif.bss_conf.use_cts_prot)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
|
||||
if (sdata->vif.bss_conf.use_short_preamble)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
|
||||
if (sdata->vif.bss_conf.use_short_slot)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
|
||||
sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
|
||||
sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
|
||||
|
||||
sinfo->sta_flags.set = 0;
|
||||
sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
||||
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
||||
BIT(NL80211_STA_FLAG_WME) |
|
||||
BIT(NL80211_STA_FLAG_MFP) |
|
||||
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
||||
BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
||||
BIT(NL80211_STA_FLAG_TDLS_PEER);
|
||||
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
|
||||
if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
|
||||
if (test_sta_flag(sta, WLAN_STA_WME))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
|
||||
if (test_sta_flag(sta, WLAN_STA_MFP))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
|
||||
if (test_sta_flag(sta, WLAN_STA_AUTH))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
|
||||
if (test_sta_flag(sta, WLAN_STA_ASSOC))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
|
||||
|
||||
/* check if the driver has a SW RC implementation */
|
||||
if (ref && ref->ops->get_expected_throughput)
|
||||
thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
|
||||
else
|
||||
thr = drv_get_expected_throughput(local, &sta->sta);
|
||||
|
||||
if (thr != 0) {
|
||||
sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
|
||||
sinfo->expected_throughput = thr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
|
||||
"rx_packets", "rx_bytes", "wep_weak_iv_count",
|
||||
"rx_duplicates", "rx_fragments", "rx_dropped",
|
||||
"tx_packets", "tx_bytes", "tx_fragments",
|
||||
"tx_filtered", "tx_retry_failed", "tx_retries",
|
||||
"beacon_loss", "sta_state", "txrate", "rxrate", "signal",
|
||||
"channel", "noise", "ch_time", "ch_time_busy",
|
||||
"ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
|
||||
};
|
||||
#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats)
|
||||
|
||||
static int ieee80211_get_et_sset_count(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
int sset)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
int rv = 0;
|
||||
|
||||
if (sset == ETH_SS_STATS)
|
||||
rv += STA_STATS_LEN;
|
||||
|
||||
rv += drv_get_et_sset_count(sdata, sset);
|
||||
|
||||
if (rv == 0)
|
||||
return -EOPNOTSUPP;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void ieee80211_get_et_stats(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct ethtool_stats *stats,
|
||||
u64 *data)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_channel *channel;
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct station_info sinfo;
|
||||
struct survey_info survey;
|
||||
int i, q;
|
||||
#define STA_STATS_SURVEY_LEN 7
|
||||
|
||||
memset(data, 0, sizeof(u64) * STA_STATS_LEN);
|
||||
|
||||
#define ADD_STA_STATS(sta) \
|
||||
do { \
|
||||
data[i++] += sta->rx_packets; \
|
||||
data[i++] += sta->rx_bytes; \
|
||||
data[i++] += sta->wep_weak_iv_count; \
|
||||
data[i++] += sta->num_duplicates; \
|
||||
data[i++] += sta->rx_fragments; \
|
||||
data[i++] += sta->rx_dropped; \
|
||||
\
|
||||
data[i++] += sinfo.tx_packets; \
|
||||
data[i++] += sinfo.tx_bytes; \
|
||||
data[i++] += sta->tx_fragments; \
|
||||
data[i++] += sta->tx_filtered_count; \
|
||||
data[i++] += sta->tx_retry_failed; \
|
||||
data[i++] += sta->tx_retry_count; \
|
||||
data[i++] += sta->beacon_loss_count; \
|
||||
} while (0)
|
||||
|
||||
/* For Managed stations, find the single station based on BSSID
|
||||
* and use that. For interface types, iterate through all available
|
||||
* stations and add stats for any station that is assigned to this
|
||||
* network device.
|
||||
*/
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
|
||||
|
||||
if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
|
||||
goto do_survey;
|
||||
|
||||
sinfo.filled = 0;
|
||||
sta_set_sinfo(sta, &sinfo);
|
||||
|
||||
i = 0;
|
||||
ADD_STA_STATS(sta);
|
||||
|
||||
data[i++] = sta->sta_state;
|
||||
|
||||
|
||||
if (sinfo.filled & STATION_INFO_TX_BITRATE)
|
||||
data[i] = 100000 *
|
||||
cfg80211_calculate_bitrate(&sinfo.txrate);
|
||||
i++;
|
||||
if (sinfo.filled & STATION_INFO_RX_BITRATE)
|
||||
data[i] = 100000 *
|
||||
cfg80211_calculate_bitrate(&sinfo.rxrate);
|
||||
i++;
|
||||
|
||||
if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
|
||||
data[i] = (u8)sinfo.signal_avg;
|
||||
i++;
|
||||
} else {
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
/* Make sure this station belongs to the proper dev */
|
||||
if (sta->sdata->dev != dev)
|
||||
continue;
|
||||
|
||||
sinfo.filled = 0;
|
||||
sta_set_sinfo(sta, &sinfo);
|
||||
i = 0;
|
||||
ADD_STA_STATS(sta);
|
||||
}
|
||||
}
|
||||
|
||||
do_survey:
|
||||
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
|
||||
/* Get survey stats for current channel */
|
||||
survey.filled = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (chanctx_conf)
|
||||
channel = chanctx_conf->def.chan;
|
||||
else
|
||||
channel = NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (channel) {
|
||||
q = 0;
|
||||
do {
|
||||
survey.filled = 0;
|
||||
if (drv_get_survey(local, q, &survey) != 0) {
|
||||
survey.filled = 0;
|
||||
break;
|
||||
}
|
||||
q++;
|
||||
} while (channel != survey.channel);
|
||||
}
|
||||
|
||||
if (survey.filled)
|
||||
data[i++] = survey.channel->center_freq;
|
||||
else
|
||||
data[i++] = 0;
|
||||
if (survey.filled & SURVEY_INFO_NOISE_DBM)
|
||||
data[i++] = (u8)survey.noise;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
|
||||
data[i++] = survey.channel_time;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
|
||||
data[i++] = survey.channel_time_busy;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
|
||||
data[i++] = survey.channel_time_ext_busy;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
|
||||
data[i++] = survey.channel_time_rx;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
|
||||
data[i++] = survey.channel_time_tx;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
if (WARN_ON(i != STA_STATS_LEN))
|
||||
return;
|
||||
|
||||
drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
|
||||
}
|
||||
|
||||
static void ieee80211_get_et_strings(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
u32 sset, u8 *data)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
int sz_sta_stats = 0;
|
||||
|
||||
if (sset == ETH_SS_STATS) {
|
||||
sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
|
||||
memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
|
||||
}
|
||||
drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
|
||||
}
|
||||
|
||||
static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, u8 *mac, struct station_info *sinfo)
|
||||
{
|
||||
|
@ -875,7 +554,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
|
|||
}
|
||||
|
||||
static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *resp, size_t resp_len)
|
||||
const u8 *resp, size_t resp_len,
|
||||
const struct ieee80211_csa_settings *csa)
|
||||
{
|
||||
struct probe_resp *new, *old;
|
||||
|
||||
|
@ -891,6 +571,11 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|||
new->len = resp_len;
|
||||
memcpy(new->data, resp, resp_len);
|
||||
|
||||
if (csa)
|
||||
memcpy(new->csa_counter_offsets, csa->counter_offsets_presp,
|
||||
csa->n_counter_offsets_presp *
|
||||
sizeof(new->csa_counter_offsets[0]));
|
||||
|
||||
rcu_assign_pointer(sdata->u.ap.probe_resp, new);
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
|
@ -899,7 +584,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
|
||||
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_beacon_data *params)
|
||||
struct cfg80211_beacon_data *params,
|
||||
const struct ieee80211_csa_settings *csa)
|
||||
{
|
||||
struct beacon_data *new, *old;
|
||||
int new_head_len, new_tail_len;
|
||||
|
@ -943,6 +629,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
new->head_len = new_head_len;
|
||||
new->tail_len = new_tail_len;
|
||||
|
||||
if (csa) {
|
||||
new->csa_current_counter = csa->count;
|
||||
memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon,
|
||||
csa->n_counter_offsets_beacon *
|
||||
sizeof(new->csa_counter_offsets[0]));
|
||||
}
|
||||
|
||||
/* copy in head */
|
||||
if (params->head)
|
||||
memcpy(new->head, params->head, new_head_len);
|
||||
|
@ -957,7 +650,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
memcpy(new->tail, old->tail, new_tail_len);
|
||||
|
||||
err = ieee80211_set_probe_resp(sdata, params->probe_resp,
|
||||
params->probe_resp_len);
|
||||
params->probe_resp_len, csa);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err == 0)
|
||||
|
@ -1042,7 +735,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
|
||||
IEEE80211_P2P_OPPPS_ENABLE_BIT;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon);
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon, NULL);
|
||||
if (err < 0) {
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
return err;
|
||||
|
@ -1090,38 +783,13 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|||
if (!old)
|
||||
return -ENOENT;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, params);
|
||||
err = ieee80211_assign_beacon(sdata, params, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
continue;
|
||||
|
||||
if (!sdata->vif.csa_active)
|
||||
continue;
|
||||
|
||||
if (!sdata->csa_block_tx)
|
||||
continue;
|
||||
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
@ -1141,10 +809,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|||
/* abort any running channel switch */
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
|
@ -1327,9 +997,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
}
|
||||
}
|
||||
|
||||
ret = sta_apply_auth_flags(local, sta, mask, set);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* auth flags will be set later for TDLS stations */
|
||||
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
||||
ret = sta_apply_auth_flags(local, sta, mask, set);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
|
||||
if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
|
||||
|
@ -1466,6 +1139,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
#endif
|
||||
}
|
||||
|
||||
/* set the STA state after all sta info from usermode has been set */
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
||||
ret = sta_apply_auth_flags(local, sta, mask, set);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3073,7 +2753,8 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
|
||||
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
|
||||
NULL);
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
sdata->u.ap.next_beacon = NULL;
|
||||
|
||||
|
@ -3111,17 +2792,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
sdata_assert_lock(sdata);
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
sdata->radar_required = sdata->csa_radar_required;
|
||||
err = ieee80211_vif_change_channel(sdata, &changed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/*
|
||||
* using reservation isn't immediate as it may be deferred until later
|
||||
* with multi-vif. once reservation is complete it will re-schedule the
|
||||
* work with no reserved_chanctx so verify chandef to check if it
|
||||
* completed successfully
|
||||
*/
|
||||
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = sdata->csa_chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
if (sdata->reserved_chanctx) {
|
||||
/*
|
||||
* with multi-vif csa driver may call ieee80211_csa_finish()
|
||||
* many times while waiting for other interfaces to use their
|
||||
* reservations
|
||||
*/
|
||||
if (sdata->reserved_ready)
|
||||
return 0;
|
||||
|
||||
err = ieee80211_vif_use_reserved_context(sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
||||
&sdata->csa_chandef))
|
||||
return -EINVAL;
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
err = ieee80211_set_after_csa_beacon(sdata, &changed);
|
||||
|
@ -3131,10 +2830,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
||||
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3157,6 +2857,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
|||
|
||||
sdata_lock(sdata);
|
||||
mutex_lock(&local->mtx);
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
|
||||
/* AP might have been stopped while waiting for the lock. */
|
||||
if (!sdata->vif.csa_active)
|
||||
|
@ -3168,6 +2869,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
|||
ieee80211_csa_finalize(sdata);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
@ -3176,6 +2878,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
struct cfg80211_csa_settings *params,
|
||||
u32 *changed)
|
||||
{
|
||||
struct ieee80211_csa_settings csa = {};
|
||||
int err;
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
|
@ -3210,20 +2913,13 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
IEEE80211_MAX_CSA_COUNTERS_NUM))
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure we don't have garbage in other counters */
|
||||
memset(sdata->csa_counter_offset_beacon, 0,
|
||||
sizeof(sdata->csa_counter_offset_beacon));
|
||||
memset(sdata->csa_counter_offset_presp, 0,
|
||||
sizeof(sdata->csa_counter_offset_presp));
|
||||
csa.counter_offsets_beacon = params->counter_offsets_beacon;
|
||||
csa.counter_offsets_presp = params->counter_offsets_presp;
|
||||
csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
|
||||
csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
|
||||
csa.count = params->count;
|
||||
|
||||
memcpy(sdata->csa_counter_offset_beacon,
|
||||
params->counter_offsets_beacon,
|
||||
params->n_counter_offsets_beacon * sizeof(u16));
|
||||
memcpy(sdata->csa_counter_offset_presp,
|
||||
params->counter_offsets_presp,
|
||||
params->n_counter_offsets_presp * sizeof(u16));
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa);
|
||||
if (err < 0) {
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
return err;
|
||||
|
@ -3319,7 +3015,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
int err, num_chanctx, changed = 0;
|
||||
int err, changed = 0;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
lockdep_assert_held(&local->mtx);
|
||||
|
@ -3334,46 +3030,50 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|||
&sdata->vif.bss_conf.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return -EBUSY;
|
||||
}
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
if (num_chanctx > 1)
|
||||
return -EBUSY;
|
||||
|
||||
/* don't allow another channel switch if one is already active. */
|
||||
if (sdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (!chanctx) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
|
||||
chanctx->mode,
|
||||
params->radar_required);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* if reservation is invalid then this will fail */
|
||||
err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
|
||||
if (err) {
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
||||
if (err) {
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdata->csa_radar_required = params->radar_required;
|
||||
sdata->csa_chandef = params->chandef;
|
||||
sdata->csa_block_tx = params->block_tx;
|
||||
sdata->csa_current_counter = params->count;
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
if (sdata->csa_block_tx)
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
ieee80211_stop_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
if (changed) {
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
@ -3383,7 +3083,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|||
ieee80211_csa_finalize(sdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
@ -3515,10 +3217,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
|
||||
params->n_csa_offsets) {
|
||||
int i;
|
||||
u8 c = sdata->csa_current_counter;
|
||||
struct beacon_data *beacon = NULL;
|
||||
|
||||
for (i = 0; i < params->n_csa_offsets; i++)
|
||||
data[params->csa_offsets[i]] = c;
|
||||
rcu_read_lock();
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
beacon = rcu_dereference(sdata->u.ap.beacon);
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
beacon = rcu_dereference(sdata->u.ibss.presp);
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
beacon = rcu_dereference(sdata->u.mesh.beacon);
|
||||
|
||||
if (beacon)
|
||||
for (i = 0; i < params->n_csa_offsets; i++)
|
||||
data[params->csa_offsets[i]] =
|
||||
beacon->csa_current_counter;
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
IEEE80211_SKB_CB(skb)->flags = flags;
|
||||
|
@ -3598,21 +3313,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
|
|||
return drv_get_antenna(local, tx_ant, rx_ant);
|
||||
}
|
||||
|
||||
static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
|
||||
return drv_set_ringparam(local, tx, rx);
|
||||
}
|
||||
|
||||
static void ieee80211_get_ringparam(struct wiphy *wiphy,
|
||||
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
|
||||
drv_get_ringparam(local, tx, tx_max, rx, rx_max);
|
||||
}
|
||||
|
||||
static int ieee80211_set_rekey_data(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_gtk_rekey_data *data)
|
||||
|
@ -3844,8 +3544,6 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|||
.mgmt_frame_register = ieee80211_mgmt_frame_register,
|
||||
.set_antenna = ieee80211_set_antenna,
|
||||
.get_antenna = ieee80211_get_antenna,
|
||||
.set_ringparam = ieee80211_set_ringparam,
|
||||
.get_ringparam = ieee80211_get_ringparam,
|
||||
.set_rekey_data = ieee80211_set_rekey_data,
|
||||
.tdls_oper = ieee80211_tdls_oper,
|
||||
.tdls_mgmt = ieee80211_tdls_mgmt,
|
||||
|
@ -3854,9 +3552,6 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|||
#ifdef CONFIG_PM
|
||||
.set_wakeup = ieee80211_set_wakeup,
|
||||
#endif
|
||||
.get_et_sset_count = ieee80211_get_et_sset_count,
|
||||
.get_et_stats = ieee80211_get_et_stats,
|
||||
.get_et_strings = ieee80211_get_et_strings,
|
||||
.get_channel = ieee80211_cfg_get_channel,
|
||||
.start_radar_detection = ieee80211_start_radar_detection,
|
||||
.channel_switch = ieee80211_channel_switch,
|
||||
|
|
|
@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
|
|||
return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
|
||||
}
|
||||
|
||||
static struct ieee80211_chanctx *
|
||||
ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
return container_of(conf, struct ieee80211_chanctx, conf);
|
||||
}
|
||||
|
||||
static const struct cfg80211_chan_def *
|
||||
ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx,
|
||||
|
@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
|
|||
return NULL;
|
||||
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
continue;
|
||||
|
||||
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
|
||||
continue;
|
||||
|
||||
|
@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
|
|||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
const struct cfg80211_chan_def *compat;
|
||||
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
|
||||
continue;
|
||||
|
||||
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
|
||||
continue;
|
||||
|
||||
|
@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
bool use_reserved_switch = false;
|
||||
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
|
@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
|
||||
if (sdata->reserved_chanctx)
|
||||
if (sdata->reserved_chanctx) {
|
||||
if (sdata->reserved_chanctx->replace_state ==
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER &&
|
||||
ieee80211_chanctx_num_reserved(local,
|
||||
sdata->reserved_chanctx) > 1)
|
||||
use_reserved_switch = true;
|
||||
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
}
|
||||
|
||||
ieee80211_assign_vif_chanctx(sdata, NULL);
|
||||
if (ieee80211_chanctx_refcount(local, ctx) == 0)
|
||||
ieee80211_free_chanctx(local, ctx);
|
||||
|
||||
/* Unreserving may ready an in-place reservation. */
|
||||
if (use_reserved_switch)
|
||||
ieee80211_vif_use_reserved_switch(local);
|
||||
}
|
||||
|
||||
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||
|
@ -787,70 +819,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_chanctx *ctx,
|
||||
u32 *changed)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
|
||||
u32 chanctx_changed = 0;
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
|
||||
if (ieee80211_chanctx_refcount(local, ctx) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width != chandef->width) {
|
||||
chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
|
||||
*changed |= BSS_CHANGED_BANDWIDTH;
|
||||
}
|
||||
|
||||
sdata->vif.bss_conf.chandef = *chandef;
|
||||
ctx->conf.def = *chandef;
|
||||
|
||||
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
|
||||
drv_change_chanctx(local, ctx, chanctx_changed);
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
ieee80211_recalc_chanctx_min_def(local, ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
u32 *changed)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
|
||||
/* should never be called if not performing a channel switch. */
|
||||
if (WARN_ON(!sdata->vif.csa_active))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
|
||||
ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
|
||||
bool clear)
|
||||
|
@ -905,8 +873,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
|
|||
list_del(&sdata->reserved_chanctx_list);
|
||||
sdata->reserved_chanctx = NULL;
|
||||
|
||||
if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
|
||||
ieee80211_free_chanctx(sdata->local, ctx);
|
||||
if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
|
||||
if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
|
||||
if (WARN_ON(!ctx->replace_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON(ctx->replace_ctx->replace_state !=
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED);
|
||||
WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
|
||||
|
||||
ctx->replace_ctx->replace_ctx = NULL;
|
||||
ctx->replace_ctx->replace_state =
|
||||
IEEE80211_CHANCTX_REPLACE_NONE;
|
||||
|
||||
list_del_rcu(&ctx->list);
|
||||
kfree_rcu(ctx, rcu_head);
|
||||
} else {
|
||||
ieee80211_free_chanctx(sdata->local, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -917,40 +902,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
|
|||
bool radar_required)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *new_ctx, *curr_ctx;
|
||||
int ret = 0;
|
||||
struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
curr_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
|
||||
return -ENOTSUPP;
|
||||
|
||||
new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
|
||||
if (!new_ctx) {
|
||||
if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
|
||||
(local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
|
||||
/* if we're the only users of the chanctx and
|
||||
* the driver supports changing a running
|
||||
* context, reserve our current context
|
||||
*/
|
||||
new_ctx = curr_ctx;
|
||||
} else if (ieee80211_can_create_new_chanctx(local)) {
|
||||
/* create a new context and reserve it */
|
||||
if (ieee80211_can_create_new_chanctx(local)) {
|
||||
new_ctx = ieee80211_new_chanctx(local, chandef, mode);
|
||||
if (IS_ERR(new_ctx)) {
|
||||
ret = PTR_ERR(new_ctx);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(new_ctx))
|
||||
return PTR_ERR(new_ctx);
|
||||
} else {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
if (!curr_ctx ||
|
||||
(curr_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
!list_empty(&curr_ctx->reserved_vifs)) {
|
||||
/*
|
||||
* Another vif already requested this context
|
||||
* for a reservation. Find another one hoping
|
||||
* all vifs assigned to it will also switch
|
||||
* soon enough.
|
||||
*
|
||||
* TODO: This needs a little more work as some
|
||||
* cases (more than 2 chanctx capable devices)
|
||||
* may fail which could otherwise succeed
|
||||
* provided some channel context juggling was
|
||||
* performed.
|
||||
*
|
||||
* Consider ctx1..3, vif1..6, each ctx has 2
|
||||
* vifs. vif1 and vif2 from ctx1 request new
|
||||
* different chandefs starting 2 in-place
|
||||
* reserations with ctx4 and ctx5 replacing
|
||||
* ctx1 and ctx2 respectively. Next vif5 and
|
||||
* vif6 from ctx3 reserve ctx4. If vif3 and
|
||||
* vif4 remain on ctx2 as they are then this
|
||||
* fails unless `replace_ctx` from ctx5 is
|
||||
* replaced with ctx3.
|
||||
*/
|
||||
list_for_each_entry(ctx, &local->chanctx_list,
|
||||
list) {
|
||||
if (ctx->replace_state !=
|
||||
IEEE80211_CHANCTX_REPLACE_NONE)
|
||||
continue;
|
||||
|
||||
if (!list_empty(&ctx->reserved_vifs))
|
||||
continue;
|
||||
|
||||
curr_ctx = ctx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If that's true then all available contexts already
|
||||
* have reservations and cannot be used.
|
||||
*/
|
||||
if (!curr_ctx ||
|
||||
(curr_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
!list_empty(&curr_ctx->reserved_vifs))
|
||||
return -EBUSY;
|
||||
|
||||
new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
|
||||
if (!new_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
new_ctx->replace_ctx = curr_ctx;
|
||||
new_ctx->replace_state =
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER;
|
||||
|
||||
curr_ctx->replace_ctx = new_ctx;
|
||||
curr_ctx->replace_state =
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED;
|
||||
|
||||
list_add_rcu(&new_ctx->list, &local->chanctx_list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -958,82 +987,601 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
|
|||
sdata->reserved_chanctx = new_ctx;
|
||||
sdata->reserved_chandef = *chandef;
|
||||
sdata->reserved_radar_required = radar_required;
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
sdata->reserved_ready = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
|
||||
u32 *changed)
|
||||
static void
|
||||
ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->csa_finalize_work);
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.mgd.chswitch_work);
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
struct ieee80211_chanctx *old_ctx;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
int ret;
|
||||
u32 tmp_changed = *changed;
|
||||
|
||||
/* TODO: need to recheck if the chandef is usable etc.? */
|
||||
struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
|
||||
struct ieee80211_chanctx *old_ctx, *new_ctx;
|
||||
const struct cfg80211_chan_def *chandef;
|
||||
u32 changed = 0;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
new_ctx = sdata->reserved_chanctx;
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
|
||||
if (WARN_ON(!sdata->reserved_ready))
|
||||
return -EBUSY;
|
||||
|
||||
if (WARN_ON(!new_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(!old_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(new_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER))
|
||||
return -EINVAL;
|
||||
|
||||
chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
|
||||
&sdata->reserved_chandef);
|
||||
if (WARN_ON(!chandef))
|
||||
return -EINVAL;
|
||||
|
||||
vif_chsw[0].vif = &sdata->vif;
|
||||
vif_chsw[0].old_ctx = &old_ctx->conf;
|
||||
vif_chsw[0].new_ctx = &new_ctx->conf;
|
||||
|
||||
list_del(&sdata->reserved_chanctx_list);
|
||||
sdata->reserved_chanctx = NULL;
|
||||
|
||||
err = drv_switch_vif_chanctx(local, vif_chsw, 1,
|
||||
CHANCTX_SWMODE_REASSIGN_VIF);
|
||||
if (err) {
|
||||
if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
|
||||
ieee80211_free_chanctx(local, new_ctx);
|
||||
|
||||
ctx = sdata->reserved_chanctx;
|
||||
if (WARN_ON(!ctx)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
|
||||
rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
|
||||
|
||||
old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
|
||||
|
||||
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
|
||||
ieee80211_free_chanctx(local, old_ctx);
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
|
||||
tmp_changed |= BSS_CHANGED_BANDWIDTH;
|
||||
changed = BSS_CHANGED_BANDWIDTH;
|
||||
|
||||
sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
|
||||
|
||||
/* unref our reservation */
|
||||
sdata->reserved_chanctx = NULL;
|
||||
sdata->radar_required = sdata->reserved_radar_required;
|
||||
list_del(&sdata->reserved_chanctx_list);
|
||||
if (changed)
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
if (old_ctx == ctx) {
|
||||
/* This is our own context, just change it */
|
||||
ret = __ieee80211_vif_change_channel(sdata, old_ctx,
|
||||
&tmp_changed);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ret = ieee80211_assign_vif_chanctx(sdata, ctx);
|
||||
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
|
||||
ieee80211_free_chanctx(local, old_ctx);
|
||||
if (ret) {
|
||||
/* if assign fails refcount stays the same */
|
||||
if (ieee80211_chanctx_refcount(local, ctx) == 0)
|
||||
ieee80211_free_chanctx(local, ctx);
|
||||
out:
|
||||
ieee80211_vif_chanctx_reservation_complete(sdata);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx *old_ctx, *new_ctx;
|
||||
const struct cfg80211_chan_def *chandef;
|
||||
int err;
|
||||
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
new_ctx = sdata->reserved_chanctx;
|
||||
|
||||
if (WARN_ON(!sdata->reserved_ready))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(old_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(!new_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(new_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER))
|
||||
return -EINVAL;
|
||||
|
||||
chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
|
||||
&sdata->reserved_chandef);
|
||||
if (WARN_ON(!chandef))
|
||||
return -EINVAL;
|
||||
|
||||
list_del(&sdata->reserved_chanctx_list);
|
||||
sdata->reserved_chanctx = NULL;
|
||||
|
||||
err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
|
||||
if (err) {
|
||||
if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
|
||||
ieee80211_free_chanctx(local, new_ctx);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
ieee80211_vif_chanctx_reservation_complete(sdata);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_chanctx *old_ctx, *new_ctx;
|
||||
|
||||
lockdep_assert_held(&sdata->local->chanctx_mtx);
|
||||
|
||||
new_ctx = sdata->reserved_chanctx;
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
|
||||
if (!old_ctx)
|
||||
return false;
|
||||
|
||||
if (WARN_ON(!new_ctx))
|
||||
return false;
|
||||
|
||||
if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
return false;
|
||||
|
||||
if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *new_ctx)
|
||||
{
|
||||
const struct cfg80211_chan_def *chandef;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
|
||||
if (WARN_ON(!chandef))
|
||||
return -EINVAL;
|
||||
|
||||
local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
|
||||
local->_oper_chandef = *chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
|
||||
int n_vifs)
|
||||
{
|
||||
struct ieee80211_vif_chanctx_switch *vif_chsw;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_chanctx *ctx, *old_ctx;
|
||||
int i, err;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL);
|
||||
if (!vif_chsw)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
if (WARN_ON(!ctx->replace_ctx)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
__ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
|
||||
list_for_each_entry(sdata, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
if (!ieee80211_vif_has_in_place_reservation(
|
||||
sdata))
|
||||
continue;
|
||||
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
vif_chsw[i].vif = &sdata->vif;
|
||||
vif_chsw[i].old_ctx = &old_ctx->conf;
|
||||
vif_chsw[i].new_ctx = &ctx->conf;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
*changed = tmp_changed;
|
||||
err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
|
||||
CHANCTX_SWMODE_SWAP_CONTEXTS);
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
ieee80211_recalc_chanctx_min_def(local, ctx);
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
kfree(vif_chsw);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
if (!list_empty(&ctx->replace_ctx->assigned_vifs))
|
||||
continue;
|
||||
|
||||
ieee80211_del_chanctx(local, ctx->replace_ctx);
|
||||
err = ieee80211_add_chanctx(local, ctx);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
WARN_ON(ieee80211_add_chanctx(local, ctx));
|
||||
list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
if (!list_empty(&ctx->replace_ctx->assigned_vifs))
|
||||
continue;
|
||||
|
||||
ieee80211_del_chanctx(local, ctx);
|
||||
WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata, *sdata_tmp;
|
||||
struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
|
||||
struct ieee80211_chanctx *new_ctx = NULL;
|
||||
int i, err, n_assigned, n_reserved, n_ready;
|
||||
int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
/*
|
||||
* If there are 2 independent pairs of channel contexts performing
|
||||
* cross-switch of their vifs this code will still wait until both are
|
||||
* ready even though it could be possible to switch one before the
|
||||
* other is ready.
|
||||
*
|
||||
* For practical reasons and code simplicity just do a single huge
|
||||
* switch.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Verify if the reservation is still feasible.
|
||||
* - if it's not then disconnect
|
||||
* - if it is but not all vifs necessary are ready then defer
|
||||
*/
|
||||
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
if (WARN_ON(!ctx->replace_ctx)) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!local->use_chanctx)
|
||||
new_ctx = ctx;
|
||||
|
||||
n_ctx++;
|
||||
|
||||
n_assigned = 0;
|
||||
n_reserved = 0;
|
||||
n_ready = 0;
|
||||
|
||||
list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
|
||||
assigned_chanctx_list) {
|
||||
n_assigned++;
|
||||
if (sdata->reserved_chanctx) {
|
||||
n_reserved++;
|
||||
if (sdata->reserved_ready)
|
||||
n_ready++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_assigned != n_reserved) {
|
||||
if (n_ready == n_reserved) {
|
||||
wiphy_info(local->hw.wiphy,
|
||||
"channel context reservation cannot be finalized because some interfaces aren't switching\n");
|
||||
err = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
ctx->conf.radar_enabled = false;
|
||||
list_for_each_entry(sdata, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
if (ieee80211_vif_has_in_place_reservation(sdata) &&
|
||||
!sdata->reserved_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
if (old_ctx) {
|
||||
if (old_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
n_vifs_switch++;
|
||||
else
|
||||
n_vifs_assign++;
|
||||
} else {
|
||||
n_vifs_ctxless++;
|
||||
}
|
||||
|
||||
if (sdata->reserved_radar_required)
|
||||
ctx->conf.radar_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(n_ctx == 0) ||
|
||||
WARN_ON(n_vifs_switch == 0 &&
|
||||
n_vifs_assign == 0 &&
|
||||
n_vifs_ctxless == 0) ||
|
||||
WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
|
||||
WARN_ON(!new_ctx && !local->use_chanctx)) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* All necessary vifs are ready. Perform the switch now depending on
|
||||
* reservations and driver capabilities.
|
||||
*/
|
||||
|
||||
if (local->use_chanctx) {
|
||||
if (n_vifs_switch > 0) {
|
||||
err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
|
||||
err = ieee80211_chsw_switch_ctxs(local);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
err = ieee80211_chsw_switch_hwconf(local, new_ctx);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update all structures, values and pointers to point to new channel
|
||||
* context(s).
|
||||
*/
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
if (WARN_ON(!ctx->replace_ctx)) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_for_each_entry(sdata, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
u32 changed = 0;
|
||||
|
||||
if (!ieee80211_vif_has_in_place_reservation(sdata))
|
||||
continue;
|
||||
|
||||
rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
__ieee80211_vif_copy_chanctx_to_vlans(sdata,
|
||||
false);
|
||||
|
||||
sdata->radar_required = sdata->reserved_radar_required;
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width !=
|
||||
sdata->reserved_chandef.width)
|
||||
changed = BSS_CHANGED_BANDWIDTH;
|
||||
|
||||
sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
|
||||
if (changed)
|
||||
ieee80211_bss_info_change_notify(sdata,
|
||||
changed);
|
||||
|
||||
ieee80211_recalc_txpower(sdata);
|
||||
}
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
ieee80211_recalc_chanctx_min_def(local, ctx);
|
||||
|
||||
list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
if (ieee80211_vif_get_chanctx(sdata) != ctx)
|
||||
continue;
|
||||
|
||||
list_del(&sdata->reserved_chanctx_list);
|
||||
list_move(&sdata->assigned_chanctx_list,
|
||||
&new_ctx->assigned_vifs);
|
||||
sdata->reserved_chanctx = NULL;
|
||||
|
||||
ieee80211_vif_chanctx_reservation_complete(sdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* This context might have been a dependency for an already
|
||||
* ready re-assign reservation interface that was deferred. Do
|
||||
* not propagate error to the caller though. The in-place
|
||||
* reservation for originally requested interface has already
|
||||
* succeeded at this point.
|
||||
*/
|
||||
list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
if (WARN_ON(ieee80211_vif_has_in_place_reservation(
|
||||
sdata)))
|
||||
continue;
|
||||
|
||||
if (WARN_ON(sdata->reserved_chanctx != ctx))
|
||||
continue;
|
||||
|
||||
if (!sdata->reserved_ready)
|
||||
continue;
|
||||
|
||||
if (ieee80211_vif_get_chanctx(sdata))
|
||||
err = ieee80211_vif_use_reserved_reassign(
|
||||
sdata);
|
||||
else
|
||||
err = ieee80211_vif_use_reserved_assign(sdata);
|
||||
|
||||
if (err) {
|
||||
sdata_info(sdata,
|
||||
"failed to finalize (re-)assign reservation (err=%d)\n",
|
||||
err);
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
cfg80211_stop_iface(local->hw.wiphy,
|
||||
&sdata->wdev,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally free old contexts
|
||||
*/
|
||||
|
||||
list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
continue;
|
||||
|
||||
ctx->replace_ctx->replace_ctx = NULL;
|
||||
ctx->replace_ctx->replace_state =
|
||||
IEEE80211_CHANCTX_REPLACE_NONE;
|
||||
|
||||
list_del_rcu(&ctx->list);
|
||||
kfree_rcu(ctx, rcu_head);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
|
||||
reserved_chanctx_list) {
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
ieee80211_vif_chanctx_reservation_complete(sdata);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx *new_ctx;
|
||||
struct ieee80211_chanctx *old_ctx;
|
||||
int err;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
new_ctx = sdata->reserved_chanctx;
|
||||
old_ctx = ieee80211_vif_get_chanctx(sdata);
|
||||
|
||||
if (WARN_ON(!new_ctx))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(new_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(sdata->reserved_ready))
|
||||
return -EINVAL;
|
||||
|
||||
sdata->reserved_ready = true;
|
||||
|
||||
if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
|
||||
if (old_ctx)
|
||||
err = ieee80211_vif_use_reserved_reassign(sdata);
|
||||
else
|
||||
err = ieee80211_vif_use_reserved_assign(sdata);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* In-place reservation may need to be finalized now either if:
|
||||
* a) sdata is taking part in the swapping itself and is the last one
|
||||
* b) sdata has switched with a re-assign reservation to an existing
|
||||
* context readying in-place switching of old_ctx
|
||||
*
|
||||
* In case of (b) do not propagate the error up because the requested
|
||||
* sdata already switched successfully. Just spill an extra warning.
|
||||
* The ieee80211_vif_use_reserved_switch() already stops all necessary
|
||||
* interfaces upon failure.
|
||||
*/
|
||||
if ((old_ctx &&
|
||||
old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
|
||||
err = ieee80211_vif_use_reserved_switch(local);
|
||||
if (err && err != -EAGAIN) {
|
||||
if (new_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
return err;
|
||||
|
||||
wiphy_info(local->hw.wiphy,
|
||||
"depending in-place reservation failed (err=%d)\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -1043,6 +1591,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
const struct cfg80211_chan_def *compat;
|
||||
int ret;
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
||||
|
@ -1069,11 +1618,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
|
||||
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
|
||||
|
||||
compat = cfg80211_chandef_compatible(&conf->def, chandef);
|
||||
if (!compat) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (ctx->replace_state) {
|
||||
case IEEE80211_CHANCTX_REPLACE_NONE:
|
||||
if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
|
||||
/* TODO: Perhaps the bandwith change could be treated as a
|
||||
* reservation itself? */
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
case IEEE80211_CHANCTX_REPLACES_OTHER:
|
||||
/* channel context that is going to replace another channel
|
||||
* context doesn't really exist and shouldn't be assigned
|
||||
* anywhere yet */
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
sdata->vif.bss_conf.chandef = *chandef;
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
|
|
|
@ -124,7 +124,7 @@ static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf,
|
|||
long connected_time_secs;
|
||||
char buf[100];
|
||||
int res;
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
ktime_get_ts(&uptime);
|
||||
connected_time_secs = uptime.tv_sec - sta->last_connected;
|
||||
time_to_tm(connected_time_secs, 0, &result);
|
||||
result.tm_year -= 70;
|
||||
|
@ -587,7 +587,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count);
|
||||
DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
|
||||
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
|
||||
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
|
||||
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
|
|
|
@ -314,7 +314,7 @@ 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)
|
||||
struct ieee80211_scan_request *req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -346,7 +346,7 @@ static inline int
|
|||
drv_sched_scan_start(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_sched_scan_request *req,
|
||||
struct ieee80211_sched_scan_ies *ies)
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
|
|||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void
|
||||
drv_mgd_protect_tdls_discover(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return;
|
||||
WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||
|
||||
trace_drv_mgd_protect_tdls_discover(local, sdata);
|
||||
if (local->ops->mgd_protect_tdls_discover)
|
||||
local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline int drv_add_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* mac80211 ethtool hooks for cfg80211
|
||||
*
|
||||
* Copied from cfg.c - originally
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2014 Intel Corporation (Author: Johannes Berg)
|
||||
*
|
||||
* This file is GPLv2 as found in COPYING.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "sta_info.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
static int ieee80211_set_ringparam(struct net_device *dev,
|
||||
struct ethtool_ringparam *rp)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
|
||||
|
||||
if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending);
|
||||
}
|
||||
|
||||
static void ieee80211_get_ringparam(struct net_device *dev,
|
||||
struct ethtool_ringparam *rp)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
|
||||
|
||||
memset(rp, 0, sizeof(*rp));
|
||||
|
||||
drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending,
|
||||
&rp->rx_pending, &rp->rx_max_pending);
|
||||
}
|
||||
|
||||
static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
|
||||
"rx_packets", "rx_bytes",
|
||||
"rx_duplicates", "rx_fragments", "rx_dropped",
|
||||
"tx_packets", "tx_bytes", "tx_fragments",
|
||||
"tx_filtered", "tx_retry_failed", "tx_retries",
|
||||
"beacon_loss", "sta_state", "txrate", "rxrate", "signal",
|
||||
"channel", "noise", "ch_time", "ch_time_busy",
|
||||
"ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
|
||||
};
|
||||
#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats)
|
||||
|
||||
static int ieee80211_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
int rv = 0;
|
||||
|
||||
if (sset == ETH_SS_STATS)
|
||||
rv += STA_STATS_LEN;
|
||||
|
||||
rv += drv_get_et_sset_count(sdata, sset);
|
||||
|
||||
if (rv == 0)
|
||||
return -EOPNOTSUPP;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void ieee80211_get_stats(struct net_device *dev,
|
||||
struct ethtool_stats *stats,
|
||||
u64 *data)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_channel *channel;
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct station_info sinfo;
|
||||
struct survey_info survey;
|
||||
int i, q;
|
||||
#define STA_STATS_SURVEY_LEN 7
|
||||
|
||||
memset(data, 0, sizeof(u64) * STA_STATS_LEN);
|
||||
|
||||
#define ADD_STA_STATS(sta) \
|
||||
do { \
|
||||
data[i++] += sta->rx_packets; \
|
||||
data[i++] += sta->rx_bytes; \
|
||||
data[i++] += sta->num_duplicates; \
|
||||
data[i++] += sta->rx_fragments; \
|
||||
data[i++] += sta->rx_dropped; \
|
||||
\
|
||||
data[i++] += sinfo.tx_packets; \
|
||||
data[i++] += sinfo.tx_bytes; \
|
||||
data[i++] += sta->tx_fragments; \
|
||||
data[i++] += sta->tx_filtered_count; \
|
||||
data[i++] += sta->tx_retry_failed; \
|
||||
data[i++] += sta->tx_retry_count; \
|
||||
data[i++] += sta->beacon_loss_count; \
|
||||
} while (0)
|
||||
|
||||
/* For Managed stations, find the single station based on BSSID
|
||||
* and use that. For interface types, iterate through all available
|
||||
* stations and add stats for any station that is assigned to this
|
||||
* network device.
|
||||
*/
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
|
||||
|
||||
if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
|
||||
goto do_survey;
|
||||
|
||||
sinfo.filled = 0;
|
||||
sta_set_sinfo(sta, &sinfo);
|
||||
|
||||
i = 0;
|
||||
ADD_STA_STATS(sta);
|
||||
|
||||
data[i++] = sta->sta_state;
|
||||
|
||||
|
||||
if (sinfo.filled & STATION_INFO_TX_BITRATE)
|
||||
data[i] = 100000 *
|
||||
cfg80211_calculate_bitrate(&sinfo.txrate);
|
||||
i++;
|
||||
if (sinfo.filled & STATION_INFO_RX_BITRATE)
|
||||
data[i] = 100000 *
|
||||
cfg80211_calculate_bitrate(&sinfo.rxrate);
|
||||
i++;
|
||||
|
||||
if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
|
||||
data[i] = (u8)sinfo.signal_avg;
|
||||
i++;
|
||||
} else {
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
/* Make sure this station belongs to the proper dev */
|
||||
if (sta->sdata->dev != dev)
|
||||
continue;
|
||||
|
||||
sinfo.filled = 0;
|
||||
sta_set_sinfo(sta, &sinfo);
|
||||
i = 0;
|
||||
ADD_STA_STATS(sta);
|
||||
}
|
||||
}
|
||||
|
||||
do_survey:
|
||||
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
|
||||
/* Get survey stats for current channel */
|
||||
survey.filled = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (chanctx_conf)
|
||||
channel = chanctx_conf->def.chan;
|
||||
else
|
||||
channel = NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (channel) {
|
||||
q = 0;
|
||||
do {
|
||||
survey.filled = 0;
|
||||
if (drv_get_survey(local, q, &survey) != 0) {
|
||||
survey.filled = 0;
|
||||
break;
|
||||
}
|
||||
q++;
|
||||
} while (channel != survey.channel);
|
||||
}
|
||||
|
||||
if (survey.filled)
|
||||
data[i++] = survey.channel->center_freq;
|
||||
else
|
||||
data[i++] = 0;
|
||||
if (survey.filled & SURVEY_INFO_NOISE_DBM)
|
||||
data[i++] = (u8)survey.noise;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
|
||||
data[i++] = survey.channel_time;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
|
||||
data[i++] = survey.channel_time_busy;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
|
||||
data[i++] = survey.channel_time_ext_busy;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
|
||||
data[i++] = survey.channel_time_rx;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
|
||||
data[i++] = survey.channel_time_tx;
|
||||
else
|
||||
data[i++] = -1LL;
|
||||
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
if (WARN_ON(i != STA_STATS_LEN))
|
||||
return;
|
||||
|
||||
drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
|
||||
}
|
||||
|
||||
static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
int sz_sta_stats = 0;
|
||||
|
||||
if (sset == ETH_SS_STATS) {
|
||||
sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
|
||||
memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
|
||||
}
|
||||
drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
|
||||
}
|
||||
|
||||
static int ieee80211_get_regs_len(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ieee80211_get_regs(struct net_device *dev,
|
||||
struct ethtool_regs *regs,
|
||||
void *data)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
regs->version = wdev->wiphy->hw_version;
|
||||
regs->len = 0;
|
||||
}
|
||||
|
||||
const struct ethtool_ops ieee80211_ethtool_ops = {
|
||||
.get_drvinfo = cfg80211_get_drvinfo,
|
||||
.get_regs_len = ieee80211_get_regs_len,
|
||||
.get_regs = ieee80211_get_regs,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = ieee80211_get_ringparam,
|
||||
.set_ringparam = ieee80211_set_ringparam,
|
||||
.get_strings = ieee80211_get_strings,
|
||||
.get_ethtool_stats = ieee80211_get_stats,
|
||||
.get_sset_count = ieee80211_get_sset_count,
|
||||
};
|
|
@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
|||
*pos++ = csa_settings->block_tx ? 1 : 0;
|
||||
*pos++ = ieee80211_frequency_to_channel(
|
||||
csa_settings->chandef.chan->center_freq);
|
||||
sdata->csa_counter_offset_beacon[0] = (pos - presp->head);
|
||||
presp->csa_counter_offsets[0] = (pos - presp->head);
|
||||
*pos++ = csa_settings->count;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,16 +229,29 @@ struct ieee80211_rx_data {
|
|||
u16 tkip_iv16;
|
||||
};
|
||||
|
||||
struct ieee80211_csa_settings {
|
||||
const u16 *counter_offsets_beacon;
|
||||
const u16 *counter_offsets_presp;
|
||||
|
||||
int n_counter_offsets_beacon;
|
||||
int n_counter_offsets_presp;
|
||||
|
||||
u8 count;
|
||||
};
|
||||
|
||||
struct beacon_data {
|
||||
u8 *head, *tail;
|
||||
int head_len, tail_len;
|
||||
struct ieee80211_meshconf_ie *meshconf;
|
||||
u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
|
||||
u8 csa_current_counter;
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
struct probe_resp {
|
||||
struct rcu_head rcu_head;
|
||||
int len;
|
||||
u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
|
@ -688,6 +701,24 @@ enum ieee80211_chanctx_mode {
|
|||
IEEE80211_CHANCTX_EXCLUSIVE
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ieee80211_chanctx_replace_state - channel context replacement state
|
||||
*
|
||||
* This is used for channel context in-place reservations that require channel
|
||||
* context switch/swap.
|
||||
*
|
||||
* @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place
|
||||
* @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced
|
||||
* by a (not yet registered) channel context pointed by %replace_ctx.
|
||||
* @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context
|
||||
* replaces an existing channel context pointed to by %replace_ctx.
|
||||
*/
|
||||
enum ieee80211_chanctx_replace_state {
|
||||
IEEE80211_CHANCTX_REPLACE_NONE,
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED,
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER,
|
||||
};
|
||||
|
||||
struct ieee80211_chanctx {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu_head;
|
||||
|
@ -695,6 +726,9 @@ struct ieee80211_chanctx {
|
|||
struct list_head assigned_vifs;
|
||||
struct list_head reserved_vifs;
|
||||
|
||||
enum ieee80211_chanctx_replace_state replace_state;
|
||||
struct ieee80211_chanctx *replace_ctx;
|
||||
|
||||
enum ieee80211_chanctx_mode mode;
|
||||
bool driver_present;
|
||||
|
||||
|
@ -754,9 +788,6 @@ struct ieee80211_sub_if_data {
|
|||
struct mac80211_qos_map __rcu *qos_map;
|
||||
|
||||
struct work_struct csa_finalize_work;
|
||||
u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM];
|
||||
u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];
|
||||
bool csa_radar_required;
|
||||
bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
|
||||
struct cfg80211_chan_def csa_chandef;
|
||||
|
||||
|
@ -767,7 +798,7 @@ struct ieee80211_sub_if_data {
|
|||
struct ieee80211_chanctx *reserved_chanctx;
|
||||
struct cfg80211_chan_def reserved_chandef;
|
||||
bool reserved_radar_required;
|
||||
u8 csa_current_counter;
|
||||
bool reserved_ready;
|
||||
|
||||
/* used to reconfigure hardware SM PS */
|
||||
struct work_struct recalc_smps;
|
||||
|
@ -784,6 +815,9 @@ struct ieee80211_sub_if_data {
|
|||
bool radar_required;
|
||||
struct delayed_work dfs_cac_timer_work;
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
|
||||
/*
|
||||
* AP this belongs to: self in AP mode and
|
||||
* corresponding AP in VLAN mode, NULL for
|
||||
|
@ -912,6 +946,9 @@ enum queue_stop_reason {
|
|||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
||||
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
|
||||
|
||||
IEEE80211_QUEUE_STOP_REASONS,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MAC80211_LEDS
|
||||
|
@ -1008,6 +1045,7 @@ struct ieee80211_local {
|
|||
struct workqueue_struct *workqueue;
|
||||
|
||||
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
|
||||
int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS];
|
||||
/* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
|
||||
spinlock_t queue_stop_reason_lock;
|
||||
|
||||
|
@ -1135,7 +1173,8 @@ struct ieee80211_local {
|
|||
unsigned long scanning;
|
||||
struct cfg80211_ssid scan_ssid;
|
||||
struct cfg80211_scan_request *int_scan_req;
|
||||
struct cfg80211_scan_request *scan_req, *hw_scan_req;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct ieee80211_scan_request *hw_scan_req;
|
||||
struct cfg80211_chan_def scan_chandef;
|
||||
enum ieee80211_band hw_scan_band;
|
||||
int scan_channel_idx;
|
||||
|
@ -1476,7 +1515,6 @@ void ieee80211_sw_roc_work(struct work_struct *work);
|
|||
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
|
||||
|
||||
/* channel switch handling */
|
||||
bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);
|
||||
void ieee80211_csa_finalize_work(struct work_struct *work);
|
||||
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params);
|
||||
|
@ -1705,14 +1743,24 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||
unsigned long queues,
|
||||
enum queue_stop_reason reason);
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted);
|
||||
void ieee80211_stop_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum queue_stop_reason reason);
|
||||
void ieee80211_wake_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum queue_stop_reason reason);
|
||||
void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
|
||||
unsigned long queues,
|
||||
enum queue_stop_reason reason);
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted);
|
||||
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason);
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted);
|
||||
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason);
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted);
|
||||
void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
|
||||
void ieee80211_add_pending_skb(struct ieee80211_local *local,
|
||||
struct sk_buff *skb);
|
||||
|
@ -1730,8 +1778,10 @@ 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,
|
||||
size_t buffer_len, const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
size_t buffer_len,
|
||||
struct ieee80211_scan_ies *ie_desc,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u8 bands_used, u32 *rate_masks,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *dst, u32 ratemask,
|
||||
|
@ -1791,18 +1841,14 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
|
|||
enum ieee80211_chanctx_mode mode,
|
||||
bool radar_required);
|
||||
int __must_check
|
||||
ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
|
||||
u32 *changed);
|
||||
ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local);
|
||||
|
||||
int __must_check
|
||||
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed);
|
||||
/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
|
||||
int __must_check
|
||||
ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
u32 *changed);
|
||||
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -1842,11 +1888,14 @@ int ieee80211_max_num_channels(struct ieee80211_local *local);
|
|||
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *extra_ies, size_t extra_ies_len);
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len);
|
||||
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, enum nl80211_tdls_operation oper);
|
||||
|
||||
|
||||
extern const struct ethtool_ops ieee80211_ethtool_ops;
|
||||
|
||||
#ifdef CONFIG_MAC80211_NOINLINE
|
||||
#define debug_noinline noinline
|
||||
#else
|
||||
|
@ -1854,3 +1903,4 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|||
#endif
|
||||
|
||||
#endif /* IEEE80211_I_H */
|
||||
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
|
||||
|
|
|
@ -841,10 +841,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
|||
sdata_lock(sdata);
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
sdata_unlock(sdata);
|
||||
|
||||
|
@ -1671,6 +1672,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
|||
ieee80211_dfs_cac_timer_work);
|
||||
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
|
||||
ieee80211_delayed_tailroom_dec);
|
||||
INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
|
||||
ieee80211_tdls_peer_del_work);
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
@ -1705,6 +1708,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
|||
|
||||
ndev->features |= local->hw.netdev_features;
|
||||
|
||||
netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
|
||||
|
||||
ret = register_netdevice(ndev);
|
||||
if (ret) {
|
||||
free_netdev(ndev);
|
||||
|
|
|
@ -272,7 +272,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
|
|||
|
||||
/* use this reason, ieee80211_reconfig will unblock it */
|
||||
ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
||||
false);
|
||||
|
||||
/*
|
||||
* Stop all Rx during the reconfig. We don't want state changes
|
||||
|
@ -1187,18 +1188,12 @@ static int __init ieee80211_init(void)
|
|||
if (ret)
|
||||
goto err_minstrel;
|
||||
|
||||
ret = rc80211_pid_init();
|
||||
if (ret)
|
||||
goto err_pid;
|
||||
|
||||
ret = ieee80211_iface_init();
|
||||
if (ret)
|
||||
goto err_netdev;
|
||||
|
||||
return 0;
|
||||
err_netdev:
|
||||
rc80211_pid_exit();
|
||||
err_pid:
|
||||
rc80211_minstrel_ht_exit();
|
||||
err_minstrel:
|
||||
rc80211_minstrel_exit();
|
||||
|
@ -1208,7 +1203,6 @@ static int __init ieee80211_init(void)
|
|||
|
||||
static void __exit ieee80211_exit(void)
|
||||
{
|
||||
rc80211_pid_exit();
|
||||
rc80211_minstrel_ht_exit();
|
||||
rc80211_minstrel_exit();
|
||||
|
||||
|
|
|
@ -679,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
|||
*pos++ = 0x0;
|
||||
*pos++ = ieee80211_frequency_to_channel(
|
||||
csa->settings.chandef.chan->center_freq);
|
||||
sdata->csa_counter_offset_beacon[0] = hdr_len + 6;
|
||||
bcn->csa_counter_offsets[0] = hdr_len + 6;
|
||||
*pos++ = csa->settings.count;
|
||||
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
|
||||
*pos++ = 6;
|
||||
|
@ -1122,7 +1122,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
|
|||
mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
|
||||
|
||||
/* offset_ttl is based on whether the secondary channel
|
||||
* offset is available or not. Substract 1 from the mesh TTL
|
||||
* offset is available or not. Subtract 1 from the mesh TTL
|
||||
* and disable the initiator flag before forwarding.
|
||||
*/
|
||||
offset_ttl = (len < 42) ? 7 : 10;
|
||||
|
|
|
@ -551,11 +551,30 @@ static void mesh_plink_timer(unsigned long data)
|
|||
return;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
if (sta->ignore_plink_timer) {
|
||||
sta->ignore_plink_timer = false;
|
||||
|
||||
/* If a timer fires just before a state transition on another CPU,
|
||||
* we may have already extended the timeout and changed state by the
|
||||
* time we've acquired the lock and arrived here. In that case,
|
||||
* skip this timer and wait for the new one.
|
||||
*/
|
||||
if (time_before(jiffies, sta->plink_timer.expires)) {
|
||||
mpl_dbg(sta->sdata,
|
||||
"Ignoring timer for %pM in state %s (timer adjusted)",
|
||||
sta->sta.addr, mplstates[sta->plink_state]);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* del_timer() and handler may race when entering these states */
|
||||
if (sta->plink_state == NL80211_PLINK_LISTEN ||
|
||||
sta->plink_state == NL80211_PLINK_ESTAB) {
|
||||
mpl_dbg(sta->sdata,
|
||||
"Ignoring timer for %pM in state %s (timer deleted)",
|
||||
sta->sta.addr, mplstates[sta->plink_state]);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
mpl_dbg(sta->sdata,
|
||||
"Mesh plink timer for %pM fired on state %s\n",
|
||||
sta->sta.addr, mplstates[sta->plink_state]);
|
||||
|
@ -773,9 +792,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
|||
break;
|
||||
case CNF_ACPT:
|
||||
sta->plink_state = NL80211_PLINK_CNF_RCVD;
|
||||
if (!mod_plink_timer(sta,
|
||||
mshcfg->dot11MeshConfirmTimeout))
|
||||
sta->ignore_plink_timer = true;
|
||||
mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -834,8 +851,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
|||
case NL80211_PLINK_HOLDING:
|
||||
switch (event) {
|
||||
case CLS_ACPT:
|
||||
if (del_timer(&sta->plink_timer))
|
||||
sta->ignore_plink_timer = 1;
|
||||
del_timer(&sta->plink_timer);
|
||||
mesh_plink_fsm_restart(sta);
|
||||
break;
|
||||
case OPN_ACPT:
|
||||
|
|
|
@ -940,51 +940,70 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u32 changed = 0;
|
||||
int ret;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
|
||||
sdata_lock(sdata);
|
||||
mutex_lock(&local->mtx);
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
|
||||
if (!ifmgd->associated)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
ret = ieee80211_vif_change_channel(sdata, &changed);
|
||||
mutex_unlock(&local->mtx);
|
||||
if (ret) {
|
||||
if (!sdata->vif.csa_active)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* using reservation isn't immediate as it may be deferred until later
|
||||
* with multi-vif. once reservation is complete it will re-schedule the
|
||||
* work with no reserved_chanctx so verify chandef to check if it
|
||||
* completed successfully
|
||||
*/
|
||||
|
||||
if (sdata->reserved_chanctx) {
|
||||
/*
|
||||
* with multi-vif csa driver may call ieee80211_csa_finish()
|
||||
* many times while waiting for other interfaces to use their
|
||||
* reservations
|
||||
*/
|
||||
if (sdata->reserved_ready)
|
||||
goto out;
|
||||
|
||||
ret = ieee80211_vif_use_reserved_context(sdata);
|
||||
if (ret) {
|
||||
sdata_info(sdata,
|
||||
"failed to use reserved channel context, disconnecting (err=%d)\n",
|
||||
ret);
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
goto out;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
||||
&sdata->csa_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"vif channel switch failed, disconnecting\n");
|
||||
"failed to finalize channel switch, disconnecting\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = sdata->csa_chandef;
|
||||
/* Call "hw_config" only if doing sw channel switch.
|
||||
* Otherwise update the channel directly
|
||||
*/
|
||||
if (!local->ops->channel_switch)
|
||||
ieee80211_hw_config(local, 0);
|
||||
else
|
||||
local->hw.conf.chandef = local->_oper_chandef;
|
||||
}
|
||||
|
||||
/* XXX: shouldn't really modify cfg80211-owned data! */
|
||||
ifmgd->associated->channel = sdata->csa_chandef.chan;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
/* XXX: wait for a beacon first? */
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
mutex_unlock(&local->mtx);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
|
@ -992,6 +1011,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
|
@ -1028,6 +1049,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct cfg80211_bss *cbss = ifmgd->associated;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum ieee80211_band current_band;
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
|
@ -1071,7 +1093,22 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
sdata_info(sdata,
|
||||
"no channel context assigned to vif?, disconnecting\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
|
||||
if (local->use_chanctx) {
|
||||
u32 num_chanctx = 0;
|
||||
list_for_each_entry(chanctx, &local->chanctx_list, list)
|
||||
|
@ -1084,38 +1121,32 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return;
|
||||
}
|
||||
chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
|
||||
struct ieee80211_chanctx, conf);
|
||||
if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
|
||||
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
|
||||
chanctx->mode, false);
|
||||
if (res) {
|
||||
sdata_info(sdata,
|
||||
"channel switch with multiple interfaces on the same channel, disconnecting\n");
|
||||
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
|
||||
res);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
sdata->csa_chandef = csa_ie.chandef;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = true;
|
||||
sdata->csa_chandef = csa_ie.chandef;
|
||||
sdata->csa_block_tx = csa_ie.mode;
|
||||
|
||||
if (sdata->csa_block_tx)
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
ieee80211_stop_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
if (local->ops->channel_switch) {
|
||||
|
@ -1385,7 +1416,8 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
|
|||
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_PS);
|
||||
IEEE80211_QUEUE_STOP_REASON_PS,
|
||||
false);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
||||
|
@ -1830,10 +1862,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
ieee80211_vif_release_channel(sdata);
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
||||
|
@ -2079,10 +2112,11 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (!ieee80211_csa_needs_block_tx(local))
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
|
||||
|
|
|
@ -119,7 +119,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
|
|||
* before sending nullfunc to enable powersave at the AP.
|
||||
*/
|
||||
ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
|
||||
false);
|
||||
ieee80211_flush_queues(local, NULL);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
@ -182,7 +183,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
|
|||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
|
||||
false);
|
||||
}
|
||||
|
||||
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
|
||||
|
|
|
@ -35,7 +35,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|||
|
||||
ieee80211_stop_queues_by_reason(hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
||||
false);
|
||||
|
||||
/* flush out all packets */
|
||||
synchronize_net();
|
||||
|
@ -74,7 +75,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|||
}
|
||||
ieee80211_wake_queues_by_reason(hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
||||
false);
|
||||
return err;
|
||||
} else if (err > 0) {
|
||||
WARN_ON(err != 1);
|
||||
|
|
|
@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local);
|
|||
|
||||
|
||||
/* Rate control algorithms */
|
||||
#ifdef CONFIG_MAC80211_RC_PID
|
||||
int rc80211_pid_init(void);
|
||||
void rc80211_pid_exit(void);
|
||||
#else
|
||||
static inline int rc80211_pid_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void rc80211_pid_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL
|
||||
int rc80211_minstrel_init(void);
|
||||
void rc80211_minstrel_exit(void);
|
||||
|
|
|
@ -1,278 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
|
||||
* Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef RC80211_PID_H
|
||||
#define RC80211_PID_H
|
||||
|
||||
/* Sampling period for measuring percentage of failed frames in ms. */
|
||||
#define RC_PID_INTERVAL 125
|
||||
|
||||
/* Exponential averaging smoothness (used for I part of PID controller) */
|
||||
#define RC_PID_SMOOTHING_SHIFT 3
|
||||
#define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT)
|
||||
|
||||
/* Sharpening factor (used for D part of PID controller) */
|
||||
#define RC_PID_SHARPENING_FACTOR 0
|
||||
#define RC_PID_SHARPENING_DURATION 0
|
||||
|
||||
/* Fixed point arithmetic shifting amount. */
|
||||
#define RC_PID_ARITH_SHIFT 8
|
||||
|
||||
/* Proportional PID component coefficient. */
|
||||
#define RC_PID_COEFF_P 15
|
||||
/* Integral PID component coefficient. */
|
||||
#define RC_PID_COEFF_I 9
|
||||
/* Derivative PID component coefficient. */
|
||||
#define RC_PID_COEFF_D 15
|
||||
|
||||
/* Target failed frames rate for the PID controller. NB: This effectively gives
|
||||
* maximum failed frames percentage we're willing to accept. If the wireless
|
||||
* link quality is good, the controller will fail to adjust failed frames
|
||||
* percentage to the target. This is intentional.
|
||||
*/
|
||||
#define RC_PID_TARGET_PF 14
|
||||
|
||||
/* Rate behaviour normalization quantity over time. */
|
||||
#define RC_PID_NORM_OFFSET 3
|
||||
|
||||
/* Push high rates right after loading. */
|
||||
#define RC_PID_FAST_START 0
|
||||
|
||||
/* Arithmetic right shift for positive and negative values for ISO C. */
|
||||
#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
|
||||
((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
|
||||
|
||||
enum rc_pid_event_type {
|
||||
RC_PID_EVENT_TYPE_TX_STATUS,
|
||||
RC_PID_EVENT_TYPE_RATE_CHANGE,
|
||||
RC_PID_EVENT_TYPE_TX_RATE,
|
||||
RC_PID_EVENT_TYPE_PF_SAMPLE,
|
||||
};
|
||||
|
||||
union rc_pid_event_data {
|
||||
/* RC_PID_EVENT_TX_STATUS */
|
||||
struct {
|
||||
u32 flags;
|
||||
struct ieee80211_tx_info tx_status;
|
||||
};
|
||||
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
|
||||
/* RC_PID_EVENT_TYPE_TX_RATE */
|
||||
struct {
|
||||
int index;
|
||||
int rate;
|
||||
};
|
||||
/* RC_PID_EVENT_TYPE_PF_SAMPLE */
|
||||
struct {
|
||||
s32 pf_sample;
|
||||
s32 prop_err;
|
||||
s32 int_err;
|
||||
s32 der_err;
|
||||
};
|
||||
};
|
||||
|
||||
struct rc_pid_event {
|
||||
/* The time when the event occurred */
|
||||
unsigned long timestamp;
|
||||
|
||||
/* Event ID number */
|
||||
unsigned int id;
|
||||
|
||||
/* Type of event */
|
||||
enum rc_pid_event_type type;
|
||||
|
||||
/* type specific data */
|
||||
union rc_pid_event_data data;
|
||||
};
|
||||
|
||||
/* Size of the event ring buffer. */
|
||||
#define RC_PID_EVENT_RING_SIZE 32
|
||||
|
||||
struct rc_pid_event_buffer {
|
||||
/* Counter that generates event IDs */
|
||||
unsigned int ev_count;
|
||||
|
||||
/* Ring buffer of events */
|
||||
struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE];
|
||||
|
||||
/* Index to the entry in events_buf to be reused */
|
||||
unsigned int next_entry;
|
||||
|
||||
/* Lock that guards against concurrent access to this buffer struct */
|
||||
spinlock_t lock;
|
||||
|
||||
/* Wait queue for poll/select and blocking I/O */
|
||||
wait_queue_head_t waitqueue;
|
||||
};
|
||||
|
||||
struct rc_pid_events_file_info {
|
||||
/* The event buffer we read */
|
||||
struct rc_pid_event_buffer *events;
|
||||
|
||||
/* The entry we have should read next */
|
||||
unsigned int next_entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rc_pid_debugfs_entries - tunable parameters
|
||||
*
|
||||
* Algorithm parameters, tunable via debugfs.
|
||||
* @target: target percentage for failed frames
|
||||
* @sampling_period: error sampling interval in milliseconds
|
||||
* @coeff_p: absolute value of the proportional coefficient
|
||||
* @coeff_i: absolute value of the integral coefficient
|
||||
* @coeff_d: absolute value of the derivative coefficient
|
||||
* @smoothing_shift: absolute value of the integral smoothing factor (i.e.
|
||||
* amount of smoothing introduced by the exponential moving average)
|
||||
* @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
|
||||
* amount of emphasis given to the derivative term after low activity
|
||||
* events)
|
||||
* @sharpen_duration: duration of the sharpening effect after the detected low
|
||||
* activity event, relative to sampling_period
|
||||
* @norm_offset: amount of normalization periodically performed on the learnt
|
||||
* rate behaviour values (lower means we should trust more what we learnt
|
||||
* about behaviour of rates, higher means we should trust more the natural
|
||||
* ordering of rates)
|
||||
*/
|
||||
struct rc_pid_debugfs_entries {
|
||||
struct dentry *target;
|
||||
struct dentry *sampling_period;
|
||||
struct dentry *coeff_p;
|
||||
struct dentry *coeff_i;
|
||||
struct dentry *coeff_d;
|
||||
struct dentry *smoothing_shift;
|
||||
struct dentry *sharpen_factor;
|
||||
struct dentry *sharpen_duration;
|
||||
struct dentry *norm_offset;
|
||||
};
|
||||
|
||||
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
|
||||
struct ieee80211_tx_info *stat);
|
||||
|
||||
void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
|
||||
int index, int rate);
|
||||
|
||||
void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
|
||||
int index, int rate);
|
||||
|
||||
void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
|
||||
s32 pf_sample, s32 prop_err,
|
||||
s32 int_err, s32 der_err);
|
||||
|
||||
void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
|
||||
struct dentry *dir);
|
||||
|
||||
void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta);
|
||||
|
||||
struct rc_pid_sta_info {
|
||||
unsigned long last_change;
|
||||
unsigned long last_sample;
|
||||
|
||||
u32 tx_num_failed;
|
||||
u32 tx_num_xmit;
|
||||
|
||||
int txrate_idx;
|
||||
|
||||
/* Average failed frames percentage error (i.e. actual vs. target
|
||||
* percentage), scaled by RC_PID_SMOOTHING. This value is computed
|
||||
* using using an exponential weighted average technique:
|
||||
*
|
||||
* (RC_PID_SMOOTHING - 1) * err_avg_old + err
|
||||
* err_avg = ------------------------------------------
|
||||
* RC_PID_SMOOTHING
|
||||
*
|
||||
* where err_avg is the new approximation, err_avg_old the previous one
|
||||
* and err is the error w.r.t. to the current failed frames percentage
|
||||
* sample. Note that the bigger RC_PID_SMOOTHING the more weight is
|
||||
* given to the previous estimate, resulting in smoother behavior (i.e.
|
||||
* corresponding to a longer integration window).
|
||||
*
|
||||
* For computation, we actually don't use the above formula, but this
|
||||
* one:
|
||||
*
|
||||
* err_avg_scaled = err_avg_old_scaled - err_avg_old + err
|
||||
*
|
||||
* where:
|
||||
* err_avg_scaled = err * RC_PID_SMOOTHING
|
||||
* err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING
|
||||
*
|
||||
* This avoids floating point numbers and the per_failed_old value can
|
||||
* easily be obtained by shifting per_failed_old_scaled right by
|
||||
* RC_PID_SMOOTHING_SHIFT.
|
||||
*/
|
||||
s32 err_avg_sc;
|
||||
|
||||
/* Last framed failes percentage sample. */
|
||||
u32 last_pf;
|
||||
|
||||
/* Sharpening needed. */
|
||||
u8 sharp_cnt;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* Event buffer */
|
||||
struct rc_pid_event_buffer events;
|
||||
|
||||
/* Events debugfs file entry */
|
||||
struct dentry *events_entry;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
|
||||
* be tuned individually for each interface.
|
||||
*/
|
||||
struct rc_pid_rateinfo {
|
||||
|
||||
/* Map sorted rates to rates in ieee80211_hw_mode. */
|
||||
int index;
|
||||
|
||||
/* Map rates in ieee80211_hw_mode to sorted rates. */
|
||||
int rev_index;
|
||||
|
||||
/* Did we do any measurement on this rate? */
|
||||
bool valid;
|
||||
|
||||
/* Comparison with the lowest rate. */
|
||||
int diff;
|
||||
};
|
||||
|
||||
struct rc_pid_info {
|
||||
|
||||
/* The failed frames percentage target. */
|
||||
unsigned int target;
|
||||
|
||||
/* Rate at which failed frames percentage is sampled in 0.001s. */
|
||||
unsigned int sampling_period;
|
||||
|
||||
/* P, I and D coefficients. */
|
||||
int coeff_p;
|
||||
int coeff_i;
|
||||
int coeff_d;
|
||||
|
||||
/* Exponential averaging shift. */
|
||||
unsigned int smoothing_shift;
|
||||
|
||||
/* Sharpening factor and duration. */
|
||||
unsigned int sharpen_factor;
|
||||
unsigned int sharpen_duration;
|
||||
|
||||
/* Normalization offset. */
|
||||
unsigned int norm_offset;
|
||||
|
||||
/* Rates information. */
|
||||
struct rc_pid_rateinfo *rinfo;
|
||||
|
||||
/* Index of the last used rate. */
|
||||
int oldrate;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* Debugfs entries created for the parameters above. */
|
||||
struct rc_pid_debugfs_entries dentries;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* RC80211_PID_H */
|
|
@ -1,478 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2005, Instant802 Networks, Inc.
|
||||
* Copyright 2005, Devicescape Software, Inc.
|
||||
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
|
||||
* Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "rate.h"
|
||||
#include "mesh.h"
|
||||
#include "rc80211_pid.h"
|
||||
|
||||
|
||||
/* This is an implementation of a TX rate control algorithm that uses a PID
|
||||
* controller. Given a target failed frames rate, the controller decides about
|
||||
* TX rate changes to meet the target failed frames rate.
|
||||
*
|
||||
* The controller basically computes the following:
|
||||
*
|
||||
* adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
|
||||
*
|
||||
* where
|
||||
* adj adjustment value that is used to switch TX rate (see below)
|
||||
* err current error: target vs. current failed frames percentage
|
||||
* last_err last error
|
||||
* err_avg average (i.e. poor man's integral) of recent errors
|
||||
* sharpening non-zero when fast response is needed (i.e. right after
|
||||
* association or no frames sent for a long time), heading
|
||||
* to zero over time
|
||||
* CP Proportional coefficient
|
||||
* CI Integral coefficient
|
||||
* CD Derivative coefficient
|
||||
*
|
||||
* CP, CI, CD are subject to careful tuning.
|
||||
*
|
||||
* The integral component uses a exponential moving average approach instead of
|
||||
* an actual sliding window. The advantage is that we don't need to keep an
|
||||
* array of the last N error values and computation is easier.
|
||||
*
|
||||
* Once we have the adj value, we map it to a rate by means of a learning
|
||||
* algorithm. This algorithm keeps the state of the percentual failed frames
|
||||
* difference between rates. The behaviour of the lowest available rate is kept
|
||||
* as a reference value, and every time we switch between two rates, we compute
|
||||
* the difference between the failed frames each rate exhibited. By doing so,
|
||||
* we compare behaviours which different rates exhibited in adjacent timeslices,
|
||||
* thus the comparison is minimally affected by external conditions. This
|
||||
* difference gets propagated to the whole set of measurements, so that the
|
||||
* reference is always the same. Periodically, we normalize this set so that
|
||||
* recent events weigh the most. By comparing the adj value with this set, we
|
||||
* avoid pejorative switches to lower rates and allow for switches to higher
|
||||
* rates if they behaved well.
|
||||
*
|
||||
* Note that for the computations we use a fixed-point representation to avoid
|
||||
* floating point arithmetic. Hence, all values are shifted left by
|
||||
* RC_PID_ARITH_SHIFT.
|
||||
*/
|
||||
|
||||
|
||||
/* Adjust the rate while ensuring that we won't switch to a lower rate if it
|
||||
* exhibited a worse failed frames behaviour and we'll choose the highest rate
|
||||
* whose failed frames behaviour is not worse than the one of the original rate
|
||||
* target. While at it, check that the new rate is valid. */
|
||||
static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta,
|
||||
struct rc_pid_sta_info *spinfo, int adj,
|
||||
struct rc_pid_rateinfo *rinfo)
|
||||
{
|
||||
int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
|
||||
int cur = spinfo->txrate_idx;
|
||||
|
||||
band = sband->band;
|
||||
n_bitrates = sband->n_bitrates;
|
||||
|
||||
/* Map passed arguments to sorted values. */
|
||||
cur_sorted = rinfo[cur].rev_index;
|
||||
new_sorted = cur_sorted + adj;
|
||||
|
||||
/* Check limits. */
|
||||
if (new_sorted < 0)
|
||||
new_sorted = rinfo[0].rev_index;
|
||||
else if (new_sorted >= n_bitrates)
|
||||
new_sorted = rinfo[n_bitrates - 1].rev_index;
|
||||
|
||||
tmp = new_sorted;
|
||||
|
||||
if (adj < 0) {
|
||||
/* Ensure that the rate decrease isn't disadvantageous. */
|
||||
for (probe = cur_sorted; probe >= new_sorted; probe--)
|
||||
if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
|
||||
rate_supported(sta, band, rinfo[probe].index))
|
||||
tmp = probe;
|
||||
} else {
|
||||
/* Look for rate increase with zero (or below) cost. */
|
||||
for (probe = new_sorted + 1; probe < n_bitrates; probe++)
|
||||
if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
|
||||
rate_supported(sta, band, rinfo[probe].index))
|
||||
tmp = probe;
|
||||
}
|
||||
|
||||
/* Fit the rate found to the nearest supported rate. */
|
||||
do {
|
||||
if (rate_supported(sta, band, rinfo[tmp].index)) {
|
||||
spinfo->txrate_idx = rinfo[tmp].index;
|
||||
break;
|
||||
}
|
||||
if (adj < 0)
|
||||
tmp--;
|
||||
else
|
||||
tmp++;
|
||||
} while (tmp < n_bitrates && tmp >= 0);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
rate_control_pid_event_rate_change(&spinfo->events,
|
||||
spinfo->txrate_idx,
|
||||
sband->bitrates[spinfo->txrate_idx].bitrate);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Normalize the failed frames per-rate differences. */
|
||||
static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
|
||||
{
|
||||
int i, norm_offset = pinfo->norm_offset;
|
||||
struct rc_pid_rateinfo *r = pinfo->rinfo;
|
||||
|
||||
if (r[0].diff > norm_offset)
|
||||
r[0].diff -= norm_offset;
|
||||
else if (r[0].diff < -norm_offset)
|
||||
r[0].diff += norm_offset;
|
||||
for (i = 0; i < l - 1; i++)
|
||||
if (r[i + 1].diff > r[i].diff + norm_offset)
|
||||
r[i + 1].diff -= norm_offset;
|
||||
else if (r[i + 1].diff <= r[i].diff)
|
||||
r[i + 1].diff += norm_offset;
|
||||
}
|
||||
|
||||
static void rate_control_pid_sample(struct rc_pid_info *pinfo,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta,
|
||||
struct rc_pid_sta_info *spinfo)
|
||||
{
|
||||
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
|
||||
u32 pf;
|
||||
s32 err_avg;
|
||||
u32 err_prop;
|
||||
u32 err_int;
|
||||
u32 err_der;
|
||||
int adj, i, j, tmp;
|
||||
unsigned long period;
|
||||
|
||||
/* In case nothing happened during the previous control interval, turn
|
||||
* the sharpening factor on. */
|
||||
period = msecs_to_jiffies(pinfo->sampling_period);
|
||||
if (jiffies - spinfo->last_sample > 2 * period)
|
||||
spinfo->sharp_cnt = pinfo->sharpen_duration;
|
||||
|
||||
spinfo->last_sample = jiffies;
|
||||
|
||||
/* This should never happen, but in case, we assume the old sample is
|
||||
* still a good measurement and copy it. */
|
||||
if (unlikely(spinfo->tx_num_xmit == 0))
|
||||
pf = spinfo->last_pf;
|
||||
else
|
||||
pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
|
||||
|
||||
spinfo->tx_num_xmit = 0;
|
||||
spinfo->tx_num_failed = 0;
|
||||
|
||||
/* If we just switched rate, update the rate behaviour info. */
|
||||
if (pinfo->oldrate != spinfo->txrate_idx) {
|
||||
|
||||
i = rinfo[pinfo->oldrate].rev_index;
|
||||
j = rinfo[spinfo->txrate_idx].rev_index;
|
||||
|
||||
tmp = (pf - spinfo->last_pf);
|
||||
tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
|
||||
|
||||
rinfo[j].diff = rinfo[i].diff + tmp;
|
||||
pinfo->oldrate = spinfo->txrate_idx;
|
||||
}
|
||||
rate_control_pid_normalize(pinfo, sband->n_bitrates);
|
||||
|
||||
/* Compute the proportional, integral and derivative errors. */
|
||||
err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT;
|
||||
|
||||
err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
|
||||
spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
|
||||
err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
|
||||
|
||||
err_der = (pf - spinfo->last_pf) *
|
||||
(1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
|
||||
spinfo->last_pf = pf;
|
||||
if (spinfo->sharp_cnt)
|
||||
spinfo->sharp_cnt--;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
|
||||
err_der);
|
||||
#endif
|
||||
|
||||
/* Compute the controller output. */
|
||||
adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
|
||||
+ err_der * pinfo->coeff_d);
|
||||
adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
|
||||
|
||||
/* Change rate. */
|
||||
if (adj)
|
||||
rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
|
||||
}
|
||||
|
||||
static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct rc_pid_info *pinfo = priv;
|
||||
struct rc_pid_sta_info *spinfo = priv_sta;
|
||||
unsigned long period;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (!spinfo)
|
||||
return;
|
||||
|
||||
/* Ignore all frames that were sent with a different rate than the rate
|
||||
* we currently advise mac80211 to use. */
|
||||
if (info->status.rates[0].idx != spinfo->txrate_idx)
|
||||
return;
|
||||
|
||||
spinfo->tx_num_xmit++;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
rate_control_pid_event_tx_status(&spinfo->events, info);
|
||||
#endif
|
||||
|
||||
/* We count frames that totally failed to be transmitted as two bad
|
||||
* frames, those that made it out but had some retries as one good and
|
||||
* one bad frame. */
|
||||
if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
|
||||
spinfo->tx_num_failed += 2;
|
||||
spinfo->tx_num_xmit++;
|
||||
} else if (info->status.rates[0].count > 1) {
|
||||
spinfo->tx_num_failed++;
|
||||
spinfo->tx_num_xmit++;
|
||||
}
|
||||
|
||||
/* Update PID controller state. */
|
||||
period = msecs_to_jiffies(pinfo->sampling_period);
|
||||
if (time_after(jiffies, spinfo->last_sample + period))
|
||||
rate_control_pid_sample(pinfo, sband, sta, spinfo);
|
||||
}
|
||||
|
||||
static void
|
||||
rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
|
||||
void *priv_sta,
|
||||
struct ieee80211_tx_rate_control *txrc)
|
||||
{
|
||||
struct sk_buff *skb = txrc->skb;
|
||||
struct ieee80211_supported_band *sband = txrc->sband;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct rc_pid_sta_info *spinfo = priv_sta;
|
||||
int rateidx;
|
||||
|
||||
if (txrc->rts)
|
||||
info->control.rates[0].count =
|
||||
txrc->hw->conf.long_frame_max_tx_count;
|
||||
else
|
||||
info->control.rates[0].count =
|
||||
txrc->hw->conf.short_frame_max_tx_count;
|
||||
|
||||
/* Send management frames and NO_ACK data using lowest rate. */
|
||||
if (rate_control_send_low(sta, priv_sta, txrc))
|
||||
return;
|
||||
|
||||
rateidx = spinfo->txrate_idx;
|
||||
|
||||
if (rateidx >= sband->n_bitrates)
|
||||
rateidx = sband->n_bitrates - 1;
|
||||
|
||||
info->control.rates[0].idx = rateidx;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
rate_control_pid_event_tx_rate(&spinfo->events,
|
||||
rateidx, sband->bitrates[rateidx].bitrate);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
{
|
||||
struct rc_pid_sta_info *spinfo = priv_sta;
|
||||
struct rc_pid_info *pinfo = priv;
|
||||
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
|
||||
int i, j, tmp;
|
||||
bool s;
|
||||
|
||||
/* TODO: This routine should consider using RSSI from previous packets
|
||||
* as we need to have IEEE 802.1X auth succeed immediately after assoc..
|
||||
* Until that method is implemented, we will use the lowest supported
|
||||
* rate as a workaround. */
|
||||
|
||||
/* Sort the rates. This is optimized for the most common case (i.e.
|
||||
* almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
|
||||
* mapping too. */
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
rinfo[i].index = i;
|
||||
rinfo[i].rev_index = i;
|
||||
if (RC_PID_FAST_START)
|
||||
rinfo[i].diff = 0;
|
||||
else
|
||||
rinfo[i].diff = i * pinfo->norm_offset;
|
||||
}
|
||||
for (i = 1; i < sband->n_bitrates; i++) {
|
||||
s = false;
|
||||
for (j = 0; j < sband->n_bitrates - i; j++)
|
||||
if (unlikely(sband->bitrates[rinfo[j].index].bitrate >
|
||||
sband->bitrates[rinfo[j + 1].index].bitrate)) {
|
||||
tmp = rinfo[j].index;
|
||||
rinfo[j].index = rinfo[j + 1].index;
|
||||
rinfo[j + 1].index = tmp;
|
||||
rinfo[rinfo[j].index].rev_index = j;
|
||||
rinfo[rinfo[j + 1].index].rev_index = j + 1;
|
||||
s = true;
|
||||
}
|
||||
if (!s)
|
||||
break;
|
||||
}
|
||||
|
||||
spinfo->txrate_idx = rate_lowest_index(sband, sta);
|
||||
}
|
||||
|
||||
static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
|
||||
struct dentry *debugfsdir)
|
||||
{
|
||||
struct rc_pid_info *pinfo;
|
||||
struct rc_pid_rateinfo *rinfo;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int i, max_rates = 0;
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct rc_pid_debugfs_entries *de;
|
||||
#endif
|
||||
|
||||
pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
|
||||
if (!pinfo)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
sband = hw->wiphy->bands[i];
|
||||
if (sband && sband->n_bitrates > max_rates)
|
||||
max_rates = sband->n_bitrates;
|
||||
}
|
||||
|
||||
rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC);
|
||||
if (!rinfo) {
|
||||
kfree(pinfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pinfo->target = RC_PID_TARGET_PF;
|
||||
pinfo->sampling_period = RC_PID_INTERVAL;
|
||||
pinfo->coeff_p = RC_PID_COEFF_P;
|
||||
pinfo->coeff_i = RC_PID_COEFF_I;
|
||||
pinfo->coeff_d = RC_PID_COEFF_D;
|
||||
pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT;
|
||||
pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR;
|
||||
pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION;
|
||||
pinfo->norm_offset = RC_PID_NORM_OFFSET;
|
||||
pinfo->rinfo = rinfo;
|
||||
pinfo->oldrate = 0;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
de = &pinfo->dentries;
|
||||
de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
|
||||
debugfsdir, &pinfo->target);
|
||||
de->sampling_period = debugfs_create_u32("sampling_period",
|
||||
S_IRUSR | S_IWUSR, debugfsdir,
|
||||
&pinfo->sampling_period);
|
||||
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
|
||||
debugfsdir, (u32 *)&pinfo->coeff_p);
|
||||
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
|
||||
debugfsdir, (u32 *)&pinfo->coeff_i);
|
||||
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
|
||||
debugfsdir, (u32 *)&pinfo->coeff_d);
|
||||
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
|
||||
S_IRUSR | S_IWUSR, debugfsdir,
|
||||
&pinfo->smoothing_shift);
|
||||
de->sharpen_factor = debugfs_create_u32("sharpen_factor",
|
||||
S_IRUSR | S_IWUSR, debugfsdir,
|
||||
&pinfo->sharpen_factor);
|
||||
de->sharpen_duration = debugfs_create_u32("sharpen_duration",
|
||||
S_IRUSR | S_IWUSR, debugfsdir,
|
||||
&pinfo->sharpen_duration);
|
||||
de->norm_offset = debugfs_create_u32("norm_offset",
|
||||
S_IRUSR | S_IWUSR, debugfsdir,
|
||||
&pinfo->norm_offset);
|
||||
#endif
|
||||
|
||||
return pinfo;
|
||||
}
|
||||
|
||||
static void rate_control_pid_free(void *priv)
|
||||
{
|
||||
struct rc_pid_info *pinfo = priv;
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct rc_pid_debugfs_entries *de = &pinfo->dentries;
|
||||
|
||||
debugfs_remove(de->norm_offset);
|
||||
debugfs_remove(de->sharpen_duration);
|
||||
debugfs_remove(de->sharpen_factor);
|
||||
debugfs_remove(de->smoothing_shift);
|
||||
debugfs_remove(de->coeff_d);
|
||||
debugfs_remove(de->coeff_i);
|
||||
debugfs_remove(de->coeff_p);
|
||||
debugfs_remove(de->sampling_period);
|
||||
debugfs_remove(de->target);
|
||||
#endif
|
||||
|
||||
kfree(pinfo->rinfo);
|
||||
kfree(pinfo);
|
||||
}
|
||||
|
||||
static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct rc_pid_sta_info *spinfo;
|
||||
|
||||
spinfo = kzalloc(sizeof(*spinfo), gfp);
|
||||
if (spinfo == NULL)
|
||||
return NULL;
|
||||
|
||||
spinfo->last_sample = jiffies;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
spin_lock_init(&spinfo->events.lock);
|
||||
init_waitqueue_head(&spinfo->events.waitqueue);
|
||||
#endif
|
||||
|
||||
return spinfo;
|
||||
}
|
||||
|
||||
static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
|
||||
void *priv_sta)
|
||||
{
|
||||
kfree(priv_sta);
|
||||
}
|
||||
|
||||
static const struct rate_control_ops mac80211_rcpid = {
|
||||
.name = "pid",
|
||||
.tx_status = rate_control_pid_tx_status,
|
||||
.get_rate = rate_control_pid_get_rate,
|
||||
.rate_init = rate_control_pid_rate_init,
|
||||
.alloc = rate_control_pid_alloc,
|
||||
.free = rate_control_pid_free,
|
||||
.alloc_sta = rate_control_pid_alloc_sta,
|
||||
.free_sta = rate_control_pid_free_sta,
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
.add_sta_debugfs = rate_control_pid_add_sta_debugfs,
|
||||
.remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
|
||||
#endif
|
||||
};
|
||||
|
||||
int __init rc80211_pid_init(void)
|
||||
{
|
||||
return ieee80211_rate_control_register(&mac80211_rcpid);
|
||||
}
|
||||
|
||||
void rc80211_pid_exit(void)
|
||||
{
|
||||
ieee80211_rate_control_unregister(&mac80211_rcpid);
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <net/mac80211.h>
|
||||
#include "rate.h"
|
||||
|
||||
#include "rc80211_pid.h"
|
||||
|
||||
static void rate_control_pid_event(struct rc_pid_event_buffer *buf,
|
||||
enum rc_pid_event_type type,
|
||||
union rc_pid_event_data *data)
|
||||
{
|
||||
struct rc_pid_event *ev;
|
||||
unsigned long status;
|
||||
|
||||
spin_lock_irqsave(&buf->lock, status);
|
||||
ev = &(buf->ring[buf->next_entry]);
|
||||
buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE;
|
||||
|
||||
ev->timestamp = jiffies;
|
||||
ev->id = buf->ev_count++;
|
||||
ev->type = type;
|
||||
ev->data = *data;
|
||||
|
||||
spin_unlock_irqrestore(&buf->lock, status);
|
||||
|
||||
wake_up_all(&buf->waitqueue);
|
||||
}
|
||||
|
||||
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
|
||||
struct ieee80211_tx_info *stat)
|
||||
{
|
||||
union rc_pid_event_data evd;
|
||||
|
||||
evd.flags = stat->flags;
|
||||
memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
|
||||
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
|
||||
}
|
||||
|
||||
void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
|
||||
int index, int rate)
|
||||
{
|
||||
union rc_pid_event_data evd;
|
||||
|
||||
evd.index = index;
|
||||
evd.rate = rate;
|
||||
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd);
|
||||
}
|
||||
|
||||
void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
|
||||
int index, int rate)
|
||||
{
|
||||
union rc_pid_event_data evd;
|
||||
|
||||
evd.index = index;
|
||||
evd.rate = rate;
|
||||
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd);
|
||||
}
|
||||
|
||||
void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
|
||||
s32 pf_sample, s32 prop_err,
|
||||
s32 int_err, s32 der_err)
|
||||
{
|
||||
union rc_pid_event_data evd;
|
||||
|
||||
evd.pf_sample = pf_sample;
|
||||
evd.prop_err = prop_err;
|
||||
evd.int_err = int_err;
|
||||
evd.der_err = der_err;
|
||||
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd);
|
||||
}
|
||||
|
||||
static int rate_control_pid_events_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct rc_pid_sta_info *sinfo = inode->i_private;
|
||||
struct rc_pid_event_buffer *events = &sinfo->events;
|
||||
struct rc_pid_events_file_info *file_info;
|
||||
unsigned long status;
|
||||
|
||||
/* Allocate a state struct */
|
||||
file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
|
||||
if (file_info == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&events->lock, status);
|
||||
|
||||
file_info->next_entry = events->next_entry;
|
||||
file_info->events = events;
|
||||
|
||||
spin_unlock_irqrestore(&events->lock, status);
|
||||
|
||||
file->private_data = file_info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rate_control_pid_events_release(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct rc_pid_events_file_info *file_info = file->private_data;
|
||||
|
||||
kfree(file_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rate_control_pid_events_poll(struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct rc_pid_events_file_info *file_info = file->private_data;
|
||||
|
||||
poll_wait(file, &file_info->events->waitqueue, wait);
|
||||
|
||||
return POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
#define RC_PID_PRINT_BUF_SIZE 64
|
||||
|
||||
static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
struct rc_pid_events_file_info *file_info = file->private_data;
|
||||
struct rc_pid_event_buffer *events = file_info->events;
|
||||
struct rc_pid_event *ev;
|
||||
char pb[RC_PID_PRINT_BUF_SIZE];
|
||||
int ret;
|
||||
int p;
|
||||
unsigned long status;
|
||||
|
||||
/* Check if there is something to read. */
|
||||
if (events->next_entry == file_info->next_entry) {
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Wait */
|
||||
ret = wait_event_interruptible(events->waitqueue,
|
||||
events->next_entry != file_info->next_entry);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write out one event per call. I don't care whether it's a little
|
||||
* inefficient, this is debugging code anyway. */
|
||||
spin_lock_irqsave(&events->lock, status);
|
||||
|
||||
/* Get an event */
|
||||
ev = &(events->ring[file_info->next_entry]);
|
||||
file_info->next_entry = (file_info->next_entry + 1) %
|
||||
RC_PID_EVENT_RING_SIZE;
|
||||
|
||||
/* Print information about the event. Note that userspace needs to
|
||||
* provide large enough buffers. */
|
||||
length = length < RC_PID_PRINT_BUF_SIZE ?
|
||||
length : RC_PID_PRINT_BUF_SIZE;
|
||||
p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
|
||||
switch (ev->type) {
|
||||
case RC_PID_EVENT_TYPE_TX_STATUS:
|
||||
p += scnprintf(pb + p, length - p, "tx_status %u %u",
|
||||
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
|
||||
ev->data.tx_status.status.rates[0].idx);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_RATE_CHANGE:
|
||||
p += scnprintf(pb + p, length - p, "rate_change %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_TX_RATE:
|
||||
p += scnprintf(pb + p, length - p, "tx_rate %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_PF_SAMPLE:
|
||||
p += scnprintf(pb + p, length - p,
|
||||
"pf_sample %d %d %d %d",
|
||||
ev->data.pf_sample, ev->data.prop_err,
|
||||
ev->data.int_err, ev->data.der_err);
|
||||
break;
|
||||
}
|
||||
p += scnprintf(pb + p, length - p, "\n");
|
||||
|
||||
spin_unlock_irqrestore(&events->lock, status);
|
||||
|
||||
if (copy_to_user(buf, pb, p))
|
||||
return -EFAULT;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#undef RC_PID_PRINT_BUF_SIZE
|
||||
|
||||
static const struct file_operations rc_pid_fop_events = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = rate_control_pid_events_read,
|
||||
.poll = rate_control_pid_events_poll,
|
||||
.open = rate_control_pid_events_open,
|
||||
.release = rate_control_pid_events_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
|
||||
struct dentry *dir)
|
||||
{
|
||||
struct rc_pid_sta_info *spinfo = priv_sta;
|
||||
|
||||
spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO,
|
||||
dir, spinfo,
|
||||
&rc_pid_fop_events);
|
||||
}
|
||||
|
||||
void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta)
|
||||
{
|
||||
struct rc_pid_sta_info *spinfo = priv_sta;
|
||||
|
||||
debugfs_remove(spinfo->events_entry);
|
||||
}
|
|
@ -1107,6 +1107,8 @@ static void sta_ps_end(struct sta_info *sta)
|
|||
return;
|
||||
}
|
||||
|
||||
set_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
ieee80211_sta_ps_deliver_wakeup(sta);
|
||||
}
|
||||
|
||||
|
|
|
@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
|||
{
|
||||
struct cfg80211_scan_request *req = local->scan_req;
|
||||
struct cfg80211_chan_def chandef;
|
||||
enum ieee80211_band band;
|
||||
u8 bands_used = 0;
|
||||
int i, ielen, n_chans;
|
||||
|
||||
if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
|
||||
return false;
|
||||
|
||||
do {
|
||||
if (local->hw_scan_band == IEEE80211_NUM_BANDS)
|
||||
return false;
|
||||
|
||||
band = local->hw_scan_band;
|
||||
n_chans = 0;
|
||||
if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
|
||||
for (i = 0; i < req->n_channels; i++) {
|
||||
if (req->channels[i]->band == band) {
|
||||
local->hw_scan_req->channels[n_chans] =
|
||||
req->channels[i];
|
||||
n_chans++;
|
||||
}
|
||||
local->hw_scan_req->req.channels[i] = req->channels[i];
|
||||
bands_used |= BIT(req->channels[i]->band);
|
||||
}
|
||||
|
||||
local->hw_scan_band++;
|
||||
} while (!n_chans);
|
||||
n_chans = req->n_channels;
|
||||
} else {
|
||||
do {
|
||||
if (local->hw_scan_band == IEEE80211_NUM_BANDS)
|
||||
return false;
|
||||
|
||||
local->hw_scan_req->n_channels = n_chans;
|
||||
n_chans = 0;
|
||||
|
||||
for (i = 0; i < req->n_channels; i++) {
|
||||
if (req->channels[i]->band !=
|
||||
local->hw_scan_band)
|
||||
continue;
|
||||
local->hw_scan_req->req.channels[n_chans] =
|
||||
req->channels[i];
|
||||
n_chans++;
|
||||
bands_used |= BIT(req->channels[i]->band);
|
||||
}
|
||||
|
||||
local->hw_scan_band++;
|
||||
} while (!n_chans);
|
||||
}
|
||||
|
||||
local->hw_scan_req->req.n_channels = n_chans;
|
||||
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
|
||||
|
||||
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
|
||||
ielen = ieee80211_build_preq_ies(local,
|
||||
(u8 *)local->hw_scan_req->req.ie,
|
||||
local->hw_scan_ies_bufsize,
|
||||
req->ie, req->ie_len, band,
|
||||
req->rates[band], &chandef);
|
||||
local->hw_scan_req->ie_len = ielen;
|
||||
local->hw_scan_req->no_cck = req->no_cck;
|
||||
&local->hw_scan_req->ies,
|
||||
req->ie, req->ie_len,
|
||||
bands_used, req->rates, &chandef);
|
||||
local->hw_scan_req->req.ie_len = ielen;
|
||||
local->hw_scan_req->req.no_cck = req->no_cck;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
|||
if (WARN_ON(!local->scan_req))
|
||||
return;
|
||||
|
||||
if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
|
||||
if (hw_scan && !aborted &&
|
||||
!(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) &&
|
||||
ieee80211_prep_hw_scan(local)) {
|
||||
int rc;
|
||||
|
||||
rc = drv_hw_scan(local,
|
||||
|
@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
u8 *ies;
|
||||
|
||||
local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
|
||||
|
||||
if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
|
||||
int i, n_bands = 0;
|
||||
u8 bands_counted = 0;
|
||||
|
||||
for (i = 0; i < req->n_channels; i++) {
|
||||
if (bands_counted & BIT(req->channels[i]->band))
|
||||
continue;
|
||||
bands_counted |= BIT(req->channels[i]->band);
|
||||
n_bands++;
|
||||
}
|
||||
|
||||
local->hw_scan_ies_bufsize *= n_bands;
|
||||
}
|
||||
|
||||
local->hw_scan_req = kmalloc(
|
||||
sizeof(*local->hw_scan_req) +
|
||||
req->n_channels * sizeof(req->channels[0]) +
|
||||
|
@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
if (!local->hw_scan_req)
|
||||
return -ENOMEM;
|
||||
|
||||
local->hw_scan_req->ssids = req->ssids;
|
||||
local->hw_scan_req->n_ssids = req->n_ssids;
|
||||
local->hw_scan_req->req.ssids = req->ssids;
|
||||
local->hw_scan_req->req.n_ssids = req->n_ssids;
|
||||
ies = (u8 *)local->hw_scan_req +
|
||||
sizeof(*local->hw_scan_req) +
|
||||
req->n_channels * sizeof(req->channels[0]);
|
||||
local->hw_scan_req->ie = ies;
|
||||
local->hw_scan_req->flags = req->flags;
|
||||
local->hw_scan_req->req.ie = ies;
|
||||
local->hw_scan_req->req.flags = req->flags;
|
||||
|
||||
local->hw_scan_band = 0;
|
||||
|
||||
|
@ -973,9 +1003,13 @@ 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 = {};
|
||||
struct ieee80211_scan_ies sched_scan_ies = {};
|
||||
struct cfg80211_chan_def chandef;
|
||||
int ret, i, iebufsz;
|
||||
int ret, i, iebufsz, num_bands = 0;
|
||||
u32 rate_masks[IEEE80211_NUM_BANDS] = {};
|
||||
u8 bands_used = 0;
|
||||
u8 *ie;
|
||||
size_t len;
|
||||
|
||||
iebufsz = local->scan_ies_len + req->ie_len;
|
||||
|
||||
|
@ -985,33 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
return -ENOTSUPP;
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
if (!local->hw.wiphy->bands[i])
|
||||
continue;
|
||||
|
||||
sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
|
||||
if (!sched_scan_ies.ie[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
if (local->hw.wiphy->bands[i]) {
|
||||
bands_used |= BIT(i);
|
||||
rate_masks[i] = (u32) -1;
|
||||
num_bands++;
|
||||
}
|
||||
|
||||
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
|
||||
|
||||
sched_scan_ies.len[i] =
|
||||
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
|
||||
iebufsz, req->ie, req->ie_len,
|
||||
i, (u32) -1, &chandef);
|
||||
}
|
||||
|
||||
ie = kzalloc(num_bands * iebufsz, GFP_KERNEL);
|
||||
if (!ie) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
|
||||
|
||||
len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
|
||||
&sched_scan_ies, req->ie,
|
||||
req->ie_len, bands_used,
|
||||
rate_masks, &chandef);
|
||||
|
||||
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
|
||||
if (ret == 0) {
|
||||
rcu_assign_pointer(local->sched_scan_sdata, sdata);
|
||||
local->sched_scan_req = req;
|
||||
}
|
||||
|
||||
out_free:
|
||||
while (i > 0)
|
||||
kfree(sched_scan_ies.ie[--i]);
|
||||
kfree(ie);
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
/* Clean in case of failure after HW restart or upon resume. */
|
||||
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
|
||||
|
|
|
@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
|
|||
struct ps_data *ps;
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
|
||||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
|
||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
ps = &sdata->bss->ps;
|
||||
|
@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
|
|||
|
||||
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
||||
|
||||
atomic_dec(&ps->num_sta_ps);
|
||||
sta_info_recalc_tim(sta);
|
||||
|
@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
|
|||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
mesh_sta_cleanup(sta);
|
||||
|
||||
cancel_work_sync(&sta->drv_unblock_wk);
|
||||
cancel_work_sync(&sta->drv_deliver_wk);
|
||||
|
||||
/*
|
||||
* Destroy aggregation state here. It would be nice to wait for the
|
||||
|
@ -253,33 +255,23 @@ static void sta_info_hash_add(struct ieee80211_local *local,
|
|||
rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
|
||||
}
|
||||
|
||||
static void sta_unblock(struct work_struct *wk)
|
||||
static void sta_deliver_ps_frames(struct work_struct *wk)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
sta = container_of(wk, struct sta_info, drv_unblock_wk);
|
||||
sta = container_of(wk, struct sta_info, drv_deliver_wk);
|
||||
|
||||
if (sta->dead)
|
||||
return;
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
local_bh_disable();
|
||||
local_bh_disable();
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_STA))
|
||||
ieee80211_sta_ps_deliver_wakeup(sta);
|
||||
local_bh_enable();
|
||||
} else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
|
||||
local_bh_disable();
|
||||
else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
|
||||
ieee80211_sta_ps_deliver_poll_response(sta);
|
||||
local_bh_enable();
|
||||
} else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
|
||||
local_bh_disable();
|
||||
else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
|
||||
ieee80211_sta_ps_deliver_uapsd(sta);
|
||||
local_bh_enable();
|
||||
} else
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static int sta_prepare_rate_control(struct ieee80211_local *local,
|
||||
|
@ -341,7 +333,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
spin_lock_init(&sta->lock);
|
||||
spin_lock_init(&sta->ps_lock);
|
||||
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
|
||||
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
||||
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||||
mutex_init(&sta->ampdu_mlme.mtx);
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
|
@ -358,7 +350,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
sta->sta_state = IEEE80211_STA_NONE;
|
||||
|
||||
do_posix_clock_monotonic_gettime(&uptime);
|
||||
ktime_get_ts(&uptime);
|
||||
sta->last_connected = uptime.tv_sec;
|
||||
ewma_init(&sta->avg_signal, 1024, 8);
|
||||
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
|
||||
|
@ -1141,8 +1133,15 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
|
|||
}
|
||||
|
||||
ieee80211_add_pending_skbs(local, &pending);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
|
||||
/* now we're no longer in the deliver code */
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
||||
|
||||
/* The station might have polled and then woken up before we responded,
|
||||
* so clear these flags now to avoid them sticking around.
|
||||
*/
|
||||
clear_sta_flag(sta, WLAN_STA_PSPOLL);
|
||||
clear_sta_flag(sta, WLAN_STA_UAPSD);
|
||||
spin_unlock(&sta->ps_lock);
|
||||
|
||||
atomic_dec(&ps->num_sta_ps);
|
||||
|
@ -1543,10 +1542,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
|
|||
|
||||
trace_api_sta_block_awake(sta->local, pubsta, block);
|
||||
|
||||
if (block)
|
||||
if (block) {
|
||||
set_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
|
||||
ieee80211_queue_work(hw, &sta->drv_unblock_wk);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER))
|
||||
return;
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
set_sta_flag(sta, WLAN_STA_PS_DELIVER);
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
ieee80211_queue_work(hw, &sta->drv_deliver_wk);
|
||||
} else if (test_sta_flag(sta, WLAN_STA_PSPOLL) ||
|
||||
test_sta_flag(sta, WLAN_STA_UAPSD)) {
|
||||
/* must be asleep in this case */
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
ieee80211_queue_work(hw, &sta->drv_deliver_wk);
|
||||
} else {
|
||||
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_sta_block_awake);
|
||||
|
||||
|
@ -1704,3 +1719,137 @@ u8 sta_info_tx_streams(struct sta_info *sta)
|
|||
return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
|
||||
>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
|
||||
}
|
||||
|
||||
void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct rate_control_ref *ref = local->rate_ctrl;
|
||||
struct timespec uptime;
|
||||
u64 packets = 0;
|
||||
u32 thr = 0;
|
||||
int i, ac;
|
||||
|
||||
sinfo->generation = sdata->local->sta_generation;
|
||||
|
||||
sinfo->filled = STATION_INFO_INACTIVE_TIME |
|
||||
STATION_INFO_RX_BYTES64 |
|
||||
STATION_INFO_TX_BYTES64 |
|
||||
STATION_INFO_RX_PACKETS |
|
||||
STATION_INFO_TX_PACKETS |
|
||||
STATION_INFO_TX_RETRIES |
|
||||
STATION_INFO_TX_FAILED |
|
||||
STATION_INFO_TX_BITRATE |
|
||||
STATION_INFO_RX_BITRATE |
|
||||
STATION_INFO_RX_DROP_MISC |
|
||||
STATION_INFO_BSS_PARAM |
|
||||
STATION_INFO_CONNECTED_TIME |
|
||||
STATION_INFO_STA_FLAGS |
|
||||
STATION_INFO_BEACON_LOSS_COUNT;
|
||||
|
||||
ktime_get_ts(&uptime);
|
||||
sinfo->connected_time = uptime.tv_sec - sta->last_connected;
|
||||
|
||||
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
|
||||
sinfo->tx_bytes = 0;
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
sinfo->tx_bytes += sta->tx_bytes[ac];
|
||||
packets += sta->tx_packets[ac];
|
||||
}
|
||||
sinfo->tx_packets = packets;
|
||||
sinfo->rx_bytes = sta->rx_bytes;
|
||||
sinfo->rx_packets = sta->rx_packets;
|
||||
sinfo->tx_retries = sta->tx_retry_count;
|
||||
sinfo->tx_failed = sta->tx_retry_failed;
|
||||
sinfo->rx_dropped_misc = sta->rx_dropped;
|
||||
sinfo->beacon_loss_count = sta->beacon_loss_count;
|
||||
|
||||
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
|
||||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
|
||||
sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
|
||||
if (!local->ops->get_rssi ||
|
||||
drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
|
||||
sinfo->signal = (s8)sta->last_signal;
|
||||
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
|
||||
}
|
||||
if (sta->chains) {
|
||||
sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
|
||||
STATION_INFO_CHAIN_SIGNAL_AVG;
|
||||
|
||||
sinfo->chains = sta->chains;
|
||||
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
||||
sinfo->chain_signal[i] = sta->chain_signal_last[i];
|
||||
sinfo->chain_signal_avg[i] =
|
||||
(s8) -ewma_read(&sta->chain_signal_avg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
|
||||
sta_set_rate_info_rx(sta, &sinfo->rxrate);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
sinfo->filled |= STATION_INFO_LLID |
|
||||
STATION_INFO_PLID |
|
||||
STATION_INFO_PLINK_STATE |
|
||||
STATION_INFO_LOCAL_PM |
|
||||
STATION_INFO_PEER_PM |
|
||||
STATION_INFO_NONPEER_PM;
|
||||
|
||||
sinfo->llid = sta->llid;
|
||||
sinfo->plid = sta->plid;
|
||||
sinfo->plink_state = sta->plink_state;
|
||||
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
||||
sinfo->filled |= STATION_INFO_T_OFFSET;
|
||||
sinfo->t_offset = sta->t_offset;
|
||||
}
|
||||
sinfo->local_pm = sta->local_pm;
|
||||
sinfo->peer_pm = sta->peer_pm;
|
||||
sinfo->nonpeer_pm = sta->nonpeer_pm;
|
||||
#endif
|
||||
}
|
||||
|
||||
sinfo->bss_param.flags = 0;
|
||||
if (sdata->vif.bss_conf.use_cts_prot)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
|
||||
if (sdata->vif.bss_conf.use_short_preamble)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
|
||||
if (sdata->vif.bss_conf.use_short_slot)
|
||||
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
|
||||
sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
|
||||
sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
|
||||
|
||||
sinfo->sta_flags.set = 0;
|
||||
sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
||||
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
||||
BIT(NL80211_STA_FLAG_WME) |
|
||||
BIT(NL80211_STA_FLAG_MFP) |
|
||||
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
||||
BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
||||
BIT(NL80211_STA_FLAG_TDLS_PEER);
|
||||
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
|
||||
if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
|
||||
if (test_sta_flag(sta, WLAN_STA_WME))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
|
||||
if (test_sta_flag(sta, WLAN_STA_MFP))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
|
||||
if (test_sta_flag(sta, WLAN_STA_AUTH))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
|
||||
if (test_sta_flag(sta, WLAN_STA_ASSOC))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
|
||||
|
||||
/* check if the driver has a SW RC implementation */
|
||||
if (ref && ref->ops->get_expected_throughput)
|
||||
thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
|
||||
else
|
||||
thr = drv_get_expected_throughput(local, &sta->sta);
|
||||
|
||||
if (thr != 0) {
|
||||
sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
|
||||
sinfo->expected_throughput = thr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
* @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
|
||||
* @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
|
||||
* @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
|
||||
* @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
|
||||
* until pending frames are delivered
|
||||
*/
|
||||
enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_AUTH,
|
||||
|
@ -82,6 +84,7 @@ enum ieee80211_sta_info_flags {
|
|||
WLAN_STA_TOFFSET_KNOWN,
|
||||
WLAN_STA_MPSP_OWNER,
|
||||
WLAN_STA_MPSP_RECIPIENT,
|
||||
WLAN_STA_PS_DELIVER,
|
||||
};
|
||||
|
||||
#define ADDBA_RESP_INTERVAL HZ
|
||||
|
@ -265,7 +268,7 @@ struct ieee80211_tx_latency_stat {
|
|||
* @last_rx_rate_vht_nss: rx status nss of last data packet
|
||||
* @lock: used for locking all fields that require locking, see comments
|
||||
* in the header file.
|
||||
* @drv_unblock_wk: used for driver PS unblocking
|
||||
* @drv_deliver_wk: used for delivering frames after driver PS unblocking
|
||||
* @listen_interval: listen interval of this station, when we're acting as AP
|
||||
* @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
|
||||
* @ps_lock: used for powersave (when mac80211 is the AP) related locking
|
||||
|
@ -278,7 +281,6 @@ struct ieee80211_tx_latency_stat {
|
|||
* @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
|
||||
* @rx_packets: Number of MSDUs received from this STA
|
||||
* @rx_bytes: Number of bytes received from this STA
|
||||
* @wep_weak_iv_count: number of weak WEP IVs received from this station
|
||||
* @last_rx: time (in jiffies) when last frame was received from this STA
|
||||
* @last_connected: time (in seconds) when a station got connected
|
||||
* @num_duplicates: number of duplicate frames received from this STA
|
||||
|
@ -303,7 +305,6 @@ struct ieee80211_tx_latency_stat {
|
|||
* @plid: Peer link ID
|
||||
* @reason: Cancel reason on PLINK_HOLDING state
|
||||
* @plink_retries: Retries in establishment
|
||||
* @ignore_plink_timer: ignore the peer-link timer (used internally)
|
||||
* @plink_state: peer link state
|
||||
* @plink_timeout: timeout of peer link
|
||||
* @plink_timer: peer link watch timer
|
||||
|
@ -345,7 +346,7 @@ struct sta_info {
|
|||
void *rate_ctrl_priv;
|
||||
spinlock_t lock;
|
||||
|
||||
struct work_struct drv_unblock_wk;
|
||||
struct work_struct drv_deliver_wk;
|
||||
|
||||
u16 listen_interval;
|
||||
|
||||
|
@ -367,7 +368,6 @@ struct sta_info {
|
|||
/* Updated from RX path only, no locking requirements */
|
||||
unsigned long rx_packets;
|
||||
u64 rx_bytes;
|
||||
unsigned long wep_weak_iv_count;
|
||||
unsigned long last_rx;
|
||||
long last_connected;
|
||||
unsigned long num_duplicates;
|
||||
|
@ -418,7 +418,6 @@ struct sta_info {
|
|||
u16 plid;
|
||||
u16 reason;
|
||||
u8 plink_retries;
|
||||
bool ignore_plink_timer;
|
||||
enum nl80211_plink_state plink_state;
|
||||
u32 plink_timeout;
|
||||
struct timer_list plink_timer;
|
||||
|
@ -628,6 +627,8 @@ void sta_set_rate_info_tx(struct sta_info *sta,
|
|||
struct rate_info *rinfo);
|
||||
void sta_set_rate_info_rx(struct sta_info *sta,
|
||||
struct rate_info *rinfo);
|
||||
void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo);
|
||||
|
||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
||||
unsigned long exp_time);
|
||||
u8 sta_info_tx_streams(struct sta_info *sta);
|
||||
|
|
|
@ -473,8 +473,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
|
|||
struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr)
|
||||
{
|
||||
ktime_t skb_dprt;
|
||||
struct timespec dprt_time;
|
||||
u32 msrmnt;
|
||||
u16 tid;
|
||||
u8 *qc;
|
||||
|
@ -506,9 +504,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
|
|||
|
||||
tx_lat = &sta->tx_lat[tid];
|
||||
|
||||
ktime_get_ts(&dprt_time); /* time stamp completion time */
|
||||
skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec);
|
||||
msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv));
|
||||
/* Calculate the latency */
|
||||
msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
|
||||
|
||||
if (tx_lat->max < msrmnt) /* update stats */
|
||||
tx_lat->max = msrmnt;
|
||||
|
|
|
@ -8,7 +8,30 @@
|
|||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
/* give usermode some time for retries in setting up the TDLS session */
|
||||
#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ)
|
||||
|
||||
void ieee80211_tdls_peer_del_work(struct work_struct *wk)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_local *local;
|
||||
|
||||
sdata = container_of(wk, struct ieee80211_sub_if_data,
|
||||
tdls_peer_del_work.work);
|
||||
local = sdata->local;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer)) {
|
||||
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
|
||||
sta_info_destroy_addr(sdata, sdata->tdls_peer);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
|
||||
{
|
||||
|
@ -168,28 +191,20 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
static int
|
||||
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code,
|
||||
u8 dialog_token, u16 status_code,
|
||||
u32 peer_capability, bool initiator,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool send_direct;
|
||||
const u8 *init_addr, *rsp_addr;
|
||||
int ret;
|
||||
|
||||
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* make sure we are in managed mode, and associated */
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION ||
|
||||
!sdata->u.mgd.associated)
|
||||
return -EINVAL;
|
||||
|
||||
tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
|
||||
action_code, peer);
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
max(sizeof(struct ieee80211_mgmt),
|
||||
sizeof(struct ieee80211_tdls_data)) +
|
||||
|
@ -230,27 +245,42 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
|
||||
|
||||
/* the TDLS link IE is always added last */
|
||||
/* sanity check for initiator */
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
/* we are the initiator */
|
||||
ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
|
||||
sdata->u.mgd.bssid);
|
||||
if (!initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
/* we are the responder */
|
||||
ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
|
||||
sdata->u.mgd.bssid);
|
||||
if (initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
/* any value is ok */
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (initiator) {
|
||||
init_addr = sdata->vif.addr;
|
||||
rsp_addr = peer;
|
||||
} else {
|
||||
init_addr = peer;
|
||||
rsp_addr = sdata->vif.addr;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
|
||||
sdata->u.mgd.bssid);
|
||||
|
||||
if (send_direct) {
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
|
@ -284,11 +314,171 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability, bool initiator,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
|
||||
/* we don't support concurrent TDLS peer setups */
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer) &&
|
||||
!ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure we have a STA representing the peer so we drop or buffer
|
||||
* non-TDLS-setup frames to the peer. We can't send other packets
|
||||
* during setup through the AP path
|
||||
*/
|
||||
rcu_read_lock();
|
||||
if (!sta_info_get(sdata, peer)) {
|
||||
rcu_read_unlock();
|
||||
ret = -ENOLINK;
|
||||
goto exit;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memcpy(sdata->tdls_peer, peer, ETH_ALEN);
|
||||
ieee80211_queue_delayed_work(&sdata->local->hw,
|
||||
&sdata->tdls_peer_del_work,
|
||||
TDLS_PEER_SETUP_TIMEOUT);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&local->mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* No packets can be transmitted to the peer via the AP during setup -
|
||||
* the STA is set as a TDLS peer, but is not authorized.
|
||||
* During teardown, we prevent direct transmissions by stopping the
|
||||
* queues and flushing all direct packets.
|
||||
*/
|
||||
ieee80211_stop_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
if (ret < 0)
|
||||
sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
|
||||
ret);
|
||||
|
||||
/*
|
||||
* Remove the STA AUTH flag to force further traffic through the AP. If
|
||||
* the STA was unreachable, it was already removed.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, peer);
|
||||
if (sta)
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
int ret;
|
||||
|
||||
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* make sure we are in managed mode, and associated */
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION ||
|
||||
!sdata->u.mgd.associated)
|
||||
return -EINVAL;
|
||||
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
|
||||
action_code, dialog_token,
|
||||
status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
/*
|
||||
* Protect the discovery so we can hear the TDLS discovery
|
||||
* response frame. It is transmitted directly and not buffered
|
||||
* by the AP.
|
||||
*/
|
||||
drv_mgd_protect_tdls_discover(sdata->local, sdata);
|
||||
/* fall-through */
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
/* no special handling */
|
||||
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
|
||||
action_code,
|
||||
dialog_token,
|
||||
status_code,
|
||||
peer_capability,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
|
||||
action_code, peer, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, enum nl80211_tdls_operation oper)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret;
|
||||
|
||||
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
||||
return -ENOTSUPP;
|
||||
|
@ -296,6 +486,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return -EINVAL;
|
||||
|
||||
switch (oper) {
|
||||
case NL80211_TDLS_ENABLE_LINK:
|
||||
case NL80211_TDLS_DISABLE_LINK:
|
||||
break;
|
||||
case NL80211_TDLS_TEARDOWN:
|
||||
case NL80211_TDLS_SETUP:
|
||||
case NL80211_TDLS_DISCOVERY_REQ:
|
||||
/* We don't support in-driver setup/teardown/discovery */
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
|
||||
|
||||
switch (oper) {
|
||||
|
@ -304,22 +506,49 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|||
sta = sta_info_get(sdata, peer);
|
||||
if (!sta) {
|
||||
rcu_read_unlock();
|
||||
return -ENOLINK;
|
||||
ret = -ENOLINK;
|
||||
break;
|
||||
}
|
||||
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
||||
rcu_read_unlock();
|
||||
|
||||
WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
|
||||
!ether_addr_equal(sdata->tdls_peer, peer));
|
||||
ret = 0;
|
||||
break;
|
||||
case NL80211_TDLS_DISABLE_LINK:
|
||||
return sta_info_destroy_addr(sdata, peer);
|
||||
case NL80211_TDLS_TEARDOWN:
|
||||
case NL80211_TDLS_SETUP:
|
||||
case NL80211_TDLS_DISCOVERY_REQ:
|
||||
/* We don't support in-driver setup/teardown/discovery */
|
||||
return -ENOTSUPP;
|
||||
/* flush a potentially queued teardown packet */
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
ret = sta_info_destroy_addr(sdata, peer);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
ret = -ENOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
cancel_delayed_work(&sdata->tdls_peer_del_work);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
}
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||
enum nl80211_tdls_operation oper,
|
||||
u16 reason_code, gfp_t gfp)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) {
|
||||
sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
|
||||
oper);
|
||||
return;
|
||||
}
|
||||
|
||||
cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tdls_oper_request);
|
||||
|
|
|
@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
|
|||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(local_chanctx,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx),
|
||||
|
|
|
@ -250,7 +250,8 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
|
|||
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_PS);
|
||||
IEEE80211_QUEUE_STOP_REASON_PS,
|
||||
false);
|
||||
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&local->dynamic_ps_disable_work);
|
||||
|
@ -469,7 +470,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
|
|||
return TX_CONTINUE;
|
||||
|
||||
if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DRIVER)) &&
|
||||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) &&
|
||||
!(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
|
||||
int ac = skb_get_queue_mapping(tx->skb);
|
||||
|
||||
|
@ -486,7 +488,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
|
|||
* ahead and Tx the packet.
|
||||
*/
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
||||
!test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
|
||||
!test_sta_flag(sta, WLAN_STA_PS_DRIVER) &&
|
||||
!test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
|
||||
spin_unlock(&sta->ps_lock);
|
||||
return TX_CONTINUE;
|
||||
}
|
||||
|
@ -1618,12 +1621,12 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_channel *chan;
|
||||
struct ieee80211_radiotap_header *prthdr =
|
||||
(struct ieee80211_radiotap_header *)skb->data;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct ieee80211_sub_if_data *tmp_sdata, *sdata;
|
||||
struct cfg80211_chan_def *chandef;
|
||||
u16 len_rthdr;
|
||||
int hdrlen;
|
||||
|
||||
|
@ -1721,9 +1724,9 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
}
|
||||
|
||||
if (chanctx_conf)
|
||||
chan = chanctx_conf->def.chan;
|
||||
chandef = &chanctx_conf->def;
|
||||
else if (!local->use_chanctx)
|
||||
chan = local->_oper_chandef.chan;
|
||||
chandef = &local->_oper_chandef;
|
||||
else
|
||||
goto fail_rcu;
|
||||
|
||||
|
@ -1743,10 +1746,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
* radar detection by itself. We can do that later by adding a
|
||||
* monitor flag interfaces used for AP support.
|
||||
*/
|
||||
if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))
|
||||
if (!cfg80211_reg_can_beacon(local->hw.wiphy, chandef,
|
||||
sdata->vif.type))
|
||||
goto fail_rcu;
|
||||
|
||||
ieee80211_xmit(sdata, skb, chan->band);
|
||||
ieee80211_xmit(sdata, skb, chandef->chan->band);
|
||||
rcu_read_unlock();
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
@ -1767,15 +1771,12 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct timespec skb_arv;
|
||||
struct ieee80211_tx_latency_bin_ranges *tx_latency;
|
||||
|
||||
tx_latency = rcu_dereference(local->tx_latency);
|
||||
if (!tx_latency)
|
||||
return;
|
||||
|
||||
ktime_get_ts(&skb_arv);
|
||||
skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec);
|
||||
skb->tstamp = ktime_get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1810,7 +1811,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
|||
int nh_pos, h_pos;
|
||||
struct sta_info *sta = NULL;
|
||||
bool wme_sta = false, authorized = false, tdls_auth = false;
|
||||
bool tdls_direct = false;
|
||||
bool tdls_peer = false, tdls_setup_frame = false;
|
||||
bool multicast;
|
||||
u32 info_flags = 0;
|
||||
u16 info_id = 0;
|
||||
|
@ -1952,34 +1953,35 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
|||
#endif
|
||||
case NL80211_IFTYPE_STATION:
|
||||
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
|
||||
bool tdls_peer = false;
|
||||
|
||||
sta = sta_info_get(sdata, skb->data);
|
||||
if (sta) {
|
||||
authorized = test_sta_flag(sta,
|
||||
WLAN_STA_AUTHORIZED);
|
||||
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
|
||||
tdls_peer = test_sta_flag(sta,
|
||||
WLAN_STA_TDLS_PEER);
|
||||
WLAN_STA_TDLS_PEER);
|
||||
tdls_auth = test_sta_flag(sta,
|
||||
WLAN_STA_TDLS_PEER_AUTH);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the TDLS link is enabled, send everything
|
||||
* directly. Otherwise, allow TDLS setup frames
|
||||
* to be transmitted indirectly.
|
||||
*/
|
||||
tdls_direct = tdls_peer && (tdls_auth ||
|
||||
!(ethertype == ETH_P_TDLS && skb->len > 14 &&
|
||||
skb->data[14] == WLAN_TDLS_SNAP_RFTYPE));
|
||||
if (tdls_peer)
|
||||
tdls_setup_frame =
|
||||
ethertype == ETH_P_TDLS &&
|
||||
skb->len > 14 &&
|
||||
skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
|
||||
}
|
||||
|
||||
if (tdls_direct) {
|
||||
/* link during setup - throw out frames to peer */
|
||||
if (!tdls_auth)
|
||||
goto fail_rcu;
|
||||
/*
|
||||
* TDLS link during setup - throw out frames to peer. We allow
|
||||
* TDLS-setup frames to unauthorized peers for the special case
|
||||
* of a link teardown after a TDLS sta is removed due to being
|
||||
* unreachable.
|
||||
*/
|
||||
if (tdls_peer && !tdls_auth && !tdls_setup_frame)
|
||||
goto fail_rcu;
|
||||
|
||||
/* send direct packets to authorized TDLS peers */
|
||||
if (tdls_peer && tdls_auth) {
|
||||
/* DA SA BSSID */
|
||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
|
@ -2423,7 +2425,7 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
|
|||
u8 *beacon_data;
|
||||
size_t beacon_data_len;
|
||||
int i;
|
||||
u8 count = sdata->csa_current_counter;
|
||||
u8 count = beacon->csa_current_counter;
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
|
@ -2442,46 +2444,53 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) {
|
||||
u16 counter_offset_beacon =
|
||||
sdata->csa_counter_offset_beacon[i];
|
||||
u16 counter_offset_presp = sdata->csa_counter_offset_presp[i];
|
||||
resp = rcu_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
if (counter_offset_beacon) {
|
||||
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
|
||||
return;
|
||||
|
||||
beacon_data[counter_offset_beacon] = count;
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
||||
counter_offset_presp) {
|
||||
rcu_read_lock();
|
||||
resp = rcu_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
/* If nl80211 accepted the offset, this should
|
||||
* not happen.
|
||||
*/
|
||||
if (WARN_ON(!resp)) {
|
||||
if (beacon->csa_counter_offsets[i]) {
|
||||
if (WARN_ON_ONCE(beacon->csa_counter_offsets[i] >=
|
||||
beacon_data_len)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
resp->data[counter_offset_presp] = count;
|
||||
rcu_read_unlock();
|
||||
|
||||
beacon_data[beacon->csa_counter_offsets[i]] = count;
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP && resp)
|
||||
resp->data[resp->csa_counter_offsets[i]] = count;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct beacon_data *beacon = NULL;
|
||||
u8 count = 0;
|
||||
|
||||
sdata->csa_current_counter--;
|
||||
rcu_read_lock();
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
beacon = rcu_dereference(sdata->u.ap.beacon);
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
beacon = rcu_dereference(sdata->u.ibss.presp);
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
beacon = rcu_dereference(sdata->u.mesh.beacon);
|
||||
|
||||
if (!beacon)
|
||||
goto unlock;
|
||||
|
||||
beacon->csa_current_counter--;
|
||||
|
||||
/* the counter should never reach 0 */
|
||||
WARN_ON(!sdata->csa_current_counter);
|
||||
WARN_ON_ONCE(!beacon->csa_current_counter);
|
||||
count = beacon->csa_current_counter;
|
||||
|
||||
return sdata->csa_current_counter;
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_csa_update_counter);
|
||||
|
||||
|
@ -2491,7 +2500,6 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
|||
struct beacon_data *beacon = NULL;
|
||||
u8 *beacon_data;
|
||||
size_t beacon_data_len;
|
||||
int counter_beacon = sdata->csa_counter_offset_beacon[0];
|
||||
int ret = false;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
|
@ -2529,10 +2537,13 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (WARN_ON(counter_beacon > beacon_data_len))
|
||||
if (!beacon->csa_counter_offsets[0])
|
||||
goto out;
|
||||
|
||||
if (beacon_data[counter_beacon] == 1)
|
||||
if (WARN_ON_ONCE(beacon->csa_counter_offsets[0] > beacon_data_len))
|
||||
goto out;
|
||||
|
||||
if (beacon_data[beacon->csa_counter_offsets[0]] == 1)
|
||||
ret = true;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
@ -2548,6 +2559,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|||
bool is_template)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct beacon_data *beacon = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct ieee80211_tx_info *info;
|
||||
struct ieee80211_sub_if_data *sdata = NULL;
|
||||
|
@ -2569,10 +2581,10 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||||
struct ieee80211_if_ap *ap = &sdata->u.ap;
|
||||
struct beacon_data *beacon = rcu_dereference(ap->beacon);
|
||||
|
||||
beacon = rcu_dereference(ap->beacon);
|
||||
if (beacon) {
|
||||
if (sdata->vif.csa_active) {
|
||||
if (beacon->csa_counter_offsets[0]) {
|
||||
if (!is_template)
|
||||
ieee80211_csa_update_counter(vif);
|
||||
|
||||
|
@ -2613,37 +2625,37 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|||
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct beacon_data *presp = rcu_dereference(ifibss->presp);
|
||||
|
||||
if (!presp)
|
||||
beacon = rcu_dereference(ifibss->presp);
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.csa_active) {
|
||||
if (beacon->csa_counter_offsets[0]) {
|
||||
if (!is_template)
|
||||
ieee80211_csa_update_counter(vif);
|
||||
|
||||
ieee80211_set_csa(sdata, presp);
|
||||
ieee80211_set_csa(sdata, beacon);
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
|
||||
skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
|
||||
local->hw.extra_beacon_tailroom);
|
||||
if (!skb)
|
||||
goto out;
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
memcpy(skb_put(skb, presp->head_len), presp->head,
|
||||
presp->head_len);
|
||||
memcpy(skb_put(skb, beacon->head_len), beacon->head,
|
||||
beacon->head_len);
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_BEACON);
|
||||
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
|
||||
|
||||
if (!bcn)
|
||||
beacon = rcu_dereference(ifmsh->beacon);
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.csa_active) {
|
||||
if (beacon->csa_counter_offsets[0]) {
|
||||
if (!is_template)
|
||||
/* TODO: For mesh csa_counter is in TU, so
|
||||
* decrementing it by one isn't correct, but
|
||||
|
@ -2652,40 +2664,42 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
|||
*/
|
||||
ieee80211_csa_update_counter(vif);
|
||||
|
||||
ieee80211_set_csa(sdata, bcn);
|
||||
ieee80211_set_csa(sdata, beacon);
|
||||
}
|
||||
|
||||
if (ifmsh->sync_ops)
|
||||
ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
|
||||
ifmsh->sync_ops->adjust_tbtt(sdata, beacon);
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom +
|
||||
bcn->head_len +
|
||||
beacon->head_len +
|
||||
256 + /* TIM IE */
|
||||
bcn->tail_len +
|
||||
beacon->tail_len +
|
||||
local->hw.extra_beacon_tailroom);
|
||||
if (!skb)
|
||||
goto out;
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
|
||||
memcpy(skb_put(skb, beacon->head_len), beacon->head,
|
||||
beacon->head_len);
|
||||
ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
|
||||
|
||||
if (offs) {
|
||||
offs->tim_offset = bcn->head_len;
|
||||
offs->tim_length = skb->len - bcn->head_len;
|
||||
offs->tim_offset = beacon->head_len;
|
||||
offs->tim_length = skb->len - beacon->head_len;
|
||||
}
|
||||
|
||||
memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
|
||||
memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
|
||||
beacon->tail_len);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* CSA offsets */
|
||||
if (offs) {
|
||||
if (offs && beacon) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
|
||||
u16 csa_off = sdata->csa_counter_offset_beacon[i];
|
||||
u16 csa_off = beacon->csa_counter_offsets[i];
|
||||
|
||||
if (!csa_off)
|
||||
continue;
|
||||
|
|
|
@ -317,7 +317,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
|
|||
}
|
||||
|
||||
static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
|
@ -329,7 +330,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
|||
if (!test_bit(reason, &local->queue_stop_reasons[queue]))
|
||||
return;
|
||||
|
||||
__clear_bit(reason, &local->queue_stop_reasons[queue]);
|
||||
if (!refcounted)
|
||||
local->q_stop_reasons[queue][reason] = 0;
|
||||
else
|
||||
local->q_stop_reasons[queue][reason]--;
|
||||
|
||||
if (local->q_stop_reasons[queue][reason] == 0)
|
||||
__clear_bit(reason, &local->queue_stop_reasons[queue]);
|
||||
|
||||
if (local->queue_stop_reasons[queue] != 0)
|
||||
/* someone still has this queue stopped */
|
||||
|
@ -344,25 +351,28 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
|||
}
|
||||
|
||||
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
__ieee80211_wake_queue(hw, queue, reason);
|
||||
__ieee80211_wake_queue(hw, queue, reason, refcounted);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
|
||||
{
|
||||
ieee80211_wake_queue_by_reason(hw, queue,
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER);
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
||||
false);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_wake_queue);
|
||||
|
||||
static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
@ -373,10 +383,13 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
|
|||
if (WARN_ON(queue >= hw->queues))
|
||||
return;
|
||||
|
||||
if (test_bit(reason, &local->queue_stop_reasons[queue]))
|
||||
return;
|
||||
if (!refcounted)
|
||||
local->q_stop_reasons[queue][reason] = 1;
|
||||
else
|
||||
local->q_stop_reasons[queue][reason]++;
|
||||
|
||||
__set_bit(reason, &local->queue_stop_reasons[queue]);
|
||||
if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
|
||||
return;
|
||||
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||
n_acs = 1;
|
||||
|
@ -398,20 +411,22 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
|
|||
}
|
||||
|
||||
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
__ieee80211_stop_queue(hw, queue, reason);
|
||||
__ieee80211_stop_queue(hw, queue, reason, refcounted);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
|
||||
{
|
||||
ieee80211_stop_queue_by_reason(hw, queue,
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER);
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
||||
false);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_stop_queue);
|
||||
|
||||
|
@ -429,9 +444,11 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
|
||||
__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
__skb_queue_tail(&local->pending[queue], skb);
|
||||
__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
|
||||
__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -455,20 +472,23 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
|||
queue = info->hw_queue;
|
||||
|
||||
__ieee80211_stop_queue(hw, queue,
|
||||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
|
||||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
|
||||
__skb_queue_tail(&local->pending[queue], skb);
|
||||
}
|
||||
|
||||
for (i = 0; i < hw->queues; i++)
|
||||
__ieee80211_wake_queue(hw, i,
|
||||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
|
||||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
|
||||
unsigned long queues,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
unsigned long flags;
|
||||
|
@ -477,7 +497,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
|
|||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
|
||||
for_each_set_bit(i, &queues, hw->queues)
|
||||
__ieee80211_stop_queue(hw, i, reason);
|
||||
__ieee80211_stop_queue(hw, i, reason, refcounted);
|
||||
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
@ -485,7 +505,8 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
|
|||
void ieee80211_stop_queues(struct ieee80211_hw *hw)
|
||||
{
|
||||
ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER);
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
||||
false);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_stop_queues);
|
||||
|
||||
|
@ -508,7 +529,8 @@ EXPORT_SYMBOL(ieee80211_queue_stopped);
|
|||
|
||||
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||
unsigned long queues,
|
||||
enum queue_stop_reason reason)
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
unsigned long flags;
|
||||
|
@ -517,7 +539,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
|||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
|
||||
for_each_set_bit(i, &queues, hw->queues)
|
||||
__ieee80211_wake_queue(hw, i, reason);
|
||||
__ieee80211_wake_queue(hw, i, reason, refcounted);
|
||||
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
@ -525,17 +547,16 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
|||
void ieee80211_wake_queues(struct ieee80211_hw *hw)
|
||||
{
|
||||
ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER);
|
||||
IEEE80211_QUEUE_STOP_REASON_DRIVER,
|
||||
false);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_wake_queues);
|
||||
|
||||
void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
static unsigned int
|
||||
ieee80211_get_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
u32 queues;
|
||||
|
||||
if (!local->ops->flush)
|
||||
return;
|
||||
unsigned int queues;
|
||||
|
||||
if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
|
||||
int ac;
|
||||
|
@ -551,13 +572,46 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
|
|||
queues = BIT(local->hw.queues) - 1;
|
||||
}
|
||||
|
||||
ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
||||
return queues;
|
||||
}
|
||||
|
||||
void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
unsigned int queues;
|
||||
|
||||
if (!local->ops->flush)
|
||||
return;
|
||||
|
||||
queues = ieee80211_get_vif_queues(local, sdata);
|
||||
|
||||
ieee80211_stop_queues_by_reason(&local->hw, queues,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
||||
false);
|
||||
|
||||
drv_flush(local, sdata, queues, false);
|
||||
|
||||
ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
||||
ieee80211_wake_queues_by_reason(&local->hw, queues,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
||||
false);
|
||||
}
|
||||
|
||||
void ieee80211_stop_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum queue_stop_reason reason)
|
||||
{
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
ieee80211_get_vif_queues(local, sdata),
|
||||
reason, true);
|
||||
}
|
||||
|
||||
void ieee80211_wake_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum queue_stop_reason reason)
|
||||
{
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
ieee80211_get_vif_queues(local, sdata),
|
||||
reason, true);
|
||||
}
|
||||
|
||||
static void __iterate_active_interfaces(struct ieee80211_local *local,
|
||||
|
@ -1165,14 +1219,17 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
}
|
||||
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
size_t buffer_len, const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
|
||||
u8 *buffer, size_t buffer_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band,
|
||||
u32 rate_mask,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
size_t *offset)
|
||||
{
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 *pos = buffer, *end = buffer + buffer_len;
|
||||
size_t offset = 0, noffset;
|
||||
size_t noffset;
|
||||
int supp_rates_len, i;
|
||||
u8 rates[32];
|
||||
int num_rates;
|
||||
|
@ -1180,6 +1237,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
int shift;
|
||||
u32 rate_flags;
|
||||
|
||||
*offset = 0;
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
if (WARN_ON_ONCE(!sband))
|
||||
return 0;
|
||||
|
@ -1218,12 +1277,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
noffset = ieee80211_ie_split(ie, ie_len,
|
||||
before_extrates,
|
||||
ARRAY_SIZE(before_extrates),
|
||||
offset);
|
||||
if (end - pos < noffset - offset)
|
||||
*offset);
|
||||
if (end - pos < noffset - *offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
offset = noffset;
|
||||
memcpy(pos, ie + *offset, noffset - *offset);
|
||||
pos += noffset - *offset;
|
||||
*offset = noffset;
|
||||
}
|
||||
|
||||
ext_rates_len = num_rates - supp_rates_len;
|
||||
|
@ -1257,12 +1316,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
};
|
||||
noffset = ieee80211_ie_split(ie, ie_len,
|
||||
before_ht, ARRAY_SIZE(before_ht),
|
||||
offset);
|
||||
if (end - pos < noffset - offset)
|
||||
*offset);
|
||||
if (end - pos < noffset - *offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
offset = noffset;
|
||||
memcpy(pos, ie + *offset, noffset - *offset);
|
||||
pos += noffset - *offset;
|
||||
*offset = noffset;
|
||||
}
|
||||
|
||||
if (sband->ht_cap.ht_supported) {
|
||||
|
@ -1297,12 +1356,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
};
|
||||
noffset = ieee80211_ie_split(ie, ie_len,
|
||||
before_vht, ARRAY_SIZE(before_vht),
|
||||
offset);
|
||||
if (end - pos < noffset - offset)
|
||||
*offset);
|
||||
if (end - pos < noffset - *offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
offset = noffset;
|
||||
memcpy(pos, ie + *offset, noffset - *offset);
|
||||
pos += noffset - *offset;
|
||||
*offset = noffset;
|
||||
}
|
||||
|
||||
if (sband->vht_cap.vht_supported) {
|
||||
|
@ -1312,21 +1371,54 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
sband->vht_cap.cap);
|
||||
}
|
||||
|
||||
/* add any remaining custom IEs */
|
||||
if (ie && ie_len) {
|
||||
noffset = ie_len;
|
||||
if (end - pos < noffset - offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
}
|
||||
|
||||
return pos - buffer;
|
||||
out_err:
|
||||
WARN_ONCE(1, "not enough space for preq IEs\n");
|
||||
return pos - buffer;
|
||||
}
|
||||
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
size_t buffer_len,
|
||||
struct ieee80211_scan_ies *ie_desc,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u8 bands_used, u32 *rate_masks,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
|
||||
int i;
|
||||
|
||||
memset(ie_desc, 0, sizeof(*ie_desc));
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
if (bands_used & BIT(i)) {
|
||||
pos += ieee80211_build_preq_ies_band(local,
|
||||
buffer + pos,
|
||||
buffer_len - pos,
|
||||
ie, ie_len, i,
|
||||
rate_masks[i],
|
||||
chandef,
|
||||
&custom_ie_offset);
|
||||
ie_desc->ies[i] = buffer + old_pos;
|
||||
ie_desc->len[i] = pos - old_pos;
|
||||
old_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
/* add any remaining custom IEs */
|
||||
if (ie && ie_len) {
|
||||
if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
|
||||
"not enough space for preq custom IEs\n"))
|
||||
return pos;
|
||||
memcpy(buffer + pos, ie + custom_ie_offset,
|
||||
ie_len - custom_ie_offset);
|
||||
ie_desc->common_ies = buffer + pos;
|
||||
ie_desc->common_ie_len = ie_len - custom_ie_offset;
|
||||
pos += ie_len - custom_ie_offset;
|
||||
}
|
||||
|
||||
return pos;
|
||||
};
|
||||
|
||||
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *dst, u32 ratemask,
|
||||
struct ieee80211_channel *chan,
|
||||
|
@ -1339,6 +1431,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
int ies_len;
|
||||
u32 rate_masks[IEEE80211_NUM_BANDS] = {};
|
||||
struct ieee80211_scan_ies dummy_ie_desc;
|
||||
|
||||
/*
|
||||
* Do not send DS Channel parameter for directed probe requests
|
||||
|
@ -1356,10 +1450,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
rate_masks[chan->band] = ratemask;
|
||||
ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
|
||||
skb_tailroom(skb),
|
||||
ie, ie_len, chan->band,
|
||||
ratemask, &chandef);
|
||||
skb_tailroom(skb), &dummy_ie_desc,
|
||||
ie, ie_len, BIT(chan->band),
|
||||
rate_masks, &chandef);
|
||||
skb_put(skb, ies_len);
|
||||
|
||||
if (dst) {
|
||||
|
@ -1603,7 +1698,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
if (local->use_chanctx) {
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list)
|
||||
WARN_ON(drv_add_chanctx(local, ctx));
|
||||
if (ctx->replace_state !=
|
||||
IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
WARN_ON(drv_add_chanctx(local, ctx));
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
|
@ -1797,7 +1894,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
}
|
||||
|
||||
ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
|
||||
false);
|
||||
|
||||
/*
|
||||
* Reconfigure sched scan if it was interrupted by FW restart or
|
||||
|
@ -2835,6 +2933,35 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
|
|||
ps->dtim_count = dtim_count;
|
||||
}
|
||||
|
||||
static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
u8 radar_detect = 0;
|
||||
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
|
||||
if (sdata->reserved_radar_required)
|
||||
radar_detect |= BIT(sdata->reserved_chandef.width);
|
||||
|
||||
/*
|
||||
* An in-place reservation context should not have any assigned vifs
|
||||
* until it replaces the other context.
|
||||
*/
|
||||
WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
|
||||
!list_empty(&ctx->assigned_vifs));
|
||||
|
||||
list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
|
||||
if (sdata->radar_required)
|
||||
radar_detect |= BIT(sdata->vif.bss_conf.chandef.width);
|
||||
|
||||
return radar_detect;
|
||||
}
|
||||
|
||||
int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_chanctx_mode chanmode,
|
||||
|
@ -2876,8 +3003,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
|
|||
num[iftype] = 1;
|
||||
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->conf.radar_enabled)
|
||||
radar_detect |= BIT(ctx->conf.def.width);
|
||||
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
continue;
|
||||
radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
|
||||
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
|
||||
num_different_channels++;
|
||||
continue;
|
||||
|
@ -2934,10 +3062,12 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
|
|||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
continue;
|
||||
|
||||
num_different_channels++;
|
||||
|
||||
if (ctx->conf.radar_enabled)
|
||||
radar_detect |= BIT(ctx->conf.def.width);
|
||||
radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list)
|
||||
|
|
|
@ -271,22 +271,6 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb,
|
||||
struct ieee80211_key *key)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
unsigned int hdrlen;
|
||||
u8 *ivpos;
|
||||
u32 iv;
|
||||
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
ivpos = skb->data + hdrlen;
|
||||
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
|
||||
|
||||
return ieee80211_wep_weak_iv(iv, key->conf.keylen);
|
||||
}
|
||||
|
||||
ieee80211_rx_result
|
||||
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
|
@ -301,16 +285,12 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
|
|||
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
||||
if (skb_linearize(rx->skb))
|
||||
return RX_DROP_UNUSABLE;
|
||||
if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
|
||||
rx->sta->wep_weak_iv_count++;
|
||||
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
|
||||
return RX_DROP_UNUSABLE;
|
||||
} else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
|
||||
if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) +
|
||||
IEEE80211_WEP_IV_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
|
||||
rx->sta->wep_weak_iv_count++;
|
||||
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
|
||||
/* remove ICV */
|
||||
if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include "wext-compat.h"
|
||||
#include "ethtool.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
/* name for sysfs, %d is appended */
|
||||
|
@ -927,8 +926,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
|
|||
/* allow mac80211 to determine the timeout */
|
||||
wdev->ps_timeout = -1;
|
||||
|
||||
netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
|
||||
|
||||
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
|
||||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
|
||||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
#include <linux/utsname.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "ethtool.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
static void cfg80211_get_drvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
|
@ -23,84 +21,4 @@ static void cfg80211_get_drvinfo(struct net_device *dev,
|
|||
strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
|
||||
sizeof(info->bus_info));
|
||||
}
|
||||
|
||||
static int cfg80211_get_regs_len(struct net_device *dev)
|
||||
{
|
||||
/* For now, return 0... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs,
|
||||
void *data)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
regs->version = wdev->wiphy->hw_version;
|
||||
regs->len = 0;
|
||||
}
|
||||
|
||||
static void cfg80211_get_ringparam(struct net_device *dev,
|
||||
struct ethtool_ringparam *rp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
memset(rp, 0, sizeof(*rp));
|
||||
|
||||
if (rdev->ops->get_ringparam)
|
||||
rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending,
|
||||
&rp->rx_pending, &rp->rx_max_pending);
|
||||
}
|
||||
|
||||
static int cfg80211_set_ringparam(struct net_device *dev,
|
||||
struct ethtool_ringparam *rp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (rdev->ops->set_ringparam)
|
||||
return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int cfg80211_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
if (rdev->ops->get_et_sset_count)
|
||||
return rdev_get_et_sset_count(rdev, dev, sset);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void cfg80211_get_stats(struct net_device *dev,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
if (rdev->ops->get_et_stats)
|
||||
rdev_get_et_stats(rdev, dev, stats, data);
|
||||
}
|
||||
|
||||
static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
if (rdev->ops->get_et_strings)
|
||||
rdev_get_et_strings(rdev, dev, sset, data);
|
||||
}
|
||||
|
||||
const struct ethtool_ops cfg80211_ethtool_ops = {
|
||||
.get_drvinfo = cfg80211_get_drvinfo,
|
||||
.get_regs_len = cfg80211_get_regs_len,
|
||||
.get_regs = cfg80211_get_regs,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = cfg80211_get_ringparam,
|
||||
.set_ringparam = cfg80211_set_ringparam,
|
||||
.get_strings = cfg80211_get_strings,
|
||||
.get_ethtool_stats = cfg80211_get_stats,
|
||||
.get_sset_count = cfg80211_get_sset_count,
|
||||
};
|
||||
EXPORT_SYMBOL(cfg80211_get_drvinfo);
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __CFG80211_ETHTOOL__
|
||||
#define __CFG80211_ETHTOOL__
|
||||
|
||||
extern const struct ethtool_ops cfg80211_ethtool_ops;
|
||||
|
||||
#endif /* __CFG80211_ETHTOOL__ */
|
|
@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|||
[NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
|
||||
.len = IEEE80211_MAX_DATA_LEN },
|
||||
|
@ -6012,17 +6013,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|||
params.radar_required = true;
|
||||
}
|
||||
|
||||
/* TODO: I left this here for now. With channel switch, the
|
||||
* verification is a bit more complicated, because we only do
|
||||
* it later when the channel switch really happens.
|
||||
*/
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
params.chandef.chan,
|
||||
CHAN_MODE_SHARED,
|
||||
radar_detect_width);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
||||
params.block_tx = true;
|
||||
|
||||
|
@ -7365,6 +7355,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
u32 peer_capability = 0;
|
||||
u16 status_code;
|
||||
u8 *peer;
|
||||
bool initiator;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
|
||||
!rdev->ops->tdls_mgmt)
|
||||
|
@ -7381,12 +7372,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|||
action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
|
||||
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
|
||||
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
|
||||
initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
|
||||
if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
|
||||
peer_capability =
|
||||
nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
|
||||
|
||||
return rdev_tdls_mgmt(rdev, dev, peer, action_code,
|
||||
dialog_token, status_code, peer_capability,
|
||||
initiator,
|
||||
nla_data(info->attrs[NL80211_ATTR_IE]),
|
||||
nla_len(info->attrs[NL80211_ATTR_IE]));
|
||||
}
|
||||
|
|
|
@ -714,25 +714,6 @@ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev,
|
||||
u32 tx, u32 rx)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_ringparam(&rdev->wiphy, tx, rx);
|
||||
ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev,
|
||||
u32 *tx, u32 *tx_max, u32 *rx,
|
||||
u32 *rx_max)
|
||||
{
|
||||
trace_rdev_get_ringparam(&rdev->wiphy);
|
||||
rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max);
|
||||
trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
|
@ -770,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
|
|||
struct net_device *dev, u8 *peer,
|
||||
u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *buf, size_t len)
|
||||
bool initiator, const u8 *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code, peer_capability,
|
||||
buf, len);
|
||||
initiator, buf, len);
|
||||
ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code, peer_capability,
|
||||
buf, len);
|
||||
initiator, buf, len);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -815,35 +796,6 @@ static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_get_et_sset_count(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, int sset)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset);
|
||||
ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
trace_rdev_get_et_stats(&rdev->wiphy, dev);
|
||||
rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u32 sset,
|
||||
u8 *data)
|
||||
{
|
||||
trace_rdev_get_et_strings(&rdev->wiphy, dev, sset);
|
||||
rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_get_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
|
|
|
@ -298,11 +298,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_return_void,
|
|||
TP_ARGS(wiphy)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam,
|
||||
TP_PROTO(struct wiphy *wiphy),
|
||||
TP_ARGS(wiphy)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
|
||||
TP_PROTO(struct wiphy *wiphy),
|
||||
TP_ARGS(wiphy)
|
||||
|
@ -580,11 +575,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
|
|||
TP_ARGS(wiphy, netdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||
TP_ARGS(wiphy, netdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||
TP_ARGS(wiphy, netdev)
|
||||
|
@ -1439,11 +1429,6 @@ DECLARE_EVENT_CLASS(tx_rx_evt,
|
|||
WIPHY_PR_ARG, __entry->tx, __entry->rx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam,
|
||||
TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
|
||||
TP_ARGS(wiphy, rx, tx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
|
||||
TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
|
||||
TP_ARGS(wiphy, rx, tx)
|
||||
|
@ -1469,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt,
|
|||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
const u8 *buf, size_t len),
|
||||
bool initiator, const u8 *buf, size_t len),
|
||||
TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
|
||||
peer_capability, buf, len),
|
||||
peer_capability, initiator, buf, len),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
|
@ -1480,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
|
|||
__field(u8, dialog_token)
|
||||
__field(u16, status_code)
|
||||
__field(u32, peer_capability)
|
||||
__field(bool, initiator)
|
||||
__dynamic_array(u8, buf, len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
|
@ -1490,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt,
|
|||
__entry->dialog_token = dialog_token;
|
||||
__entry->status_code = status_code;
|
||||
__entry->peer_capability = peer_capability;
|
||||
__entry->initiator = initiator;
|
||||
memcpy(__get_dynamic_array(buf), buf, len);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
|
||||
"dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
|
||||
"dialog_token: %u, status_code: %u, peer_capability: %u "
|
||||
"initiator: %s buf: %#.2x ",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
|
||||
__entry->action_code, __entry->dialog_token,
|
||||
__entry->status_code, __entry->peer_capability,
|
||||
BOOL_TO_STR(__entry->initiator),
|
||||
((u8 *)__get_dynamic_array(buf))[0])
|
||||
);
|
||||
|
||||
|
@ -1725,40 +1714,6 @@ TRACE_EVENT(rdev_set_noack_map,
|
|||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_get_et_sset_count,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset),
|
||||
TP_ARGS(wiphy, netdev, sset),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
__field(int, sset)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
__entry->sset = sset;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_get_et_strings,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset),
|
||||
TP_ARGS(wiphy, netdev, sset),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
__field(u32, sset)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
__entry->sset = sset;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
|
||||
TP_ARGS(wiphy, wdev)
|
||||
|
|
Loading…
Reference in New Issue