Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

This commit is contained in:
John W. Linville 2014-06-25 15:15:14 -04:00
commit 855df36de3
65 changed files with 2349 additions and 2324 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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,
};

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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");

View File

@ -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;

View File

@ -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,

View File

@ -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, &params);
@ -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;

View File

@ -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)) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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)) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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, &params->beacon);
err = ieee80211_assign_beacon(sdata, &params->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, &params->beacon_csa);
err = ieee80211_assign_beacon(sdata, &params->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, &params->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,

View File

@ -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);

View File

@ -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,

View File

@ -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)
{

244
net/mac80211/ethtool.c Normal file
View File

@ -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,
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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:

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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);

View File

@ -1,6 +0,0 @@
#ifndef __CFG80211_ETHTOOL__
#define __CFG80211_ETHTOOL__
extern const struct ethtool_ops cfg80211_ethtool_ops;
#endif /* __CFG80211_ETHTOOL__ */

View File

@ -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]));
}

View File

@ -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,

View File

@ -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)