mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 23:54:04 +08:00
This relatively large batch of changes is comprised of the
following: * large mac80211-hwsim changes from Ben, Jukka and a bit myself * OCB/WAVE/11p support from Rostislav on behalf of the Czech Technical University in Prague and Volkswagen Group Research * minstrel VHT work from Karl * more CSA work from Luca * WMM admission control support in mac80211 (myself) * various smaller fixes, spelling corrections, and minor API additions -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJUWMs/AAoJEDBSmw7B7bqrmBQQAIbfAe7wH1WifRtOnhw3zWQQ K36+Edf3HlQ+EIkSs63QousRj2e7pGDOyhzMWLaqsmeTLteUtlGbr7qwiJO1QZdf Ml2V5O2s+b8hUIClDBVQF2L6+GGUmRUdQqvDDhkN1guoxD/Nk8cNtsRkSdiXWJWy R48NzvYDflBhc8uqPtR8jDb10eM3c00YP9HB+w9hYAfizD+FRue7UNp4MQIqwp9V HdKRT6L2n/6QA+Mzse0rMDes5qI7nIUNgj+hjqgJSnhITPMgGR5j/pitnVHrr81M ngOipBFG3svsQrwZh8nM4Llp0cM4Gs+GlgCieu9+TJpr2sY00Z3kYcp0pxtDoSxz Wblqz9n/bnW9mrkEfl12XqwwT5vguchwHoZ9cXhejDxSawWXoTRx20uW4ahO8ArA kWwwjTBVsQ5WMCtOBiqggzNKghwCc2ILmcZnjGdg9aNXcWsmQ4vyeCfG2QxBz/UB Grv/f9NSy6mzKQ34yv+lyR7rFZ8XcT03EVAnZSYz8X0ZZGxwtFupRp1RrBh1KPtD TJoe6Q71FfHKYRJ2xgygYkQFo+r9d0BKBeerq+Vu2hBeaqyi4aUwSj7d1sUaaq6N tL8fmAUqFjVOOUFeH1g07Xke5QD+yrEC7sJKkeRMfcRGB+dEa+2m3I5p4WDz9bWM AEvFSsYr/I9KI4d1huXD =6GIj -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-john-2014-11-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg <johannes@sipsolutions.net> says: "This relatively large batch of changes is comprised of the following: * large mac80211-hwsim changes from Ben, Jukka and a bit myself * OCB/WAVE/11p support from Rostislav on behalf of the Czech Technical University in Prague and Volkswagen Group Research * minstrel VHT work from Karl * more CSA work from Luca * WMM admission control support in mac80211 (myself) * various smaller fixes, spelling corrections, and minor API additions" Conflicts: drivers/net/wireless/ath/wil6210/cfg80211.c Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
bf515fb11a
@ -338,7 +338,7 @@ enum ath10k_state {
|
||||
* stopped in ath10k_core_restart() work holding conf_mutex. The state
|
||||
* RESTARTED means that the device is up and mac80211 has started hw
|
||||
* reconfiguration. Once mac80211 is done with the reconfiguration we
|
||||
* set the state to STATE_ON in restart_complete(). */
|
||||
* set the state to STATE_ON in reconfig_complete(). */
|
||||
ATH10K_STATE_RESTARTING,
|
||||
ATH10K_STATE_RESTARTED,
|
||||
|
||||
|
@ -3994,10 +3994,14 @@ exit:
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ath10k_restart_complete(struct ieee80211_hw *hw)
|
||||
static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
|
||||
enum ieee80211_reconfig_type reconfig_type)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
|
||||
if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
|
||||
return;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
/* If device failed to restart it will be in a different state, e.g.
|
||||
@ -4515,7 +4519,7 @@ static const struct ieee80211_ops ath10k_ops = {
|
||||
.tx_last_beacon = ath10k_tx_last_beacon,
|
||||
.set_antenna = ath10k_set_antenna,
|
||||
.get_antenna = ath10k_get_antenna,
|
||||
.restart_complete = ath10k_restart_complete,
|
||||
.reconfig_complete = ath10k_reconfig_complete,
|
||||
.get_survey = ath10k_get_survey,
|
||||
.set_bitrate_mask = ath10k_set_bitrate_mask,
|
||||
.sta_rc_update = ath10k_sta_rc_update,
|
||||
|
@ -2976,11 +2976,11 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac)
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
struct ath6kl *ar = ath6kl_priv(dev);
|
||||
struct ath6kl_vif *vif = netdev_priv(dev);
|
||||
const u8 *addr = mac ? mac : bcast_addr;
|
||||
const u8 *addr = params->mac ? params->mac : bcast_addr;
|
||||
|
||||
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
|
||||
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||
|
@ -792,12 +792,13 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
|
||||
}
|
||||
|
||||
static int wil_cfg80211_del_station(struct wiphy *wiphy,
|
||||
struct net_device *dev, const u8 *mac)
|
||||
struct net_device *dev,
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
||||
|
||||
mutex_lock(&wil->mutex);
|
||||
wil6210_disconnect(wil, mac, false);
|
||||
wil6210_disconnect(wil, params->mac, false);
|
||||
mutex_unlock(&wil->mutex);
|
||||
|
||||
return 0;
|
||||
|
@ -4045,24 +4045,24 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
|
||||
|
||||
static int
|
||||
brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
||||
const u8 *mac)
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
|
||||
struct brcmf_scb_val_le scbval;
|
||||
struct brcmf_if *ifp = netdev_priv(ndev);
|
||||
s32 err;
|
||||
|
||||
if (!mac)
|
||||
if (!params->mac)
|
||||
return -EFAULT;
|
||||
|
||||
brcmf_dbg(TRACE, "Enter %pM\n", mac);
|
||||
brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
|
||||
|
||||
if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
|
||||
ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
|
||||
if (!check_vif_up(ifp->vif))
|
||||
return -EIO;
|
||||
|
||||
memcpy(&scbval.ea, mac, ETH_ALEN);
|
||||
memcpy(&scbval.ea, params->mac, ETH_ALEN);
|
||||
scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
|
||||
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
|
||||
&scbval, sizeof(scbval));
|
||||
|
@ -6063,7 +6063,7 @@ il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
}
|
||||
|
||||
void
|
||||
il4965_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
{
|
||||
struct il_priv *il = hw->priv;
|
||||
|
@ -187,8 +187,9 @@ int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
u8 buf_size);
|
||||
int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
void il4965_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
void
|
||||
il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
|
||||
void il4965_led_enable(struct il_priv *il);
|
||||
|
||||
|
@ -941,6 +941,7 @@ static int iwlagn_mac_sta_state(struct ieee80211_hw *hw,
|
||||
}
|
||||
|
||||
static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
{
|
||||
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
|
||||
|
@ -895,9 +895,8 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
|
||||
static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
@ -915,6 +914,21 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
|
||||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
|
||||
enum ieee80211_reconfig_type reconfig_type)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
switch (reconfig_type) {
|
||||
case IEEE80211_RECONFIG_TYPE_RESTART:
|
||||
iwl_mvm_restart_complete(mvm);
|
||||
break;
|
||||
case IEEE80211_RECONFIG_TYPE_SUSPEND:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
|
||||
{
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
@ -3058,7 +3072,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
|
||||
.tx = iwl_mvm_mac_tx,
|
||||
.ampdu_action = iwl_mvm_mac_ampdu_action,
|
||||
.start = iwl_mvm_mac_start,
|
||||
.restart_complete = iwl_mvm_mac_restart_complete,
|
||||
.reconfig_complete = iwl_mvm_mac_reconfig_complete,
|
||||
.stop = iwl_mvm_mac_stop,
|
||||
.add_interface = iwl_mvm_mac_add_interface,
|
||||
.remove_interface = iwl_mvm_mac_remove_interface,
|
||||
|
@ -412,6 +412,9 @@ struct mac80211_hwsim_data {
|
||||
struct mac_address addresses[2];
|
||||
int channels, idx;
|
||||
bool use_chanctx;
|
||||
bool destroy_on_close;
|
||||
struct work_struct destroy_work;
|
||||
u32 portid;
|
||||
|
||||
struct ieee80211_channel *tmp_chan;
|
||||
struct delayed_work roc_done;
|
||||
@ -436,7 +439,7 @@ struct mac80211_hwsim_data {
|
||||
/*
|
||||
* Only radios in the same group can communicate together (the
|
||||
* channel has to match too). Each bit represents a group. A
|
||||
* radio can be in more then one group.
|
||||
* radio can be in more than one group.
|
||||
*/
|
||||
u64 group;
|
||||
|
||||
@ -447,6 +450,14 @@ struct mac80211_hwsim_data {
|
||||
s64 bcn_delta;
|
||||
/* absolute beacon transmission time. Used to cover up "tx" delay. */
|
||||
u64 abs_bcn_ts;
|
||||
|
||||
/* Stats */
|
||||
u64 tx_pkts;
|
||||
u64 rx_pkts;
|
||||
u64 tx_bytes;
|
||||
u64 rx_bytes;
|
||||
u64 tx_dropped;
|
||||
u64 tx_failed;
|
||||
};
|
||||
|
||||
|
||||
@ -476,6 +487,14 @@ static struct genl_family hwsim_genl_family = {
|
||||
.maxattr = HWSIM_ATTR_MAX,
|
||||
};
|
||||
|
||||
enum hwsim_multicast_groups {
|
||||
HWSIM_MCGRP_CONFIG,
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group hwsim_mcgrps[] = {
|
||||
[HWSIM_MCGRP_CONFIG] = { .name = "config", },
|
||||
};
|
||||
|
||||
/* MAC80211_HWSIM netlink policy */
|
||||
|
||||
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||
@ -496,6 +515,10 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
|
||||
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
|
||||
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||
@ -861,8 +884,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||
/* If the queue contains MAX_QUEUE skb's drop some */
|
||||
if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
|
||||
/* Droping until WARN_QUEUE level */
|
||||
while (skb_queue_len(&data->pending) >= WARN_QUEUE)
|
||||
skb_dequeue(&data->pending);
|
||||
while (skb_queue_len(&data->pending) >= WARN_QUEUE) {
|
||||
ieee80211_free_txskb(hw, skb_dequeue(&data->pending));
|
||||
data->tx_dropped++;
|
||||
}
|
||||
}
|
||||
|
||||
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
||||
@ -896,6 +921,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||
if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u32(skb, HWSIM_ATTR_FREQ, data->channel->center_freq))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* We get the tx control (rate and retries) info*/
|
||||
|
||||
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
|
||||
@ -917,10 +945,14 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||
|
||||
/* Enqueue the packet */
|
||||
skb_queue_tail(&data->pending, my_skb);
|
||||
data->tx_pkts++;
|
||||
data->tx_bytes += my_skb->len;
|
||||
return;
|
||||
|
||||
nla_put_failure:
|
||||
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
||||
ieee80211_free_txskb(hw, my_skb);
|
||||
data->tx_failed++;
|
||||
}
|
||||
|
||||
static bool hwsim_chans_compat(struct ieee80211_channel *c1,
|
||||
@ -1066,6 +1098,8 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
rx_status.mactime = now + data2->tsf_offset;
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
|
||||
data2->rx_pkts++;
|
||||
data2->rx_bytes += nskb->len;
|
||||
ieee80211_rx_irqsafe(data2->hw, nskb);
|
||||
}
|
||||
spin_unlock(&hwsim_radio_lock);
|
||||
@ -1133,6 +1167,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
||||
|
||||
/* NO wmediumd detected, perfect medium simulation */
|
||||
data->tx_pkts++;
|
||||
data->tx_bytes += skb->len;
|
||||
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
|
||||
|
||||
if (ack && skb->len >= 16) {
|
||||
@ -1916,6 +1952,57 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
||||
hwsim_check_chanctx_magic(ctx);
|
||||
}
|
||||
|
||||
static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = {
|
||||
"tx_pkts_nic",
|
||||
"tx_bytes_nic",
|
||||
"rx_pkts_nic",
|
||||
"rx_bytes_nic",
|
||||
"d_tx_dropped",
|
||||
"d_tx_failed",
|
||||
"d_ps_mode",
|
||||
"d_group",
|
||||
"d_tx_power",
|
||||
};
|
||||
|
||||
#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
|
||||
|
||||
static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
u32 sset, u8 *data)
|
||||
{
|
||||
if (sset == ETH_SS_STATS)
|
||||
memcpy(data, *mac80211_hwsim_gstrings_stats,
|
||||
sizeof(mac80211_hwsim_gstrings_stats));
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, int sset)
|
||||
{
|
||||
if (sset == ETH_SS_STATS)
|
||||
return MAC80211_HWSIM_SSTATS_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
struct mac80211_hwsim_data *ar = hw->priv;
|
||||
int i = 0;
|
||||
|
||||
data[i++] = ar->tx_pkts;
|
||||
data[i++] = ar->tx_bytes;
|
||||
data[i++] = ar->rx_pkts;
|
||||
data[i++] = ar->rx_bytes;
|
||||
data[i++] = ar->tx_dropped;
|
||||
data[i++] = ar->tx_failed;
|
||||
data[i++] = ar->ps;
|
||||
data[i++] = ar->group;
|
||||
data[i++] = ar->power_level;
|
||||
|
||||
WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
|
||||
}
|
||||
|
||||
static const struct ieee80211_ops mac80211_hwsim_ops = {
|
||||
.tx = mac80211_hwsim_tx,
|
||||
.start = mac80211_hwsim_start,
|
||||
@ -1939,14 +2026,131 @@ static const struct ieee80211_ops mac80211_hwsim_ops = {
|
||||
.flush = mac80211_hwsim_flush,
|
||||
.get_tsf = mac80211_hwsim_get_tsf,
|
||||
.set_tsf = mac80211_hwsim_set_tsf,
|
||||
.get_et_sset_count = mac80211_hwsim_get_et_sset_count,
|
||||
.get_et_stats = mac80211_hwsim_get_et_stats,
|
||||
.get_et_strings = mac80211_hwsim_get_et_strings,
|
||||
};
|
||||
|
||||
static struct ieee80211_ops mac80211_hwsim_mchan_ops;
|
||||
|
||||
static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
const struct ieee80211_regdomain *regd,
|
||||
bool reg_strict, bool p2p_device,
|
||||
bool use_chanctx)
|
||||
struct hwsim_new_radio_params {
|
||||
unsigned int channels;
|
||||
const char *reg_alpha2;
|
||||
const struct ieee80211_regdomain *regd;
|
||||
bool reg_strict;
|
||||
bool p2p_device;
|
||||
bool use_chanctx;
|
||||
bool destroy_on_close;
|
||||
const char *hwname;
|
||||
bool no_vif;
|
||||
};
|
||||
|
||||
static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
if (info)
|
||||
genl_notify(&hwsim_genl_family, mcast_skb,
|
||||
genl_info_net(info), info->snd_portid,
|
||||
HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL);
|
||||
else
|
||||
genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
|
||||
HWSIM_MCGRP_CONFIG, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static struct sk_buff *build_radio_msg(int cmd, int id,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, cmd);
|
||||
if (!data)
|
||||
goto error;
|
||||
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (param->channels) {
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (param->reg_alpha2) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2,
|
||||
param->reg_alpha2);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (param->regd) {
|
||||
int i;
|
||||
|
||||
for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
|
||||
i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
|
||||
;
|
||||
|
||||
if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (param->reg_strict) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (param->p2p_device) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (param->use_chanctx) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (param->hwname) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
|
||||
strlen(param->hwname), param->hwname);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, data);
|
||||
|
||||
return skb;
|
||||
|
||||
error:
|
||||
nlmsg_free(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hswim_mcast_new_radio(int id, struct genl_info *info,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
struct sk_buff *mcast_skb;
|
||||
|
||||
mcast_skb = build_radio_msg(HWSIM_CMD_NEW_RADIO, id, param);
|
||||
if (!mcast_skb)
|
||||
return;
|
||||
|
||||
hwsim_mcast_config_msg(mcast_skb, info);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
int err;
|
||||
u8 addr[ETH_ALEN];
|
||||
@ -1956,16 +2160,16 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
|
||||
int idx;
|
||||
|
||||
if (WARN_ON(channels > 1 && !use_chanctx))
|
||||
if (WARN_ON(param->channels > 1 && !param->use_chanctx))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
idx = hwsim_radio_idx++;
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
|
||||
if (use_chanctx)
|
||||
if (param->use_chanctx)
|
||||
ops = &mac80211_hwsim_mchan_ops;
|
||||
hw = ieee80211_alloc_hw(sizeof(*data), ops);
|
||||
hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname);
|
||||
if (!hw) {
|
||||
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
|
||||
err = -ENOMEM;
|
||||
@ -2003,9 +2207,12 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
hw->wiphy->n_addresses = 2;
|
||||
hw->wiphy->addresses = data->addresses;
|
||||
|
||||
data->channels = channels;
|
||||
data->use_chanctx = use_chanctx;
|
||||
data->channels = param->channels;
|
||||
data->use_chanctx = param->use_chanctx;
|
||||
data->idx = idx;
|
||||
data->destroy_on_close = param->destroy_on_close;
|
||||
if (info)
|
||||
data->portid = info->snd_portid;
|
||||
|
||||
if (data->use_chanctx) {
|
||||
hw->wiphy->max_scan_ssids = 255;
|
||||
@ -2014,12 +2221,12 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
/* For channels > 1 DFS is not allowed */
|
||||
hw->wiphy->n_iface_combinations = 1;
|
||||
hw->wiphy->iface_combinations = &data->if_combination;
|
||||
if (p2p_device)
|
||||
if (param->p2p_device)
|
||||
data->if_combination = hwsim_if_comb_p2p_dev[0];
|
||||
else
|
||||
data->if_combination = hwsim_if_comb[0];
|
||||
data->if_combination.num_different_channels = data->channels;
|
||||
} else if (p2p_device) {
|
||||
} else if (param->p2p_device) {
|
||||
hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
|
||||
hw->wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(hwsim_if_comb_p2p_dev);
|
||||
@ -2040,7 +2247,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
BIT(NL80211_IFTYPE_ADHOC) |
|
||||
BIT(NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
if (p2p_device)
|
||||
if (param->p2p_device)
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
|
||||
hw->flags = IEEE80211_HW_MFP_CAPABLE |
|
||||
@ -2095,6 +2302,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
sband->ht_cap.ht_supported = true;
|
||||
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_GRN_FLD |
|
||||
IEEE80211_HT_CAP_SGI_20 |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_DSSSCCK40;
|
||||
sband->ht_cap.ampdu_factor = 0x3;
|
||||
@ -2142,15 +2350,18 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
hw->max_rates = 4;
|
||||
hw->max_rate_tries = 11;
|
||||
|
||||
if (reg_strict)
|
||||
if (param->reg_strict)
|
||||
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
|
||||
if (regd) {
|
||||
if (param->regd) {
|
||||
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
||||
wiphy_apply_custom_regulatory(hw->wiphy, regd);
|
||||
wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
|
||||
/* give the regulatory workqueue a chance to run */
|
||||
schedule_timeout_interruptible(1);
|
||||
}
|
||||
|
||||
if (param->no_vif)
|
||||
hw->flags |= IEEE80211_HW_NO_AUTO_VIF;
|
||||
|
||||
err = ieee80211_register_hw(hw);
|
||||
if (err < 0) {
|
||||
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
|
||||
@ -2160,8 +2371,8 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
|
||||
|
||||
if (reg_alpha2)
|
||||
regulatory_hint(hw->wiphy, reg_alpha2);
|
||||
if (param->reg_alpha2)
|
||||
regulatory_hint(hw->wiphy, param->reg_alpha2);
|
||||
|
||||
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
|
||||
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
|
||||
@ -2180,6 +2391,9 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
|
||||
list_add_tail(&data->list, &hwsim_radios);
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
|
||||
if (idx > 0)
|
||||
hswim_mcast_new_radio(idx, info, param);
|
||||
|
||||
return idx;
|
||||
|
||||
failed_hw:
|
||||
@ -2190,8 +2404,48 @@ failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
|
||||
static void hwsim_mcast_del_radio(int id, const char *hwname,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
|
||||
HWSIM_CMD_DEL_RADIO);
|
||||
if (!data)
|
||||
goto error;
|
||||
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (hwname) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
|
||||
hwname);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, data);
|
||||
|
||||
hwsim_mcast_config_msg(skb, info);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
nlmsg_free(skb);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
|
||||
const char *hwname,
|
||||
struct genl_info *info)
|
||||
{
|
||||
hwsim_mcast_del_radio(data->idx, hwname, info);
|
||||
debugfs_remove_recursive(data->debugfs);
|
||||
ieee80211_unregister_hw(data->hw);
|
||||
device_release_driver(data->dev);
|
||||
@ -2209,7 +2463,7 @@ static void mac80211_hwsim_free(void)
|
||||
list))) {
|
||||
list_del(&data->list);
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
mac80211_hwsim_destroy_radio(data);
|
||||
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
}
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
@ -2337,7 +2591,6 @@ out:
|
||||
static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
struct genl_info *info)
|
||||
{
|
||||
|
||||
struct mac80211_hwsim_data *data2;
|
||||
struct ieee80211_rx_status rx_status;
|
||||
const u8 *dst;
|
||||
@ -2380,18 +2633,22 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
|
||||
/* A frame is received from user space */
|
||||
memset(&rx_status, 0, sizeof(rx_status));
|
||||
/* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
|
||||
* packets?
|
||||
*/
|
||||
rx_status.freq = data2->channel->center_freq;
|
||||
rx_status.band = data2->channel->band;
|
||||
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
|
||||
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
|
||||
data2->rx_pkts++;
|
||||
data2->rx_bytes += skb->len;
|
||||
ieee80211_rx_irqsafe(data2->hw, skb);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
||||
goto out;
|
||||
out:
|
||||
dev_kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
@ -2427,54 +2684,72 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
{
|
||||
unsigned int chans = channels;
|
||||
const char *alpha2 = NULL;
|
||||
const struct ieee80211_regdomain *regd = NULL;
|
||||
bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
|
||||
bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
|
||||
bool use_chanctx;
|
||||
struct hwsim_new_radio_params param = { 0 };
|
||||
|
||||
param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
|
||||
param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
|
||||
param.channels = channels;
|
||||
param.destroy_on_close =
|
||||
info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE];
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_CHANNELS])
|
||||
chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
|
||||
param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_NO_VIF])
|
||||
param.no_vif = true;
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_RADIO_NAME])
|
||||
param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
|
||||
use_chanctx = true;
|
||||
param.use_chanctx = true;
|
||||
else
|
||||
use_chanctx = (chans > 1);
|
||||
param.use_chanctx = (param.channels > 1);
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
|
||||
alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
|
||||
param.reg_alpha2 =
|
||||
nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
|
||||
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
|
||||
|
||||
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
|
||||
return -EINVAL;
|
||||
regd = hwsim_world_regdom_custom[idx];
|
||||
param.regd = hwsim_world_regdom_custom[idx];
|
||||
}
|
||||
|
||||
return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
|
||||
p2p_device, use_chanctx);
|
||||
return mac80211_hwsim_new_radio(info, ¶m);
|
||||
}
|
||||
|
||||
static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
{
|
||||
struct mac80211_hwsim_data *data;
|
||||
int idx;
|
||||
s64 idx = -1;
|
||||
const char *hwname = NULL;
|
||||
|
||||
if (!info->attrs[HWSIM_ATTR_RADIO_ID])
|
||||
if (info->attrs[HWSIM_ATTR_RADIO_ID])
|
||||
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
|
||||
else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
|
||||
hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
|
||||
else
|
||||
return -EINVAL;
|
||||
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
list_for_each_entry(data, &hwsim_radios, list) {
|
||||
if (data->idx != idx)
|
||||
continue;
|
||||
if (idx >= 0) {
|
||||
if (data->idx != idx)
|
||||
continue;
|
||||
} else {
|
||||
if (hwname &&
|
||||
strcmp(hwname, wiphy_name(data->hw->wiphy)))
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del(&data->list);
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
mac80211_hwsim_destroy_radio(data);
|
||||
mac80211_hwsim_del_radio(data, hwname, info);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
@ -2501,19 +2776,42 @@ static const struct genl_ops hwsim_ops[] = {
|
||||
.doit = hwsim_tx_info_frame_received_nl,
|
||||
},
|
||||
{
|
||||
.cmd = HWSIM_CMD_CREATE_RADIO,
|
||||
.cmd = HWSIM_CMD_NEW_RADIO,
|
||||
.policy = hwsim_genl_policy,
|
||||
.doit = hwsim_create_radio_nl,
|
||||
.doit = hwsim_new_radio_nl,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = HWSIM_CMD_DESTROY_RADIO,
|
||||
.cmd = HWSIM_CMD_DEL_RADIO,
|
||||
.policy = hwsim_genl_policy,
|
||||
.doit = hwsim_destroy_radio_nl,
|
||||
.doit = hwsim_del_radio_nl,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
};
|
||||
|
||||
static void destroy_radio(struct work_struct *work)
|
||||
{
|
||||
struct mac80211_hwsim_data *data =
|
||||
container_of(work, struct mac80211_hwsim_data, destroy_work);
|
||||
|
||||
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||
}
|
||||
|
||||
static void remove_user_radios(u32 portid)
|
||||
{
|
||||
struct mac80211_hwsim_data *entry, *tmp;
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) {
|
||||
if (entry->destroy_on_close && entry->portid == portid) {
|
||||
list_del(&entry->list);
|
||||
INIT_WORK(&entry->destroy_work, destroy_radio);
|
||||
schedule_work(&entry->destroy_work);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
|
||||
unsigned long state,
|
||||
void *_notify)
|
||||
@ -2523,6 +2821,8 @@ static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
|
||||
if (state != NETLINK_URELEASE)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
remove_user_radios(notify->portid);
|
||||
|
||||
if (notify->portid == wmediumd_portid) {
|
||||
printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
|
||||
" socket, switching to perfect channel medium\n");
|
||||
@ -2542,7 +2842,9 @@ static int hwsim_init_netlink(void)
|
||||
|
||||
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
|
||||
|
||||
rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
|
||||
rc = genl_register_family_with_ops_groups(&hwsim_genl_family,
|
||||
hwsim_ops,
|
||||
hwsim_mcgrps);
|
||||
if (rc)
|
||||
goto failure;
|
||||
|
||||
@ -2603,69 +2905,73 @@ static int __init init_mac80211_hwsim(void)
|
||||
goto out_unregister_driver;
|
||||
}
|
||||
|
||||
err = hwsim_init_netlink();
|
||||
if (err < 0)
|
||||
goto out_unregister_driver;
|
||||
|
||||
for (i = 0; i < radios; i++) {
|
||||
const char *reg_alpha2 = NULL;
|
||||
const struct ieee80211_regdomain *regd = NULL;
|
||||
bool reg_strict = false;
|
||||
struct hwsim_new_radio_params param = { 0 };
|
||||
|
||||
param.channels = channels;
|
||||
|
||||
switch (regtest) {
|
||||
case HWSIM_REGTEST_DIFF_COUNTRY:
|
||||
if (i < ARRAY_SIZE(hwsim_alpha2s))
|
||||
reg_alpha2 = hwsim_alpha2s[i];
|
||||
param.reg_alpha2 = hwsim_alpha2s[i];
|
||||
break;
|
||||
case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
|
||||
if (!i)
|
||||
reg_alpha2 = hwsim_alpha2s[0];
|
||||
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||
break;
|
||||
case HWSIM_REGTEST_STRICT_ALL:
|
||||
reg_strict = true;
|
||||
param.reg_strict = true;
|
||||
case HWSIM_REGTEST_DRIVER_REG_ALL:
|
||||
reg_alpha2 = hwsim_alpha2s[0];
|
||||
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||
break;
|
||||
case HWSIM_REGTEST_WORLD_ROAM:
|
||||
if (i == 0)
|
||||
regd = &hwsim_world_regdom_custom_01;
|
||||
param.regd = &hwsim_world_regdom_custom_01;
|
||||
break;
|
||||
case HWSIM_REGTEST_CUSTOM_WORLD:
|
||||
regd = &hwsim_world_regdom_custom_01;
|
||||
param.regd = &hwsim_world_regdom_custom_01;
|
||||
break;
|
||||
case HWSIM_REGTEST_CUSTOM_WORLD_2:
|
||||
if (i == 0)
|
||||
regd = &hwsim_world_regdom_custom_01;
|
||||
param.regd = &hwsim_world_regdom_custom_01;
|
||||
else if (i == 1)
|
||||
regd = &hwsim_world_regdom_custom_02;
|
||||
param.regd = &hwsim_world_regdom_custom_02;
|
||||
break;
|
||||
case HWSIM_REGTEST_STRICT_FOLLOW:
|
||||
if (i == 0) {
|
||||
reg_strict = true;
|
||||
reg_alpha2 = hwsim_alpha2s[0];
|
||||
param.reg_strict = true;
|
||||
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||
}
|
||||
break;
|
||||
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
|
||||
if (i == 0) {
|
||||
reg_strict = true;
|
||||
reg_alpha2 = hwsim_alpha2s[0];
|
||||
param.reg_strict = true;
|
||||
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||
} else if (i == 1) {
|
||||
reg_alpha2 = hwsim_alpha2s[1];
|
||||
param.reg_alpha2 = hwsim_alpha2s[1];
|
||||
}
|
||||
break;
|
||||
case HWSIM_REGTEST_ALL:
|
||||
switch (i) {
|
||||
case 0:
|
||||
regd = &hwsim_world_regdom_custom_01;
|
||||
param.regd = &hwsim_world_regdom_custom_01;
|
||||
break;
|
||||
case 1:
|
||||
regd = &hwsim_world_regdom_custom_02;
|
||||
param.regd = &hwsim_world_regdom_custom_02;
|
||||
break;
|
||||
case 2:
|
||||
reg_alpha2 = hwsim_alpha2s[0];
|
||||
param.reg_alpha2 = hwsim_alpha2s[0];
|
||||
break;
|
||||
case 3:
|
||||
reg_alpha2 = hwsim_alpha2s[1];
|
||||
param.reg_alpha2 = hwsim_alpha2s[1];
|
||||
break;
|
||||
case 4:
|
||||
reg_strict = true;
|
||||
reg_alpha2 = hwsim_alpha2s[2];
|
||||
param.reg_strict = true;
|
||||
param.reg_alpha2 = hwsim_alpha2s[2];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -2673,10 +2979,10 @@ static int __init init_mac80211_hwsim(void)
|
||||
break;
|
||||
}
|
||||
|
||||
err = mac80211_hwsim_create_radio(channels, reg_alpha2,
|
||||
regd, reg_strict,
|
||||
support_p2p_device,
|
||||
channels > 1);
|
||||
param.p2p_device = support_p2p_device;
|
||||
param.use_chanctx = channels > 1;
|
||||
|
||||
err = mac80211_hwsim_new_radio(NULL, ¶m);
|
||||
if (err < 0)
|
||||
goto out_free_radios;
|
||||
}
|
||||
@ -2702,10 +3008,6 @@ static int __init init_mac80211_hwsim(void)
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
err = hwsim_init_netlink();
|
||||
if (err < 0)
|
||||
goto out_free_mon;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_mon:
|
||||
|
@ -60,14 +60,15 @@ enum hwsim_tx_control_flags {
|
||||
* space, uses:
|
||||
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
|
||||
* %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
|
||||
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
|
||||
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional)
|
||||
* @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
|
||||
* kernel, uses:
|
||||
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
|
||||
* %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
|
||||
* @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters,
|
||||
* returns the radio ID (>= 0) or negative on errors
|
||||
* @HWSIM_CMD_DESTROY_RADIO: destroy a radio
|
||||
* @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
|
||||
* returns the radio ID (>= 0) or negative on errors, if successful
|
||||
* then multicast the result
|
||||
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
|
||||
* @__HWSIM_CMD_MAX: enum limit
|
||||
*/
|
||||
enum {
|
||||
@ -75,12 +76,15 @@ enum {
|
||||
HWSIM_CMD_REGISTER,
|
||||
HWSIM_CMD_FRAME,
|
||||
HWSIM_CMD_TX_INFO_FRAME,
|
||||
HWSIM_CMD_CREATE_RADIO,
|
||||
HWSIM_CMD_DESTROY_RADIO,
|
||||
HWSIM_CMD_NEW_RADIO,
|
||||
HWSIM_CMD_DEL_RADIO,
|
||||
__HWSIM_CMD_MAX,
|
||||
};
|
||||
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
|
||||
|
||||
#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO
|
||||
#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO
|
||||
|
||||
/**
|
||||
* enum hwsim_attrs - hwsim netlink attributes
|
||||
*
|
||||
@ -111,6 +115,11 @@ enum {
|
||||
* @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
|
||||
* command to force use of channel contexts even when only a
|
||||
* single channel is supported
|
||||
* @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_CREATE_RADIO
|
||||
* command to force radio removal when process that created the radio dies
|
||||
* @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666
|
||||
* @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio.
|
||||
* @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received.
|
||||
* @__HWSIM_ATTR_MAX: enum limit
|
||||
*/
|
||||
|
||||
@ -132,6 +141,10 @@ enum {
|
||||
HWSIM_ATTR_REG_STRICT_REG,
|
||||
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
|
||||
HWSIM_ATTR_USE_CHANCTX,
|
||||
HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
|
||||
HWSIM_ATTR_RADIO_NAME,
|
||||
HWSIM_ATTR_NO_VIF,
|
||||
HWSIM_ATTR_FREQ,
|
||||
__HWSIM_ATTR_MAX,
|
||||
};
|
||||
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
|
||||
|
@ -1285,7 +1285,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
|
||||
*/
|
||||
static int
|
||||
mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac)
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
|
||||
struct mwifiex_sta_node *sta_node;
|
||||
@ -1294,7 +1294,7 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (list_empty(&priv->sta_list) || !priv->bss_started)
|
||||
return 0;
|
||||
|
||||
if (!mac || is_broadcast_ether_addr(mac)) {
|
||||
if (!params->mac || is_broadcast_ether_addr(params->mac)) {
|
||||
wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
|
||||
list_for_each_entry(sta_node, &priv->sta_list, list) {
|
||||
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
||||
@ -1304,9 +1304,10 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
mwifiex_uap_del_sta_data(priv, sta_node);
|
||||
}
|
||||
} else {
|
||||
wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
|
||||
wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__,
|
||||
params->mac);
|
||||
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
|
||||
sta_node = mwifiex_get_sta_entry(priv, mac);
|
||||
sta_node = mwifiex_get_sta_entry(priv, params->mac);
|
||||
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
|
||||
if (sta_node) {
|
||||
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
|
||||
|
@ -5177,10 +5177,11 @@ out:
|
||||
}
|
||||
|
||||
static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
struct wl12xx_vif *wlvif;
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
int ret;
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
|
||||
@ -5190,14 +5191,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||
mutex_lock(&wl->mutex);
|
||||
|
||||
if (unlikely(wl->state == WLCORE_STATE_OFF)) {
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
||||
|
||||
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
||||
continue;
|
||||
|
||||
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
||||
ieee80211_chswitch_done(vif, false);
|
||||
}
|
||||
goto out;
|
||||
} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
||||
goto out;
|
||||
@ -5208,11 +5203,9 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||
goto out;
|
||||
|
||||
/* TODO: change mac80211 to pass vif as param */
|
||||
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
||||
unsigned long delay_usec;
|
||||
|
||||
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
||||
continue;
|
||||
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
|
||||
unsigned long delay_usec;
|
||||
|
||||
ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
|
||||
if (ret)
|
||||
@ -5222,10 +5215,10 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
||||
|
||||
/* indicate failure 5 seconds after channel switch time */
|
||||
delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
|
||||
ch_switch->count;
|
||||
ch_switch->count;
|
||||
ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
|
||||
usecs_to_jiffies(delay_usec) +
|
||||
msecs_to_jiffies(5000));
|
||||
usecs_to_jiffies(delay_usec) +
|
||||
msecs_to_jiffies(5000));
|
||||
}
|
||||
|
||||
out_sleep:
|
||||
|
@ -2856,8 +2856,10 @@ static int cfg80211_rtw_add_station(struct wiphy *wiphy,
|
||||
}
|
||||
|
||||
static int cfg80211_rtw_del_station(struct wiphy *wiphy,
|
||||
struct net_device *ndev, const u8 *mac)
|
||||
struct net_device *ndev,
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
const u8 *mac = params->mac;
|
||||
int ret = 0;
|
||||
struct list_head *phead, *plist, *ptmp;
|
||||
u8 updated = 0;
|
||||
|
@ -1274,7 +1274,7 @@ struct ieee80211_ht_cap {
|
||||
#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
|
||||
|
||||
/*
|
||||
* Maximum length of AMPDU that the STA can receive.
|
||||
* Maximum length of AMPDU that the STA can receive in high-throughput (HT).
|
||||
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
|
||||
*/
|
||||
enum ieee80211_max_ampdu_length_exp {
|
||||
@ -1284,6 +1284,21 @@ enum ieee80211_max_ampdu_length_exp {
|
||||
IEEE80211_HT_MAX_AMPDU_64K = 3
|
||||
};
|
||||
|
||||
/*
|
||||
* Maximum length of AMPDU that the STA can receive in VHT.
|
||||
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
|
||||
*/
|
||||
enum ieee80211_vht_max_ampdu_length_exp {
|
||||
IEEE80211_VHT_MAX_AMPDU_8K = 0,
|
||||
IEEE80211_VHT_MAX_AMPDU_16K = 1,
|
||||
IEEE80211_VHT_MAX_AMPDU_32K = 2,
|
||||
IEEE80211_VHT_MAX_AMPDU_64K = 3,
|
||||
IEEE80211_VHT_MAX_AMPDU_128K = 4,
|
||||
IEEE80211_VHT_MAX_AMPDU_256K = 5,
|
||||
IEEE80211_VHT_MAX_AMPDU_512K = 6,
|
||||
IEEE80211_VHT_MAX_AMPDU_1024K = 7
|
||||
};
|
||||
|
||||
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
|
||||
|
||||
/* Minimum MPDU start spacing */
|
||||
@ -1998,6 +2013,11 @@ enum ieee80211_tdls_actioncode {
|
||||
WLAN_TDLS_DISCOVERY_REQUEST = 10,
|
||||
};
|
||||
|
||||
/* Extended Channel Switching capability to be set in the 1st byte of
|
||||
* the @WLAN_EID_EXT_CAPABILITY information element
|
||||
*/
|
||||
#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
|
||||
|
||||
/* Interworking capabilities are set in 7th bit of 4th byte of the
|
||||
* @WLAN_EID_EXT_CAPABILITY information element
|
||||
*/
|
||||
|
@ -319,9 +319,12 @@ struct ieee80211_supported_band {
|
||||
/**
|
||||
* struct vif_params - describes virtual interface parameters
|
||||
* @use_4addr: use 4-address frames
|
||||
* @macaddr: address to use for this virtual interface. This will only
|
||||
* be used for non-netdevice interfaces. If this parameter is set
|
||||
* to zero address the driver may determine the address as needed.
|
||||
* @macaddr: address to use for this virtual interface.
|
||||
* If this parameter is set to zero address the driver may
|
||||
* determine the address as needed.
|
||||
* This feature is only fully supported by drivers that enable the
|
||||
* %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating
|
||||
** only p2p devices with specified MAC.
|
||||
*/
|
||||
struct vif_params {
|
||||
int use_4addr;
|
||||
@ -798,6 +801,22 @@ struct station_parameters {
|
||||
bool opmode_notif_used;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct station_del_parameters - station deletion parameters
|
||||
*
|
||||
* Used to delete a station entry (or all stations).
|
||||
*
|
||||
* @mac: MAC address of the station to remove or NULL to remove all stations
|
||||
* @subtype: Management frame subtype to use for indicating removal
|
||||
* (10 = Disassociation, 12 = Deauthentication)
|
||||
* @reason_code: Reason code for the Disassociation/Deauthentication frame
|
||||
*/
|
||||
struct station_del_parameters {
|
||||
const u8 *mac;
|
||||
u8 subtype;
|
||||
u16 reason_code;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum cfg80211_station_type - the type of station being modified
|
||||
* @CFG80211_STA_AP_CLIENT: client of an AP interface
|
||||
@ -1339,6 +1358,16 @@ struct mesh_setup {
|
||||
u32 basic_rates;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ocb_setup - 802.11p OCB mode setup configuration
|
||||
* @chandef: defines the channel to use
|
||||
*
|
||||
* These parameters are fixed when connecting to the network
|
||||
*/
|
||||
struct ocb_setup {
|
||||
struct cfg80211_chan_def chandef;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_txq_params - TX queue parameters
|
||||
* @ac: AC identifier
|
||||
@ -2132,7 +2161,7 @@ struct cfg80211_qos_map {
|
||||
* @stop_ap: Stop being an AP, including stopping beaconing.
|
||||
*
|
||||
* @add_station: Add a new station.
|
||||
* @del_station: Remove a station; @mac may be NULL to remove all stations.
|
||||
* @del_station: Remove a station
|
||||
* @change_station: Modify a given station. Note that flags changes are not much
|
||||
* validated in cfg80211, in particular the auth/assoc/authorized flags
|
||||
* might come to the driver in invalid combinations -- make sure to check
|
||||
@ -2146,6 +2175,8 @@ struct cfg80211_qos_map {
|
||||
* @change_mpath: change a given mesh path
|
||||
* @get_mpath: get a mesh path for the given parameters
|
||||
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
|
||||
* @get_mpp: get a mesh proxy path for the given parameters
|
||||
* @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
|
||||
* @join_mesh: join the mesh network with the specified parameters
|
||||
* (invoked with the wireless_dev mutex held)
|
||||
* @leave_mesh: leave the current mesh network
|
||||
@ -2331,6 +2362,11 @@ struct cfg80211_qos_map {
|
||||
* with the peer followed by immediate teardown when the addition is later
|
||||
* rejected)
|
||||
* @del_tx_ts: remove an existing TX TS
|
||||
*
|
||||
* @join_ocb: join the OCB network with the specified parameters
|
||||
* (invoked with the wireless_dev mutex held)
|
||||
* @leave_ocb: leave the current OCB network
|
||||
* (invoked with the wireless_dev mutex held)
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||
@ -2376,7 +2412,7 @@ struct cfg80211_ops {
|
||||
const u8 *mac,
|
||||
struct station_parameters *params);
|
||||
int (*del_station)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac);
|
||||
struct station_del_parameters *params);
|
||||
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac,
|
||||
struct station_parameters *params);
|
||||
@ -2396,6 +2432,11 @@ struct cfg80211_ops {
|
||||
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, u8 *dst, u8 *next_hop,
|
||||
struct mpath_info *pinfo);
|
||||
int (*get_mpp)(struct wiphy *wiphy, struct net_device *dev,
|
||||
u8 *dst, u8 *mpp, struct mpath_info *pinfo);
|
||||
int (*dump_mpp)(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, u8 *dst, u8 *mpp,
|
||||
struct mpath_info *pinfo);
|
||||
int (*get_mesh_config)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct mesh_config *conf);
|
||||
@ -2407,6 +2448,10 @@ struct cfg80211_ops {
|
||||
const struct mesh_setup *setup);
|
||||
int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);
|
||||
|
||||
int (*join_ocb)(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ocb_setup *setup);
|
||||
int (*leave_ocb)(struct wiphy *wiphy, struct net_device *dev);
|
||||
|
||||
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct bss_parameters *params);
|
||||
|
||||
@ -2623,13 +2668,9 @@ struct cfg80211_ops {
|
||||
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
|
||||
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
|
||||
* beaconing mode (AP, IBSS, Mesh, ...).
|
||||
* @WIPHY_FLAG_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
|
||||
* TSPEC sessions (TID aka TSID 0-7) with the NL80211_CMD_ADD_TX_TS
|
||||
* command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
|
||||
* needs to be able to handle Block-Ack agreements and other things.
|
||||
*/
|
||||
enum wiphy_flags {
|
||||
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION = BIT(0),
|
||||
/* use hole at 0 */
|
||||
/* use hole at 1 */
|
||||
/* use hole at 2 */
|
||||
WIPHY_FLAG_NETNS_OK = BIT(3),
|
||||
@ -3165,6 +3206,23 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
||||
return dev_name(&wiphy->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* wiphy_new_nm - create a new wiphy for use with cfg80211
|
||||
*
|
||||
* @ops: The configuration operations for this device
|
||||
* @sizeof_priv: The size of the private area to allocate
|
||||
* @requested_name: Request a particular name.
|
||||
* NULL is valid value, and means use the default phy%d naming.
|
||||
*
|
||||
* Create a new wiphy and associate the given operations with it.
|
||||
* @sizeof_priv bytes are allocated for private use.
|
||||
*
|
||||
* Return: A pointer to the new wiphy. This pointer must be
|
||||
* assigned to each netdev's ieee80211_ptr for proper operation.
|
||||
*/
|
||||
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
||||
const char *requested_name);
|
||||
|
||||
/**
|
||||
* wiphy_new - create a new wiphy for use with cfg80211
|
||||
*
|
||||
@ -3177,7 +3235,11 @@ static inline const char *wiphy_name(const struct wiphy *wiphy)
|
||||
* Return: A pointer to the new wiphy. This pointer must be
|
||||
* assigned to each netdev's ieee80211_ptr for proper operation.
|
||||
*/
|
||||
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
|
||||
static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
|
||||
int sizeof_priv)
|
||||
{
|
||||
return wiphy_new_nm(ops, sizeof_priv, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* wiphy_register - register a wiphy with cfg80211
|
||||
|
@ -263,6 +263,7 @@ struct ieee80211_vif_chanctx_switch {
|
||||
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
||||
* note that this is only called when it changes after the channel
|
||||
* context had been assigned.
|
||||
* @BSS_CHANGED_OCB: OCB join status changed
|
||||
*/
|
||||
enum ieee80211_bss_change {
|
||||
BSS_CHANGED_ASSOC = 1<<0,
|
||||
@ -287,6 +288,7 @@ enum ieee80211_bss_change {
|
||||
BSS_CHANGED_P2P_PS = 1<<19,
|
||||
BSS_CHANGED_BEACON_INFO = 1<<20,
|
||||
BSS_CHANGED_BANDWIDTH = 1<<21,
|
||||
BSS_CHANGED_OCB = 1<<22,
|
||||
|
||||
/* when adding here, make sure to change ieee80211_reconfig */
|
||||
};
|
||||
@ -739,7 +741,8 @@ struct ieee80211_tx_info {
|
||||
u8 ampdu_ack_len;
|
||||
u8 ampdu_len;
|
||||
u8 antenna;
|
||||
void *status_driver_data[21 / sizeof(void *)];
|
||||
u16 tx_time;
|
||||
void *status_driver_data[19 / sizeof(void *)];
|
||||
} status;
|
||||
struct {
|
||||
struct ieee80211_tx_rate driver_rates[
|
||||
@ -1117,6 +1120,8 @@ struct ieee80211_conf {
|
||||
* Function (TSF) timer when the frame containing the channel switch
|
||||
* announcement was received. This is simply the rx.mactime parameter
|
||||
* the driver passed into mac80211.
|
||||
* @device_timestamp: arbitrary timestamp for the device, this is the
|
||||
* rx.device_timestamp parameter the driver passed to mac80211.
|
||||
* @block_tx: Indicates whether transmission must be blocked before the
|
||||
* scheduled channel switch, as indicated by the AP.
|
||||
* @chandef: the new channel to switch to
|
||||
@ -1124,6 +1129,7 @@ struct ieee80211_conf {
|
||||
*/
|
||||
struct ieee80211_channel_switch {
|
||||
u64 timestamp;
|
||||
u32 device_timestamp;
|
||||
bool block_tx;
|
||||
struct cfg80211_chan_def chandef;
|
||||
u8 count;
|
||||
@ -1423,6 +1429,8 @@ struct ieee80211_sta_rates {
|
||||
* @smps_mode: current SMPS mode (off, static or dynamic)
|
||||
* @rates: rate control selection table
|
||||
* @tdls: indicates whether the STA is a TDLS peer
|
||||
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
|
||||
* valid if the STA is a TDLS peer in the first place.
|
||||
*/
|
||||
struct ieee80211_sta {
|
||||
u32 supp_rates[IEEE80211_NUM_BANDS];
|
||||
@ -1438,6 +1446,7 @@ struct ieee80211_sta {
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
struct ieee80211_sta_rates __rcu *rates;
|
||||
bool tdls;
|
||||
bool tdls_initiator;
|
||||
|
||||
/* must be last */
|
||||
u8 drv_priv[0] __aligned(sizeof(void *));
|
||||
@ -1576,6 +1585,10 @@ struct ieee80211_tx_control {
|
||||
* a virtual monitor interface when monitor interfaces are the only
|
||||
* active interfaces.
|
||||
*
|
||||
* @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
|
||||
* be created. It is expected user-space will create vifs as
|
||||
* desired (and thus have them named as desired).
|
||||
*
|
||||
* @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
|
||||
* queue mapping in order to use different queues (not just one per AC)
|
||||
* for different virtual interfaces. See the doc section on HW queue
|
||||
@ -1622,7 +1635,8 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
|
||||
IEEE80211_HW_MFP_CAPABLE = 1<<13,
|
||||
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
|
||||
/* free slots */
|
||||
IEEE80211_HW_NO_AUTO_VIF = 1<<15,
|
||||
/* free slot */
|
||||
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
|
||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
|
||||
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
|
||||
@ -2374,6 +2388,22 @@ enum ieee80211_roc_type {
|
||||
IEEE80211_ROC_TYPE_MGMT_TX,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ieee80211_reconfig_complete_type - reconfig type
|
||||
*
|
||||
* This enum is used by the reconfig_complete() callback to indicate what
|
||||
* reconfiguration type was completed.
|
||||
*
|
||||
* @IEEE80211_RECONFIG_TYPE_RESTART: hw restart type
|
||||
* (also due to resume() callback returning 1)
|
||||
* @IEEE80211_RECONFIG_TYPE_SUSPEND: suspend type (regardless
|
||||
* of wowlan configuration)
|
||||
*/
|
||||
enum ieee80211_reconfig_type {
|
||||
IEEE80211_RECONFIG_TYPE_RESTART,
|
||||
IEEE80211_RECONFIG_TYPE_SUSPEND,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_ops - callbacks from mac80211 to the driver
|
||||
*
|
||||
@ -2809,11 +2839,11 @@ enum ieee80211_roc_type {
|
||||
* disabled/enabled via @bss_info_changed.
|
||||
* @stop_ap: Stop operation on the AP interface.
|
||||
*
|
||||
* @restart_complete: Called after a call to ieee80211_restart_hw(), when the
|
||||
* reconfiguration has completed. This can help the driver implement the
|
||||
* reconfiguration step. Also called when reconfiguring because the
|
||||
* driver's resume function returned 1, as this is just like an "inline"
|
||||
* hardware restart. This callback may sleep.
|
||||
* @reconfig_complete: Called after a call to ieee80211_restart_hw() and
|
||||
* during resume, when the reconfiguration has completed.
|
||||
* This can help the driver implement the reconfiguration step (and
|
||||
* indicate mac80211 is ready to receive frames).
|
||||
* This callback may sleep.
|
||||
*
|
||||
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
|
||||
* Currently, this is only called for managed or P2P client interfaces.
|
||||
@ -2829,6 +2859,13 @@ enum ieee80211_roc_type {
|
||||
* transmitted and then call ieee80211_csa_finish().
|
||||
* If the CSA count starts as zero or 1, this function will not be called,
|
||||
* since there won't be any time to beacon before the switch anyway.
|
||||
* @pre_channel_switch: This is an optional callback that is called
|
||||
* before a channel switch procedure is started (ie. when a STA
|
||||
* gets a CSA or an userspace initiated channel-switch), allowing
|
||||
* the driver to prepare for the channel switch.
|
||||
* @post_channel_switch: This is an optional callback that is called
|
||||
* after a channel switch procedure is completed, allowing the
|
||||
* driver to go back to a normal configuration.
|
||||
*
|
||||
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
|
||||
* information in bss_conf is set up and the beacon can be retrieved. A
|
||||
@ -2838,6 +2875,9 @@ enum ieee80211_roc_type {
|
||||
* @get_expected_throughput: extract the expected throughput towards the
|
||||
* specified station. The returned value is expressed in Kbps. It returns 0
|
||||
* if the RC algorithm does not have proper data to provide.
|
||||
*
|
||||
* @get_txpower: get current maximum tx power (in dBm) based on configuration
|
||||
* and hardware limits.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2959,6 +2999,7 @@ struct ieee80211_ops {
|
||||
void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
u32 queues, bool drop);
|
||||
void (*channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
|
||||
int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
|
||||
@ -3025,7 +3066,8 @@ struct ieee80211_ops {
|
||||
int n_vifs,
|
||||
enum ieee80211_chanctx_switch_mode mode);
|
||||
|
||||
void (*restart_complete)(struct ieee80211_hw *hw);
|
||||
void (*reconfig_complete)(struct ieee80211_hw *hw,
|
||||
enum ieee80211_reconfig_type reconfig_type);
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
void (*ipv6_addr_change)(struct ieee80211_hw *hw,
|
||||
@ -3035,14 +3077,42 @@ struct ieee80211_ops {
|
||||
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
int (*pre_channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
|
||||
int (*post_channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
|
||||
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
|
||||
int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
int *dbm);
|
||||
};
|
||||
|
||||
/**
|
||||
* ieee80211_alloc_hw - Allocate a new hardware device
|
||||
* ieee80211_alloc_hw_nm - Allocate a new hardware device
|
||||
*
|
||||
* This must be called once for each hardware device. The returned pointer
|
||||
* must be used to refer to this device when calling other functions.
|
||||
* mac80211 allocates a private data area for the driver pointed to by
|
||||
* @priv in &struct ieee80211_hw, the size of this area is given as
|
||||
* @priv_data_len.
|
||||
*
|
||||
* @priv_data_len: length of private data
|
||||
* @ops: callbacks for this device
|
||||
* @requested_name: Requested name for this device.
|
||||
* NULL is valid value, and means use the default naming (phy%d)
|
||||
*
|
||||
* Return: A pointer to the new hardware device, or %NULL on error.
|
||||
*/
|
||||
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops,
|
||||
const char *requested_name);
|
||||
|
||||
/**
|
||||
* ieee80211_alloc_hw - Allocate a new hardware device
|
||||
*
|
||||
* This must be called once for each hardware device. The returned pointer
|
||||
* must be used to refer to this device when calling other functions.
|
||||
@ -3055,8 +3125,12 @@ struct ieee80211_ops {
|
||||
*
|
||||
* Return: A pointer to the new hardware device, or %NULL on error.
|
||||
*/
|
||||
static inline
|
||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops);
|
||||
const struct ieee80211_ops *ops)
|
||||
{
|
||||
return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_register_hw - Register hardware device
|
||||
@ -4171,6 +4245,22 @@ void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ieee80211_iterate_stations_atomic - iterate stations
|
||||
*
|
||||
* This function iterates over all stations associated with a given
|
||||
* hardware that are currently uploaded to the driver and calls the callback
|
||||
* function for them.
|
||||
* This function requires the iterator callback function to be atomic,
|
||||
*
|
||||
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||
* @iterator: the iterator function to call, cannot sleep
|
||||
* @data: first argument of the iterator function
|
||||
*/
|
||||
void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
|
||||
void (*iterator)(void *data,
|
||||
struct ieee80211_sta *sta),
|
||||
void *data);
|
||||
/**
|
||||
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
||||
*
|
||||
@ -4888,4 +4978,32 @@ void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
|
||||
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||
enum nl80211_tdls_operation oper,
|
||||
u16 reason_code, gfp_t gfp);
|
||||
|
||||
/**
|
||||
* ieee80211_ie_split - split an IE buffer according to ordering
|
||||
*
|
||||
* @ies: the IE buffer
|
||||
* @ielen: the length of the IE buffer
|
||||
* @ids: an array with element IDs that are allowed before
|
||||
* the split
|
||||
* @n_ids: the size of the element ID array
|
||||
* @offset: offset where to start splitting in the buffer
|
||||
*
|
||||
* This function splits an IE buffer by updating the @offset
|
||||
* variable to point to the location where the buffer should be
|
||||
* split.
|
||||
*
|
||||
* It assumes that the given IE buffer is well-formed, this
|
||||
* has to be guaranteed by the caller!
|
||||
*
|
||||
* It also assumes that the IEs in the buffer are ordered
|
||||
* correctly, if not the result of using this function will not
|
||||
* be ordered correctly either, i.e. it does no reordering.
|
||||
*
|
||||
* The function returns the offset where the next part of the
|
||||
* buffer starts, which may be @ielen if the entire (remainder)
|
||||
* of the buffer should be used.
|
||||
*/
|
||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids, size_t offset);
|
||||
#endif /* MAC80211_H */
|
||||
|
@ -227,7 +227,11 @@
|
||||
* the interface identified by %NL80211_ATTR_IFINDEX.
|
||||
* @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
|
||||
* or, if no MAC address given, all stations, on the interface identified
|
||||
* by %NL80211_ATTR_IFINDEX.
|
||||
* by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
|
||||
* %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
|
||||
* of disconnection indication should be sent to the station
|
||||
* (Deauthentication or Disassociation frame and reason code for that
|
||||
* frame).
|
||||
*
|
||||
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
|
||||
* destination %NL80211_ATTR_MAC on the interface identified by
|
||||
@ -738,6 +742,15 @@
|
||||
* before removing a station entry entirely, or before disassociating
|
||||
* or similar, cleanup will happen in the driver/device in this case.
|
||||
*
|
||||
* @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
|
||||
* destination %NL80211_ATTR_MAC on the interface identified by
|
||||
* %NL80211_ATTR_IFINDEX.
|
||||
*
|
||||
* @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
|
||||
* bandwidth of a channel must be given.
|
||||
* @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
|
||||
* network is determined by the network interface.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -912,6 +925,11 @@ enum nl80211_commands {
|
||||
NL80211_CMD_ADD_TX_TS,
|
||||
NL80211_CMD_DEL_TX_TS,
|
||||
|
||||
NL80211_CMD_GET_MPP,
|
||||
|
||||
NL80211_CMD_JOIN_OCB,
|
||||
NL80211_CMD_LEAVE_OCB,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
@ -2064,6 +2082,8 @@ enum nl80211_attrs {
|
||||
* and therefore can't be created in the normal ways, use the
|
||||
* %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
|
||||
* commands to create and destroy one
|
||||
* @NL80211_IF_TYPE_OCB: Outside Context of a BSS
|
||||
* This mode corresponds to the MIB variable dot11OCBActivated=true
|
||||
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
|
||||
* @NUM_NL80211_IFTYPES: number of defined interface types
|
||||
*
|
||||
@ -2083,6 +2103,7 @@ enum nl80211_iftype {
|
||||
NL80211_IFTYPE_P2P_CLIENT,
|
||||
NL80211_IFTYPE_P2P_GO,
|
||||
NL80211_IFTYPE_P2P_DEVICE,
|
||||
NL80211_IFTYPE_OCB,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_IFTYPES,
|
||||
@ -4042,6 +4063,13 @@ enum nl80211_ap_sme_features {
|
||||
* multiplexing powersave, ie. can turn off all but one chain
|
||||
* and then wake the rest up as required after, for example,
|
||||
* rts/cts handshake.
|
||||
* @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
|
||||
* TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
|
||||
* command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
|
||||
* needs to be able to handle Block-Ack agreements and other things.
|
||||
* @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
|
||||
* the vif's MAC address upon creation.
|
||||
* See 'macaddr' field in the vif_params (cfg80211.h).
|
||||
*/
|
||||
enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||
@ -4070,6 +4098,8 @@ enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
|
||||
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
|
||||
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
|
||||
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
|
||||
NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,13 @@ config MAC80211_RC_MINSTREL_HT
|
||||
---help---
|
||||
This option enables the 'minstrel_ht' TX rate control algorithm
|
||||
|
||||
config MAC80211_RC_MINSTREL_VHT
|
||||
bool "Minstrel 802.11ac support" if EXPERT
|
||||
depends on MAC80211_RC_MINSTREL_HT
|
||||
default n
|
||||
---help---
|
||||
This option enables VHT in the 'minstrel_ht' TX rate control algorithm
|
||||
|
||||
choice
|
||||
prompt "Default rate control algorithm"
|
||||
depends on MAC80211_HAS_RC
|
||||
@ -169,6 +176,17 @@ config MAC80211_HT_DEBUG
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_OCB_DEBUG
|
||||
bool "Verbose OCB debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
---help---
|
||||
Selecting this option causes mac80211 to print out
|
||||
very verbose OCB debugging messages. It should not
|
||||
be selected on production systems as those messages
|
||||
are remotely triggerable.
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_IBSS_DEBUG
|
||||
bool "Verbose IBSS debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
|
@ -27,7 +27,8 @@ mac80211-y := \
|
||||
event.o \
|
||||
chan.o \
|
||||
trace.o mlme.o \
|
||||
tdls.o
|
||||
tdls.o \
|
||||
ocb.o
|
||||
|
||||
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
|
||||
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
|
||||
|
@ -149,11 +149,6 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
|
||||
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
|
||||
}
|
||||
|
||||
static inline int ieee80211_ac_from_tid(int tid)
|
||||
{
|
||||
return ieee802_1d_to_ac[tid & 7];
|
||||
}
|
||||
|
||||
/*
|
||||
* When multiple aggregation sessions on multiple stations
|
||||
* are being created/destroyed simultaneously, we need to
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "cfg.h"
|
||||
#include "rate.h"
|
||||
#include "mesh.h"
|
||||
#include "wme.h"
|
||||
|
||||
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
||||
const char *name,
|
||||
@ -190,7 +191,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
* receive the key. When wpa_supplicant has roamed
|
||||
* using FT, it attempts to set the key before
|
||||
* association has completed, this rejects that attempt
|
||||
* so it will set the key again after assocation.
|
||||
* so it will set the key again after association.
|
||||
*
|
||||
* TODO: accept the key if we have a station entry and
|
||||
* add it to the device after the station.
|
||||
@ -229,6 +230,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
case NUM_NL80211_IFTYPES:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/* shouldn't happen */
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
@ -1225,14 +1227,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
}
|
||||
|
||||
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac)
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (mac)
|
||||
return sta_info_destroy_addr_bss(sdata, mac);
|
||||
if (params->mac)
|
||||
return sta_info_destroy_addr_bss(sdata, params->mac);
|
||||
|
||||
sta_info_flush(sdata);
|
||||
return 0;
|
||||
@ -1516,6 +1518,57 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
|
||||
struct mpath_info *pinfo)
|
||||
{
|
||||
memset(pinfo, 0, sizeof(*pinfo));
|
||||
memcpy(mpp, mpath->mpp, ETH_ALEN);
|
||||
|
||||
pinfo->generation = mpp_paths_generation;
|
||||
}
|
||||
|
||||
static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
|
||||
u8 *dst, u8 *mpp, struct mpath_info *pinfo)
|
||||
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct mesh_path *mpath;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
rcu_read_lock();
|
||||
mpath = mpp_path_lookup(sdata, dst);
|
||||
if (!mpath) {
|
||||
rcu_read_unlock();
|
||||
return -ENOENT;
|
||||
}
|
||||
memcpy(dst, mpath->dst, ETH_ALEN);
|
||||
mpp_set_pinfo(mpath, mpp, pinfo);
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, u8 *dst, u8 *mpp,
|
||||
struct mpath_info *pinfo)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct mesh_path *mpath;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
rcu_read_lock();
|
||||
mpath = mpp_path_lookup_by_idx(sdata, idx);
|
||||
if (!mpath) {
|
||||
rcu_read_unlock();
|
||||
return -ENOENT;
|
||||
}
|
||||
memcpy(dst, mpath->dst, ETH_ALEN);
|
||||
mpp_set_pinfo(mpath, mpp, pinfo);
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct mesh_config *conf)
|
||||
@ -1966,6 +2019,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
||||
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||
}
|
||||
|
||||
static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ocb_setup *setup)
|
||||
{
|
||||
return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
|
||||
}
|
||||
|
||||
static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
|
||||
{
|
||||
return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
||||
}
|
||||
|
||||
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
||||
int rate[IEEE80211_NUM_BANDS])
|
||||
{
|
||||
@ -2081,6 +2145,9 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy,
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
|
||||
if (local->ops->get_txpower)
|
||||
return drv_get_txpower(local, sdata, dbm);
|
||||
|
||||
if (!local->use_chanctx)
|
||||
*dbm = local->hw.conf.power_level;
|
||||
else
|
||||
@ -2850,11 +2917,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||
if (sdata->reserved_ready)
|
||||
return 0;
|
||||
|
||||
err = ieee80211_vif_use_reserved_context(sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return ieee80211_vif_use_reserved_context(sdata);
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
||||
@ -2868,7 +2931,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||
return err;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
||||
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
@ -2876,6 +2938,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
err = drv_post_channel_switch(sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3053,9 +3121,11 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_channel_switch ch_switch;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
int err, changed = 0;
|
||||
u32 changed = 0;
|
||||
int err;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
lockdep_assert_held(&local->mtx);
|
||||
@ -3088,6 +3158,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = drv_pre_channel_switch(sdata, &ch_switch);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
|
||||
chanctx->mode,
|
||||
params->radar_required);
|
||||
@ -3101,6 +3175,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ch_switch.timestamp = 0;
|
||||
ch_switch.device_timestamp = 0;
|
||||
ch_switch.block_tx = params->block_tx;
|
||||
ch_switch.chandef = params->chandef;
|
||||
ch_switch.count = params->count;
|
||||
|
||||
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
||||
if (err) {
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
@ -3458,7 +3538,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (chanctx_conf) {
|
||||
*chandef = chanctx_conf->def;
|
||||
*chandef = sdata->vif.bss_conf.chandef;
|
||||
ret = 0;
|
||||
} else if (local->open_count > 0 &&
|
||||
local->open_count == local->monitors &&
|
||||
@ -3521,6 +3601,76 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
|
||||
u8 tsid, const u8 *peer, u8 up,
|
||||
u16 admitted_time)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
int ac = ieee802_1d_to_ac[up];
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!(sdata->wmm_acm & BIT(up)))
|
||||
return -EINVAL;
|
||||
|
||||
if (ifmgd->tx_tspec[ac].admitted_time)
|
||||
return -EBUSY;
|
||||
|
||||
if (admitted_time) {
|
||||
ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
|
||||
ifmgd->tx_tspec[ac].tsid = tsid;
|
||||
ifmgd->tx_tspec[ac].up = up;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
|
||||
u8 tsid, const u8 *peer)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
int ac;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||
|
||||
/* skip unused entries */
|
||||
if (!tx_tspec->admitted_time)
|
||||
continue;
|
||||
|
||||
if (tx_tspec->tsid != tsid)
|
||||
continue;
|
||||
|
||||
/* due to this new packets will be reassigned to non-ACM ACs */
|
||||
tx_tspec->up = -1;
|
||||
|
||||
/* Make sure that all packets have been sent to avoid to
|
||||
* restore the QoS params on packets that are still on the
|
||||
* queues.
|
||||
*/
|
||||
synchronize_net();
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
/* restore the normal QoS parameters
|
||||
* (unconditionally to avoid races)
|
||||
*/
|
||||
tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||
tx_tspec->downgraded = false;
|
||||
ieee80211_sta_handle_tspec_ac_params(sdata);
|
||||
|
||||
/* finally clear all the data */
|
||||
memset(tx_tspec, 0, sizeof(*tx_tspec));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
const struct cfg80211_ops mac80211_config_ops = {
|
||||
.add_virtual_intf = ieee80211_add_iface,
|
||||
.del_virtual_intf = ieee80211_del_iface,
|
||||
@ -3547,11 +3697,15 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
.change_mpath = ieee80211_change_mpath,
|
||||
.get_mpath = ieee80211_get_mpath,
|
||||
.dump_mpath = ieee80211_dump_mpath,
|
||||
.get_mpp = ieee80211_get_mpp,
|
||||
.dump_mpp = ieee80211_dump_mpp,
|
||||
.update_mesh_config = ieee80211_update_mesh_config,
|
||||
.get_mesh_config = ieee80211_get_mesh_config,
|
||||
.join_mesh = ieee80211_join_mesh,
|
||||
.leave_mesh = ieee80211_leave_mesh,
|
||||
#endif
|
||||
.join_ocb = ieee80211_join_ocb,
|
||||
.leave_ocb = ieee80211_leave_ocb,
|
||||
.change_bss = ieee80211_change_bss,
|
||||
.set_txq_params = ieee80211_set_txq_params,
|
||||
.set_monitor_channel = ieee80211_set_monitor_channel,
|
||||
@ -3597,4 +3751,6 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
.channel_switch = ieee80211_channel_switch,
|
||||
.set_qos_map = ieee80211_set_qos_map,
|
||||
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
|
||||
.add_tx_ts = ieee80211_add_tx_ts,
|
||||
.del_tx_ts = ieee80211_del_tx_ts,
|
||||
};
|
||||
|
@ -270,6 +270,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
width = vif->bss_conf.chandef.width;
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
@ -674,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
@ -909,6 +911,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->csa_finalize_work);
|
||||
break;
|
||||
@ -1634,7 +1637,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
break;
|
||||
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
|
||||
/* TODO: Perhaps the bandwith change could be treated as a
|
||||
/* TODO: Perhaps the bandwidth change could be treated as a
|
||||
* reservation itself? */
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
|
@ -2,6 +2,12 @@
|
||||
#define __MAC80211_DEBUG_H
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#ifdef CONFIG_MAC80211_OCB_DEBUG
|
||||
#define MAC80211_OCB_DEBUG 1
|
||||
#else
|
||||
#define MAC80211_OCB_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
||||
#define MAC80211_IBSS_DEBUG 1
|
||||
#else
|
||||
@ -131,6 +137,10 @@ do { \
|
||||
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define ocb_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_OCB_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define ibss_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_IBSS_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
@ -300,10 +300,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
|
||||
|
||||
lockdep_assert_held(&sdata->local->key_mtx);
|
||||
|
||||
if (sdata->debugfs.default_unicast_key) {
|
||||
debugfs_remove(sdata->debugfs.default_unicast_key);
|
||||
sdata->debugfs.default_unicast_key = NULL;
|
||||
}
|
||||
debugfs_remove(sdata->debugfs.default_unicast_key);
|
||||
sdata->debugfs.default_unicast_key = NULL;
|
||||
|
||||
if (sdata->default_unicast_key) {
|
||||
key = key_mtx_dereference(sdata->local,
|
||||
@ -314,10 +312,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->vif.debugfs_dir, buf);
|
||||
}
|
||||
|
||||
if (sdata->debugfs.default_multicast_key) {
|
||||
debugfs_remove(sdata->debugfs.default_multicast_key);
|
||||
sdata->debugfs.default_multicast_key = NULL;
|
||||
}
|
||||
debugfs_remove(sdata->debugfs.default_multicast_key);
|
||||
sdata->debugfs.default_multicast_key = NULL;
|
||||
|
||||
if (sdata->default_multicast_key) {
|
||||
key = key_mtx_dereference(sdata->local,
|
||||
|
@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
|
||||
BSS_CHANGED_BEACON_ENABLED) &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||
sdata->vif.type != NL80211_IFTYPE_OCB))
|
||||
return;
|
||||
|
||||
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||
@ -631,6 +632,12 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return -EIO;
|
||||
|
||||
if (WARN_ONCE(params->cw_min == 0 ||
|
||||
params->cw_min > params->cw_max,
|
||||
"%s: invalid CW_min/CW_max: %d/%d\n",
|
||||
sdata->name, params->cw_min, params->cw_max))
|
||||
return -EINVAL;
|
||||
|
||||
trace_drv_conf_tx(local, sdata, ac, params);
|
||||
if (local->ops->conf_tx)
|
||||
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
|
||||
@ -764,12 +771,13 @@ static inline void drv_flush(struct ieee80211_local *local,
|
||||
}
|
||||
|
||||
static inline void drv_channel_switch(struct ieee80211_local *local,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_drv_channel_switch(local, ch_switch);
|
||||
local->ops->channel_switch(&local->hw, ch_switch);
|
||||
trace_drv_channel_switch(local, sdata, ch_switch);
|
||||
local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
@ -1144,13 +1152,15 @@ static inline void drv_stop_ap(struct ieee80211_local *local,
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void drv_restart_complete(struct ieee80211_local *local)
|
||||
static inline void
|
||||
drv_reconfig_complete(struct ieee80211_local *local,
|
||||
enum ieee80211_reconfig_type reconfig_type)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_drv_restart_complete(local);
|
||||
if (local->ops->restart_complete)
|
||||
local->ops->restart_complete(&local->hw);
|
||||
trace_drv_reconfig_complete(local, reconfig_type);
|
||||
if (local->ops->reconfig_complete)
|
||||
local->ops->reconfig_complete(&local->hw, reconfig_type);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
@ -1196,6 +1206,40 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_switch *ch_switch)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret = 0;
|
||||
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return -EIO;
|
||||
|
||||
trace_drv_pre_channel_switch(local, sdata, ch_switch);
|
||||
if (local->ops->pre_channel_switch)
|
||||
ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif,
|
||||
ch_switch);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_post_channel_switch(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret = 0;
|
||||
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return -EIO;
|
||||
|
||||
trace_drv_post_channel_switch(local, sdata);
|
||||
if (local->ops->post_channel_switch)
|
||||
ret = local->ops->post_channel_switch(&local->hw, &sdata->vif);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_join_ibss(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
@ -1238,4 +1282,18 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_get_txpower(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata, int *dbm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!local->ops->get_txpower)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm);
|
||||
trace_drv_get_txpower(local, sdata, *dbm, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -131,7 +131,7 @@ enum ieee80211_bss_corrupt_data_flags {
|
||||
*
|
||||
* These are bss flags that are attached to a bss in the
|
||||
* @valid_data field of &struct ieee80211_bss. They show which parts
|
||||
* of the data structure were recieved as a result of an un-corrupted
|
||||
* of the data structure were received as a result of an un-corrupted
|
||||
* beacon/probe response.
|
||||
*/
|
||||
enum ieee80211_bss_valid_data_flags {
|
||||
@ -399,6 +399,24 @@ struct ieee80211_mgd_assoc_data {
|
||||
u8 ie[];
|
||||
};
|
||||
|
||||
struct ieee80211_sta_tx_tspec {
|
||||
/* timestamp of the first packet in the time slice */
|
||||
unsigned long time_slice_start;
|
||||
|
||||
u32 admitted_time; /* in usecs, unlike over the air */
|
||||
u8 tsid;
|
||||
s8 up; /* signed to be able to invalidate with -1 during teardown */
|
||||
|
||||
/* consumed TX time in microseconds in the time slice */
|
||||
u32 consumed_tx_time;
|
||||
enum {
|
||||
TX_TSPEC_ACTION_NONE = 0,
|
||||
TX_TSPEC_ACTION_DOWNGRADE,
|
||||
TX_TSPEC_ACTION_STOP_DOWNGRADE,
|
||||
} action;
|
||||
bool downgraded;
|
||||
};
|
||||
|
||||
struct ieee80211_if_managed {
|
||||
struct timer_list timer;
|
||||
struct timer_list conn_mon_timer;
|
||||
@ -434,6 +452,8 @@ struct ieee80211_if_managed {
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
bool csa_waiting_bcn;
|
||||
|
||||
bool beacon_crc_valid;
|
||||
u32 beacon_crc;
|
||||
|
||||
@ -507,6 +527,16 @@ struct ieee80211_if_managed {
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
|
||||
/* WMM-AC TSPEC support */
|
||||
struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
|
||||
/* Use a separate work struct so that we can do something here
|
||||
* while the sdata->work is flushing the queues, for example.
|
||||
* otherwise, in scenarios where we hardly get any traffic out
|
||||
* on the BE queue, but there's a lot of VO traffic, we might
|
||||
* get stuck in a downgraded situation and flush takes forever.
|
||||
*/
|
||||
struct delayed_work tx_tspec_wk;
|
||||
};
|
||||
|
||||
struct ieee80211_if_ibss {
|
||||
@ -546,6 +576,25 @@ struct ieee80211_if_ibss {
|
||||
} state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_if_ocb - OCB mode state
|
||||
*
|
||||
* @housekeeping_timer: timer for periodic invocation of a housekeeping task
|
||||
* @wrkq_flags: OCB deferred task action
|
||||
* @incomplete_lock: delayed STA insertion lock
|
||||
* @incomplete_stations: list of STAs waiting for delayed insertion
|
||||
* @joined: indication if the interface is connected to an OCB network
|
||||
*/
|
||||
struct ieee80211_if_ocb {
|
||||
struct timer_list housekeeping_timer;
|
||||
unsigned long wrkq_flags;
|
||||
|
||||
spinlock_t incomplete_lock;
|
||||
struct list_head incomplete_stations;
|
||||
|
||||
bool joined;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
|
||||
*
|
||||
@ -839,6 +888,7 @@ struct ieee80211_sub_if_data {
|
||||
struct ieee80211_if_managed mgd;
|
||||
struct ieee80211_if_ibss ibss;
|
||||
struct ieee80211_if_mesh mesh;
|
||||
struct ieee80211_if_ocb ocb;
|
||||
u32 mntr_flags;
|
||||
} u;
|
||||
|
||||
@ -1307,6 +1357,9 @@ struct ieee80211_local {
|
||||
/* virtual monitor interface */
|
||||
struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
||||
struct cfg80211_chan_def monitor_chandef;
|
||||
|
||||
/* extended capabilities provided by mac80211 */
|
||||
u8 ext_capa[8];
|
||||
};
|
||||
|
||||
static inline struct ieee80211_sub_if_data *
|
||||
@ -1454,6 +1507,7 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
||||
__le16 fc, bool acked);
|
||||
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* IBSS code */
|
||||
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
|
||||
@ -1471,6 +1525,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* OCB code */
|
||||
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, const u8 *addr, u32 supp_rates);
|
||||
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||
struct ocb_setup *setup);
|
||||
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* mesh code */
|
||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
@ -1758,6 +1821,13 @@ static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
|
||||
return true;
|
||||
}
|
||||
|
||||
extern const int ieee802_1d_to_ac[8];
|
||||
|
||||
static inline int ieee80211_ac_from_tid(int tid)
|
||||
{
|
||||
return ieee802_1d_to_ac[tid & 7];
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data);
|
||||
@ -1767,7 +1837,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
||||
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr);
|
||||
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr, bool ack);
|
||||
struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
|
||||
|
||||
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||
unsigned long queues,
|
||||
@ -1833,8 +1903,10 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids, size_t offset);
|
||||
size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids,
|
||||
const u8 *after_ric, int n_after_ric,
|
||||
size_t offset);
|
||||
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
|
||||
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
||||
u16 cap);
|
||||
|
@ -258,6 +258,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
|
||||
/* we hold the RTNL here so can safely walk the list */
|
||||
list_for_each_entry(nsdata, &local->interfaces, list) {
|
||||
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
|
||||
/*
|
||||
* Only OCB and monitor mode may coexist
|
||||
*/
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
|
||||
nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
|
||||
(sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
||||
nsdata->vif.type == NL80211_IFTYPE_OCB))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* Allow only a single IBSS interface to be up at any
|
||||
* time. This is restricted because beacon distribution
|
||||
@ -521,6 +530,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/* no special treatment */
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
@ -631,6 +641,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
netif_carrier_off(dev);
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
@ -842,6 +853,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
sdata_lock(sdata);
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
sdata->u.mgd.csa_waiting_bcn = false;
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -1279,6 +1292,9 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||
break;
|
||||
ieee80211_mesh_work(sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
ieee80211_ocb_work(sdata);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1298,6 +1314,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
|
||||
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||
enum nl80211_iftype type)
|
||||
{
|
||||
static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff};
|
||||
|
||||
/* clear type-dependent union */
|
||||
memset(&sdata->u, 0, sizeof(sdata->u));
|
||||
|
||||
@ -1349,6 +1368,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
|
||||
ieee80211_sta_setup_sdata(sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
sdata->vif.bss_conf.bssid = bssid_wildcard;
|
||||
ieee80211_ocb_setup_sdata(sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
||||
ieee80211_ibss_setup_sdata(sdata);
|
||||
@ -1396,6 +1419,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/*
|
||||
* Could maybe also all others here?
|
||||
* Just not sure how that interacts
|
||||
@ -1411,6 +1435,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/*
|
||||
* Could probably support everything
|
||||
* but WDS here (WDS do_open can fail
|
||||
@ -1669,7 +1694,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
}
|
||||
|
||||
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
|
||||
if (params && is_valid_ether_addr(params->macaddr))
|
||||
memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
|
||||
else
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
|
||||
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
|
||||
|
||||
/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
|
||||
|
@ -94,8 +94,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (key->flags & KEY_FLAG_TAINTED)
|
||||
if (key->flags & KEY_FLAG_TAINTED) {
|
||||
/* If we get here, it's during resume and the key is
|
||||
* tainted so shouldn't be used/programmed any more.
|
||||
* However, its flags may still indicate that it was
|
||||
* programmed into the device (since we're in resume)
|
||||
* so clear that flag now to avoid trying to remove
|
||||
* it again later.
|
||||
*/
|
||||
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!key->local->ops->set_key)
|
||||
goto out_unsupported;
|
||||
|
@ -478,13 +478,9 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
|
||||
},
|
||||
};
|
||||
|
||||
static const u8 extended_capabilities[] = {
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
WLAN_EXT_CAPA8_OPMODE_NOTIF,
|
||||
};
|
||||
|
||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops)
|
||||
struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops,
|
||||
const char *requested_name)
|
||||
{
|
||||
struct ieee80211_local *local;
|
||||
int priv_size, i;
|
||||
@ -524,7 +520,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
*/
|
||||
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
|
||||
|
||||
wiphy = wiphy_new(&mac80211_config_ops, priv_size);
|
||||
wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name);
|
||||
|
||||
if (!wiphy)
|
||||
return NULL;
|
||||
@ -539,10 +535,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
WIPHY_FLAG_REPORTS_OBSS |
|
||||
WIPHY_FLAG_OFFCHAN_TX;
|
||||
|
||||
wiphy->extended_capabilities = extended_capabilities;
|
||||
wiphy->extended_capabilities_mask = extended_capabilities;
|
||||
wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
|
||||
|
||||
if (ops->remain_on_channel)
|
||||
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
||||
|
||||
@ -550,6 +542,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
NL80211_FEATURE_SAE |
|
||||
NL80211_FEATURE_HT_IBSS |
|
||||
NL80211_FEATURE_VIF_TXPOWER |
|
||||
NL80211_FEATURE_MAC_ON_CREATE |
|
||||
NL80211_FEATURE_USERSPACE_MPM;
|
||||
|
||||
if (!ops->hw_scan)
|
||||
@ -591,6 +584,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
|
||||
wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
|
||||
|
||||
local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
|
||||
|
||||
wiphy->extended_capabilities = local->ext_capa;
|
||||
wiphy->extended_capabilities_mask = local->ext_capa;
|
||||
wiphy->extended_capabilities_len =
|
||||
ARRAY_SIZE(local->ext_capa);
|
||||
|
||||
INIT_LIST_HEAD(&local->interfaces);
|
||||
|
||||
__hw_addr_init(&local->mc_list);
|
||||
@ -651,7 +651,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
|
||||
return &local->hw;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_alloc_hw);
|
||||
EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
|
||||
|
||||
static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
|
||||
{
|
||||
@ -787,13 +787,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
|
||||
return -EINVAL;
|
||||
|
||||
/* DFS currently not supported with channel context drivers */
|
||||
/* DFS is not supported with multi-channel combinations yet */
|
||||
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
|
||||
const struct ieee80211_iface_combination *comb;
|
||||
|
||||
comb = &local->hw.wiphy->iface_combinations[i];
|
||||
|
||||
if (comb->radar_detect_widths)
|
||||
if (comb->radar_detect_widths &&
|
||||
comb->num_different_channels > 1)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -958,6 +959,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
|
||||
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
|
||||
|
||||
/* mac80211 supports eCSA, if the driver supports STA CSA at all */
|
||||
if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)
|
||||
local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
|
||||
|
||||
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
|
||||
|
||||
result = wiphy_register(local->hw.wiphy);
|
||||
@ -1019,7 +1024,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
}
|
||||
|
||||
/* add one default STA interface if supported */
|
||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
|
||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
|
||||
!(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
|
||||
result = ieee80211_if_add(local, "wlan%d", NULL,
|
||||
NL80211_IFTYPE_STATION, NULL);
|
||||
if (result)
|
||||
|
@ -270,6 +270,8 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *dst, const u8 *mpp);
|
||||
struct mesh_path *
|
||||
mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
|
||||
struct mesh_path *
|
||||
mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
|
||||
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
|
||||
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||
@ -317,6 +319,7 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
|
||||
extern int mesh_paths_generation;
|
||||
extern int mpp_paths_generation;
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
static inline
|
||||
|
@ -44,6 +44,7 @@ static struct mesh_table __rcu *mesh_paths;
|
||||
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
|
||||
|
||||
int mesh_paths_generation;
|
||||
int mpp_paths_generation;
|
||||
|
||||
/* This lock will have the grow table function as writer and add / delete nodes
|
||||
* as readers. RCU provides sufficient protection only when reading the table
|
||||
@ -409,6 +410,33 @@ mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mpp_path_lookup_by_idx - look up a path in the proxy path table by its index
|
||||
* @idx: index
|
||||
* @sdata: local subif, or NULL for all entries
|
||||
*
|
||||
* Returns: pointer to the proxy path structure, or NULL if not found.
|
||||
*
|
||||
* Locking: must be called within a read rcu section.
|
||||
*/
|
||||
struct mesh_path *
|
||||
mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
|
||||
{
|
||||
struct mesh_table *tbl = rcu_dereference(mpp_paths);
|
||||
struct mpath_node *node;
|
||||
int i;
|
||||
int j = 0;
|
||||
|
||||
for_each_mesh_entry(tbl, node, i) {
|
||||
if (sdata && node->mpath->sdata != sdata)
|
||||
continue;
|
||||
if (j++ == idx)
|
||||
return node->mpath;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* mesh_path_add_gate - add the given mpath to a mesh gate to our path table
|
||||
* @mpath: gate path to add to table
|
||||
@ -691,6 +719,9 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
spin_unlock(&tbl->hashwlock[hash_idx]);
|
||||
read_unlock_bh(&pathtbl_resize_lock);
|
||||
|
||||
mpp_paths_generation++;
|
||||
|
||||
if (grow) {
|
||||
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
|
@ -775,11 +775,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||
WLAN_EID_QOS_CAPA,
|
||||
WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
||||
WLAN_EID_MOBILITY_DOMAIN,
|
||||
WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
|
||||
WLAN_EID_RIC_DATA, /* reassoc only */
|
||||
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||
};
|
||||
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
||||
before_ht, ARRAY_SIZE(before_ht),
|
||||
offset);
|
||||
static const u8 after_ric[] = {
|
||||
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||
WLAN_EID_HT_CAPABILITY,
|
||||
WLAN_EID_BSS_COEX_2040,
|
||||
WLAN_EID_EXT_CAPABILITY,
|
||||
WLAN_EID_QOS_TRAFFIC_CAPA,
|
||||
WLAN_EID_TIM_BCAST_REQ,
|
||||
WLAN_EID_INTERWORKING,
|
||||
/* 60GHz doesn't happen right now */
|
||||
WLAN_EID_VHT_CAPABILITY,
|
||||
WLAN_EID_OPMODE_NOTIF,
|
||||
};
|
||||
|
||||
noffset = ieee80211_ie_split_ric(assoc_data->ie,
|
||||
assoc_data->ie_len,
|
||||
before_ht,
|
||||
ARRAY_SIZE(before_ht),
|
||||
after_ric,
|
||||
ARRAY_SIZE(after_ric),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
@ -813,6 +832,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||
WLAN_EID_TIM_BCAST_REQ,
|
||||
WLAN_EID_INTERWORKING,
|
||||
};
|
||||
|
||||
/* RIC already taken above, so no need to handle here anymore */
|
||||
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
||||
before_vht, ARRAY_SIZE(before_vht),
|
||||
offset);
|
||||
@ -1001,14 +1022,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
/* XXX: shouldn't really modify cfg80211-owned data! */
|
||||
ifmgd->associated->channel = sdata->csa_chandef.chan;
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
/* XXX: wait for a beacon first? */
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
ifmgd->csa_waiting_bcn = true;
|
||||
|
||||
ieee80211_sta_reset_beacon_monitor(sdata);
|
||||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
@ -1019,6 +1033,35 @@ out:
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
int ret;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
WARN_ON(!sdata->vif.csa_active);
|
||||
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->csa_waiting_bcn = false;
|
||||
|
||||
ret = drv_post_channel_switch(sdata);
|
||||
if (ret) {
|
||||
sdata_info(sdata,
|
||||
"driver post channel switch failed, disconnecting\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
@ -1046,7 +1089,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
|
||||
|
||||
static void
|
||||
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
u64 timestamp, struct ieee802_11_elems *elems,
|
||||
u64 timestamp, u32 device_timestamp,
|
||||
struct ieee802_11_elems *elems,
|
||||
bool beacon)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@ -1056,6 +1100,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum ieee80211_band current_band;
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
struct ieee80211_channel_switch ch_switch;
|
||||
int res;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
@ -1110,21 +1155,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
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)
|
||||
num_chanctx++;
|
||||
if (local->use_chanctx &&
|
||||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
||||
sdata_info(sdata,
|
||||
"driver doesn't support chan-switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (num_chanctx > 1 ||
|
||||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
||||
sdata_info(sdata,
|
||||
"not handling chan-switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
ch_switch.timestamp = timestamp;
|
||||
ch_switch.device_timestamp = device_timestamp;
|
||||
ch_switch.block_tx = csa_ie.mode;
|
||||
ch_switch.chandef = csa_ie.chandef;
|
||||
ch_switch.count = csa_ie.count;
|
||||
|
||||
if (drv_pre_channel_switch(sdata, &ch_switch)) {
|
||||
sdata_info(sdata,
|
||||
"preparing for channel switch failed, disconnecting\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
|
||||
@ -1152,14 +1207,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
if (local->ops->channel_switch) {
|
||||
/* use driver's channel switch callback */
|
||||
struct ieee80211_channel_switch ch_switch = {
|
||||
.timestamp = timestamp,
|
||||
.block_tx = csa_ie.mode,
|
||||
.chandef = csa_ie.chandef,
|
||||
.count = csa_ie.count,
|
||||
};
|
||||
|
||||
drv_channel_switch(local, &ch_switch);
|
||||
drv_channel_switch(local, sdata, &ch_switch);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1579,6 +1627,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
}
|
||||
|
||||
static bool
|
||||
__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
bool ret;
|
||||
int ac;
|
||||
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||
return false;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||
int non_acm_ac;
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
|
||||
tx_tspec->admitted_time &&
|
||||
time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||
tx_tspec->consumed_tx_time = 0;
|
||||
tx_tspec->time_slice_start = now;
|
||||
|
||||
if (tx_tspec->downgraded)
|
||||
tx_tspec->action =
|
||||
TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||
}
|
||||
|
||||
switch (tx_tspec->action) {
|
||||
case TX_TSPEC_ACTION_STOP_DOWNGRADE:
|
||||
/* take the original parameters */
|
||||
if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
|
||||
sdata_err(sdata,
|
||||
"failed to set TX queue parameters for queue %d\n",
|
||||
ac);
|
||||
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||
tx_tspec->downgraded = false;
|
||||
ret = true;
|
||||
break;
|
||||
case TX_TSPEC_ACTION_DOWNGRADE:
|
||||
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
/* downgrade next lower non-ACM AC */
|
||||
for (non_acm_ac = ac + 1;
|
||||
non_acm_ac < IEEE80211_NUM_ACS;
|
||||
non_acm_ac++)
|
||||
if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
|
||||
break;
|
||||
/* The loop will result in using BK even if it requires
|
||||
* admission control, such configuration makes no sense
|
||||
* and we have to transmit somehow - the AC selection
|
||||
* does the same thing.
|
||||
*/
|
||||
if (drv_conf_tx(local, sdata, ac,
|
||||
&sdata->tx_conf[non_acm_ac]))
|
||||
sdata_err(sdata,
|
||||
"failed to set TX queue parameters for queue %d\n",
|
||||
ac);
|
||||
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
||||
ret = true;
|
||||
schedule_delayed_work(&ifmgd->tx_tspec_wk,
|
||||
tx_tspec->time_slice_start + HZ - now + 1);
|
||||
break;
|
||||
case TX_TSPEC_ACTION_NONE:
|
||||
/* nothing now */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
if (__ieee80211_sta_handle_tspec_ac_params(sdata))
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
||||
}
|
||||
|
||||
static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
sdata = container_of(work, struct ieee80211_sub_if_data,
|
||||
u.mgd.tx_tspec_wk.work);
|
||||
ieee80211_sta_handle_tspec_ac_params(sdata);
|
||||
}
|
||||
|
||||
/* MLME */
|
||||
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
@ -1663,12 +1800,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
params.uapsd = uapsd;
|
||||
|
||||
mlme_dbg(sdata,
|
||||
"WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
|
||||
"WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
|
||||
queue, aci, acm,
|
||||
params.aifs, params.cw_min, params.cw_max,
|
||||
params.txop, params.uapsd);
|
||||
params.txop, params.uapsd,
|
||||
ifmgd->tx_tspec[queue].downgraded);
|
||||
sdata->tx_conf[queue] = params;
|
||||
if (drv_conf_tx(local, sdata, queue, ¶ms))
|
||||
if (!ifmgd->tx_tspec[queue].downgraded &&
|
||||
drv_conf_tx(local, sdata, queue, ¶ms))
|
||||
sdata_err(sdata,
|
||||
"failed to set TX queue parameters for queue %d\n",
|
||||
queue);
|
||||
@ -1923,6 +2062,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->csa_waiting_bcn = false;
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -1930,6 +2070,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
/* existing TX TSPEC sessions no longer exist */
|
||||
memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
|
||||
cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
|
||||
|
||||
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
||||
}
|
||||
|
||||
@ -1982,9 +2126,46 @@ out:
|
||||
mutex_unlock(&local->mtx);
|
||||
}
|
||||
|
||||
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr, bool ack)
|
||||
static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr,
|
||||
u16 tx_time)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
|
||||
int ac = ieee80211_ac_from_tid(tid);
|
||||
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (likely(!tx_tspec->admitted_time))
|
||||
return;
|
||||
|
||||
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
||||
tx_tspec->consumed_tx_time = 0;
|
||||
tx_tspec->time_slice_start = now;
|
||||
|
||||
if (tx_tspec->downgraded) {
|
||||
tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
||||
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_tspec->downgraded)
|
||||
return;
|
||||
|
||||
tx_tspec->consumed_tx_time += tx_time;
|
||||
|
||||
if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
|
||||
tx_tspec->downgraded = true;
|
||||
tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
|
||||
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
|
||||
{
|
||||
ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
|
||||
|
||||
if (!ieee80211_is_data(hdr->frame_control))
|
||||
return;
|
||||
|
||||
@ -2047,8 +2228,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
||||
|
||||
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
||||
run_again(sdata, ifmgd->probe_timeout);
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
ieee80211_flush_queues(sdata->local, sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
||||
@ -2171,6 +2350,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
true, frame_buf);
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->csa_waiting_bcn = false;
|
||||
if (sdata->csa_block_tx) {
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -3195,6 +3375,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmgd->csa_waiting_bcn)
|
||||
ieee80211_chswitch_post_beacon(sdata);
|
||||
|
||||
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
||||
return;
|
||||
ifmgd->beacon_crc = ncrc;
|
||||
@ -3203,6 +3386,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||
|
||||
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
|
||||
rx_status->device_timestamp,
|
||||
&elems, true);
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
|
||||
@ -3334,8 +3518,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
break;
|
||||
|
||||
ieee80211_sta_process_chanswitch(sdata,
|
||||
rx_status->mactime,
|
||||
&elems, false);
|
||||
rx_status->mactime,
|
||||
rx_status->device_timestamp,
|
||||
&elems, false);
|
||||
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
||||
ies_len = skb->len -
|
||||
offsetof(struct ieee80211_mgmt,
|
||||
@ -3356,8 +3541,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
&mgmt->u.action.u.ext_chan_switch.data;
|
||||
|
||||
ieee80211_sta_process_chanswitch(sdata,
|
||||
rx_status->mactime,
|
||||
&elems, false);
|
||||
rx_status->mactime,
|
||||
rx_status->device_timestamp,
|
||||
&elems, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3664,11 +3850,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
(struct ieee80211_sub_if_data *) data;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
|
||||
if (local->quiescing)
|
||||
return;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
||||
return;
|
||||
|
||||
sdata->u.mgd.connection_loss = false;
|
||||
@ -3686,7 +3873,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
|
||||
if (local->quiescing)
|
||||
return;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
||||
return;
|
||||
|
||||
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
||||
@ -3798,6 +3985,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
(unsigned long) sdata);
|
||||
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
||||
(unsigned long) sdata);
|
||||
INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
|
||||
ieee80211_sta_handle_tspec_ac_params_wk);
|
||||
|
||||
ifmgd->flags = 0;
|
||||
ifmgd->powersave = sdata->wdev.ps;
|
||||
|
250
net/mac80211/ocb.c
Normal file
250
net/mac80211/ocb.c
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* OCB mode implementation
|
||||
*
|
||||
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||
* (c) 2014 Volkswagen Group Research
|
||||
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||
* Funded by: Volkswagen Group Research
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/mac80211.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "ieee80211_i.h"
|
||||
#include "driver-ops.h"
|
||||
#include "rate.h"
|
||||
|
||||
#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
|
||||
#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
|
||||
#define IEEE80211_OCB_MAX_STA_ENTRIES 128
|
||||
|
||||
/**
|
||||
* enum ocb_deferred_task_flags - mac80211 OCB deferred tasks
|
||||
* @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks
|
||||
*
|
||||
* These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb
|
||||
*/
|
||||
enum ocb_deferred_task_flags {
|
||||
OCB_WORK_HOUSEKEEPING,
|
||||
};
|
||||
|
||||
void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, const u8 *addr,
|
||||
u32 supp_rates)
|
||||
{
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_supported_band *sband;
|
||||
enum nl80211_bss_scan_width scan_width;
|
||||
struct sta_info *sta;
|
||||
int band;
|
||||
|
||||
/* XXX: Consider removing the least recently used entry and
|
||||
* allow new one to be added.
|
||||
*/
|
||||
if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
|
||||
net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
|
||||
sdata->name, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (WARN_ON_ONCE(!chanctx_conf)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
|
||||
rcu_read_unlock();
|
||||
|
||||
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
sta->last_rx = jiffies;
|
||||
|
||||
/* Add only mandatory rates for now */
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
sta->sta.supp_rates[band] =
|
||||
ieee80211_mandatory_rates(sband, scan_width);
|
||||
|
||||
spin_lock(&ifocb->incomplete_lock);
|
||||
list_add(&sta->list, &ifocb->incomplete_stations);
|
||||
spin_unlock(&ifocb->incomplete_lock);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
memcpy(addr, sta->sta.addr, ETH_ALEN);
|
||||
|
||||
ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
|
||||
addr, sdata->name);
|
||||
|
||||
sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
||||
sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
||||
sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
||||
|
||||
rate_control_rate_init(sta);
|
||||
|
||||
/* If it fails, maybe we raced another insertion? */
|
||||
if (sta_info_insert_rcu(sta))
|
||||
return sta_info_get(sdata, addr);
|
||||
return sta;
|
||||
}
|
||||
|
||||
static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
|
||||
ocb_dbg(sdata, "Running ocb housekeeping\n");
|
||||
|
||||
ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
|
||||
|
||||
mod_timer(&ifocb->housekeeping_timer,
|
||||
round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
|
||||
}
|
||||
|
||||
void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
struct sta_info *sta;
|
||||
|
||||
if (ifocb->joined != true)
|
||||
return;
|
||||
|
||||
sdata_lock(sdata);
|
||||
|
||||
spin_lock_bh(&ifocb->incomplete_lock);
|
||||
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||
|
||||
ieee80211_ocb_finish_sta(sta);
|
||||
rcu_read_unlock();
|
||||
spin_lock_bh(&ifocb->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||
|
||||
if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
|
||||
ieee80211_ocb_housekeeping(sdata);
|
||||
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_ocb_housekeeping_timer(unsigned long data)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = (void *)data;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
|
||||
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
|
||||
setup_timer(&ifocb->housekeeping_timer,
|
||||
ieee80211_ocb_housekeeping_timer,
|
||||
(unsigned long)sdata);
|
||||
INIT_LIST_HEAD(&ifocb->incomplete_stations);
|
||||
spin_lock_init(&ifocb->incomplete_lock);
|
||||
}
|
||||
|
||||
int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||
struct ocb_setup *setup)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
u32 changed = BSS_CHANGED_OCB;
|
||||
int err;
|
||||
|
||||
if (ifocb->joined == true)
|
||||
return -EINVAL;
|
||||
|
||||
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
||||
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||
sdata->needed_rx_chains = sdata->local->rx_chains;
|
||||
|
||||
mutex_lock(&sdata->local->mtx);
|
||||
err = ieee80211_vif_use_channel(sdata, &setup->chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
ifocb->joined = true;
|
||||
|
||||
set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
|
||||
netif_carrier_on(sdata->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
|
||||
ifocb->joined = false;
|
||||
sta_info_flush(sdata);
|
||||
|
||||
spin_lock_bh(&ifocb->incomplete_lock);
|
||||
while (!list_empty(&ifocb->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifocb->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||
|
||||
sta_info_free(local, sta);
|
||||
spin_lock_bh(&ifocb->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifocb->incomplete_lock);
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
|
||||
|
||||
mutex_lock(&sdata->local->mtx);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
del_timer_sync(&sdata->u.ocb.housekeeping_timer);
|
||||
/* If the timer fired while we waited for it, it will have
|
||||
* requeued the work. Now the work will be running again
|
||||
* but will not rearm the timer again because it checks
|
||||
* whether we are connected to the network or not -- at this
|
||||
* point we shouldn't be anymore.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
@ -448,7 +448,7 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
|
||||
*/
|
||||
if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
|
||||
u32 basic_rates = vif->bss_conf.basic_rates;
|
||||
s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0;
|
||||
s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
|
||||
|
||||
rate = &sband->bitrates[rates[0].idx];
|
||||
|
||||
|
@ -191,7 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
|
||||
* (1) if any success probabilitiy >= 95%, out of those rates
|
||||
* choose the maximum throughput rate as max_prob_rate
|
||||
* (2) if all success probabilities < 95%, the rate with
|
||||
* highest success probability is choosen as max_prob_rate */
|
||||
* highest success probability is chosen as max_prob_rate */
|
||||
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
|
||||
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
|
||||
tmp_prob_rate = i;
|
||||
|
@ -62,14 +62,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||
unsigned int i, tp, prob, eprob;
|
||||
char *p;
|
||||
|
||||
ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL);
|
||||
ms = kmalloc(2048, GFP_KERNEL);
|
||||
if (!ms)
|
||||
return -ENOMEM;
|
||||
|
||||
file->private_data = ms;
|
||||
p = ms->buf;
|
||||
p += sprintf(p, "rate throughput ewma prob this prob "
|
||||
"this succ/attempt success attempts\n");
|
||||
p += sprintf(p, "rate tpt eprob *prob"
|
||||
" *ok(*cum) ok( cum)\n");
|
||||
for (i = 0; i < mi->n_rates; i++) {
|
||||
struct minstrel_rate *mr = &mi->r[i];
|
||||
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
|
||||
@ -86,8 +86,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
|
||||
eprob = MINSTREL_TRUNC(mrs->probability * 1000);
|
||||
|
||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
||||
" %3u(%3u) %8llu %8llu\n",
|
||||
p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
|
||||
" %4u(%4u) %9llu(%9llu)\n",
|
||||
tp / 10, tp % 10,
|
||||
eprob / 10, eprob % 10,
|
||||
prob / 10, prob % 10,
|
||||
@ -102,6 +102,8 @@ minstrel_stats_open(struct inode *inode, struct file *file)
|
||||
mi->sample_packets);
|
||||
ms->len = p - ms->buf;
|
||||
|
||||
WARN_ON(ms->len + sizeof(*ms) > 2048);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "rate.h"
|
||||
@ -34,12 +35,17 @@
|
||||
/* Transmit duration for the raw data part of an average sized packet */
|
||||
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
|
||||
|
||||
#define BW_20 0
|
||||
#define BW_40 1
|
||||
#define BW_80 2
|
||||
|
||||
/*
|
||||
* Define group sort order: HT40 -> SGI -> #streams
|
||||
*/
|
||||
#define GROUP_IDX(_streams, _sgi, _ht40) \
|
||||
MINSTREL_HT_GROUP_0 + \
|
||||
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
|
||||
MINSTREL_MAX_STREAMS * _sgi + \
|
||||
MINSTREL_MAX_STREAMS * _sgi + \
|
||||
_streams - 1
|
||||
|
||||
/* MCS rate information for an MCS group */
|
||||
@ -47,6 +53,7 @@
|
||||
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
||||
.streams = _streams, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||
.duration = { \
|
||||
@ -61,6 +68,47 @@
|
||||
} \
|
||||
}
|
||||
|
||||
#define VHT_GROUP_IDX(_streams, _sgi, _bw) \
|
||||
(MINSTREL_VHT_GROUP_0 + \
|
||||
MINSTREL_MAX_STREAMS * 2 * (_bw) + \
|
||||
MINSTREL_MAX_STREAMS * (_sgi) + \
|
||||
(_streams) - 1)
|
||||
|
||||
#define BW2VBPS(_bw, r3, r2, r1) \
|
||||
(_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
|
||||
|
||||
#define VHT_GROUP(_streams, _sgi, _bw) \
|
||||
[VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
|
||||
.streams = _streams, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_VHT_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
(_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \
|
||||
_bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||
.duration = { \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 117, 54, 26)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 234, 108, 52)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 351, 162, 78)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 468, 216, 104)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 702, 324, 156)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 936, 432, 208)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 1053, 486, 234)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 1170, 540, 260)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 1404, 648, 312)), \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
BW2VBPS(_bw, 1560, 720, 346)) \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCK_DURATION(_bitrate, _short, _len) \
|
||||
(1000 * (10 /* SIFS */ + \
|
||||
(_short ? 72 + 24 : 144 + 48) + \
|
||||
@ -76,70 +124,161 @@
|
||||
CCK_ACK_DURATION(55, _short), \
|
||||
CCK_ACK_DURATION(110, _short)
|
||||
|
||||
#define CCK_GROUP \
|
||||
[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
|
||||
.streams = 0, \
|
||||
.duration = { \
|
||||
CCK_DURATION_LIST(false), \
|
||||
CCK_DURATION_LIST(true) \
|
||||
} \
|
||||
#define CCK_GROUP \
|
||||
[MINSTREL_CCK_GROUP] = { \
|
||||
.streams = 0, \
|
||||
.flags = 0, \
|
||||
.duration = { \
|
||||
CCK_DURATION_LIST(false), \
|
||||
CCK_DURATION_LIST(true) \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
static bool minstrel_vht_only = true;
|
||||
module_param(minstrel_vht_only, bool, 0644);
|
||||
MODULE_PARM_DESC(minstrel_vht_only,
|
||||
"Use only VHT rates when VHT is supported by sta.");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* To enable sufficiently targeted rate sampling, MCS rates are divided into
|
||||
* groups, based on the number of streams and flags (HT40, SGI) that they
|
||||
* use.
|
||||
*
|
||||
* Sortorder has to be fixed for GROUP_IDX macro to be applicable:
|
||||
* HT40 -> SGI -> #streams
|
||||
* BW -> SGI -> #streams
|
||||
*/
|
||||
const struct mcs_group minstrel_mcs_groups[] = {
|
||||
MCS_GROUP(1, 0, 0),
|
||||
MCS_GROUP(2, 0, 0),
|
||||
MCS_GROUP(1, 0, BW_20),
|
||||
MCS_GROUP(2, 0, BW_20),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
MCS_GROUP(3, 0, 0),
|
||||
MCS_GROUP(3, 0, BW_20),
|
||||
#endif
|
||||
|
||||
MCS_GROUP(1, 1, 0),
|
||||
MCS_GROUP(2, 1, 0),
|
||||
MCS_GROUP(1, 1, BW_20),
|
||||
MCS_GROUP(2, 1, BW_20),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
MCS_GROUP(3, 1, 0),
|
||||
MCS_GROUP(3, 1, BW_20),
|
||||
#endif
|
||||
|
||||
MCS_GROUP(1, 0, 1),
|
||||
MCS_GROUP(2, 0, 1),
|
||||
MCS_GROUP(1, 0, BW_40),
|
||||
MCS_GROUP(2, 0, BW_40),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
MCS_GROUP(3, 0, 1),
|
||||
MCS_GROUP(3, 0, BW_40),
|
||||
#endif
|
||||
|
||||
MCS_GROUP(1, 1, 1),
|
||||
MCS_GROUP(2, 1, 1),
|
||||
MCS_GROUP(1, 1, BW_40),
|
||||
MCS_GROUP(2, 1, BW_40),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
MCS_GROUP(3, 1, 1),
|
||||
MCS_GROUP(3, 1, BW_40),
|
||||
#endif
|
||||
|
||||
/* must be last */
|
||||
CCK_GROUP
|
||||
CCK_GROUP,
|
||||
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
VHT_GROUP(1, 0, BW_20),
|
||||
VHT_GROUP(2, 0, BW_20),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 0, BW_20),
|
||||
#endif
|
||||
|
||||
VHT_GROUP(1, 1, BW_20),
|
||||
VHT_GROUP(2, 1, BW_20),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 1, BW_20),
|
||||
#endif
|
||||
|
||||
VHT_GROUP(1, 0, BW_40),
|
||||
VHT_GROUP(2, 0, BW_40),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 0, BW_40),
|
||||
#endif
|
||||
|
||||
VHT_GROUP(1, 1, BW_40),
|
||||
VHT_GROUP(2, 1, BW_40),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 1, BW_40),
|
||||
#endif
|
||||
|
||||
VHT_GROUP(1, 0, BW_80),
|
||||
VHT_GROUP(2, 0, BW_80),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 0, BW_80),
|
||||
#endif
|
||||
|
||||
VHT_GROUP(1, 1, BW_80),
|
||||
VHT_GROUP(2, 1, BW_80),
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
VHT_GROUP(3, 1, BW_80),
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
|
||||
|
||||
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
|
||||
|
||||
static void
|
||||
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
|
||||
|
||||
/*
|
||||
* Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
|
||||
* e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1
|
||||
*
|
||||
* Returns the valid mcs map for struct minstrel_mcs_group_data.supported
|
||||
*/
|
||||
static u16
|
||||
minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
|
||||
{
|
||||
u16 mask = 0;
|
||||
|
||||
if (bw == BW_20) {
|
||||
if (nss != 3 && nss != 6)
|
||||
mask = BIT(9);
|
||||
} else if (bw == BW_80) {
|
||||
if (nss == 3 || nss == 7)
|
||||
mask = BIT(6);
|
||||
else if (nss == 6)
|
||||
mask = BIT(9);
|
||||
} else {
|
||||
WARN_ON(bw != BW_40);
|
||||
}
|
||||
|
||||
switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) {
|
||||
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
||||
mask |= 0x300;
|
||||
break;
|
||||
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
||||
mask |= 0x200;
|
||||
break;
|
||||
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
||||
break;
|
||||
default:
|
||||
mask = 0x3ff;
|
||||
}
|
||||
|
||||
return 0x3ff & ~mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up an MCS group index based on mac80211 rate information
|
||||
*/
|
||||
static int
|
||||
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
|
||||
return GROUP_IDX((rate->idx / 8) + 1,
|
||||
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
|
||||
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
|
||||
}
|
||||
|
||||
static int
|
||||
minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate),
|
||||
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
|
||||
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
|
||||
2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH));
|
||||
}
|
||||
|
||||
static struct minstrel_rate_stats *
|
||||
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
struct ieee80211_tx_rate *rate)
|
||||
@ -149,6 +288,9 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
||||
group = minstrel_ht_get_group_idx(rate);
|
||||
idx = rate->idx % 8;
|
||||
} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
group = minstrel_vht_get_group_idx(rate);
|
||||
idx = ieee80211_rate_get_vht_mcs(rate);
|
||||
} else {
|
||||
group = MINSTREL_CCK_GROUP;
|
||||
|
||||
@ -240,8 +382,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
|
||||
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
|
||||
*/
|
||||
static void
|
||||
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
|
||||
u8 *tp_list)
|
||||
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
|
||||
u16 *tp_list)
|
||||
{
|
||||
int cur_group, cur_idx, cur_thr, cur_prob;
|
||||
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
|
||||
@ -278,7 +420,7 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
|
||||
* Find and set the topmost probability rate per sta and per group
|
||||
*/
|
||||
static void
|
||||
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
|
||||
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
|
||||
{
|
||||
struct minstrel_mcs_group_data *mg;
|
||||
struct minstrel_rate_stats *mr;
|
||||
@ -321,8 +463,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
|
||||
*/
|
||||
static void
|
||||
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
|
||||
u8 tmp_mcs_tp_rate[MAX_THR_RATES],
|
||||
u8 tmp_cck_tp_rate[MAX_THR_RATES])
|
||||
u16 tmp_mcs_tp_rate[MAX_THR_RATES],
|
||||
u16 tmp_cck_tp_rate[MAX_THR_RATES])
|
||||
{
|
||||
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
|
||||
int i;
|
||||
@ -386,8 +528,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
struct minstrel_mcs_group_data *mg;
|
||||
struct minstrel_rate_stats *mr;
|
||||
int group, i, j;
|
||||
u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
||||
u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||
u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
||||
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||
|
||||
if (mi->ampdu_packets > 0) {
|
||||
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||
@ -485,7 +627,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
|
||||
if (!rate->count)
|
||||
return false;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS)
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS ||
|
||||
rate->flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
return true;
|
||||
|
||||
return rate->idx == mp->cck_rates[0] ||
|
||||
@ -517,7 +660,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
|
||||
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
|
||||
{
|
||||
int group, orig_group;
|
||||
|
||||
@ -714,7 +857,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
struct minstrel_rate_stats *mr;
|
||||
u8 idx;
|
||||
u16 flags;
|
||||
u16 flags = group->flags;
|
||||
|
||||
mr = minstrel_get_ratestats(mi, index);
|
||||
if (!mr->retry_updated)
|
||||
@ -730,13 +873,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
|
||||
}
|
||||
|
||||
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
||||
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
|
||||
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
|
||||
flags = 0;
|
||||
} else {
|
||||
else if (flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
idx = ((group->streams - 1) << 4) |
|
||||
((index % MCS_GROUP_RATES) & 0xF);
|
||||
else
|
||||
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
|
||||
flags = IEEE80211_TX_RC_MCS | group->flags;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
|
||||
@ -916,13 +1059,15 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
||||
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
|
||||
rate->idx = mp->cck_rates[idx];
|
||||
rate->flags = 0;
|
||||
return;
|
||||
} else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
|
||||
sample_group->streams);
|
||||
} else {
|
||||
rate->idx = sample_idx % MCS_GROUP_RATES +
|
||||
(sample_group->streams - 1) * 8;
|
||||
}
|
||||
|
||||
rate->idx = sample_idx % MCS_GROUP_RATES +
|
||||
(sample_group->streams - 1) * 8;
|
||||
rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
|
||||
rate->flags = sample_group->flags;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -962,6 +1107,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
|
||||
u16 sta_cap = sta->ht_cap.cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
|
||||
int use_vht;
|
||||
int n_supported = 0;
|
||||
int ack_dur;
|
||||
int stbc;
|
||||
@ -971,8 +1118,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
if (!sta->ht_cap.ht_supported)
|
||||
goto use_legacy;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
|
||||
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
|
||||
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
if (vht_cap->vht_supported)
|
||||
use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
|
||||
else
|
||||
#endif
|
||||
use_vht = 0;
|
||||
|
||||
msp->is_ht = true;
|
||||
memset(mi, 0, sizeof(*mi));
|
||||
@ -997,22 +1150,28 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
}
|
||||
mi->sample_tries = 4;
|
||||
|
||||
stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
||||
IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
||||
mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
||||
/* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
|
||||
if (!use_vht) {
|
||||
stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
||||
IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
||||
mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
||||
|
||||
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
|
||||
u32 gflags = minstrel_mcs_groups[i].flags;
|
||||
int bw, nss;
|
||||
|
||||
mi->groups[i].supported = 0;
|
||||
if (i == MINSTREL_CCK_GROUP) {
|
||||
minstrel_ht_update_cck(mp, mi, sband, sta);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
||||
if (gflags & IEEE80211_TX_RC_SHORT_GI) {
|
||||
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
||||
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
|
||||
continue;
|
||||
} else {
|
||||
@ -1021,17 +1180,51 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
}
|
||||
}
|
||||
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
|
||||
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
|
||||
sta->bandwidth < IEEE80211_STA_RX_BW_40)
|
||||
continue;
|
||||
|
||||
nss = minstrel_mcs_groups[i].streams;
|
||||
|
||||
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
|
||||
if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
|
||||
minstrel_mcs_groups[i].streams > 1)
|
||||
if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
|
||||
continue;
|
||||
|
||||
mi->groups[i].supported =
|
||||
mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
|
||||
/* HT rate */
|
||||
if (gflags & IEEE80211_TX_RC_MCS) {
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
if (use_vht && minstrel_vht_only)
|
||||
continue;
|
||||
#endif
|
||||
mi->groups[i].supported = mcs->rx_mask[nss - 1];
|
||||
if (mi->groups[i].supported)
|
||||
n_supported++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* VHT rate */
|
||||
if (!vht_cap->vht_supported ||
|
||||
WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) ||
|
||||
WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH))
|
||||
continue;
|
||||
|
||||
if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) {
|
||||
if (sta->bandwidth < IEEE80211_STA_RX_BW_80 ||
|
||||
((gflags & IEEE80211_TX_RC_SHORT_GI) &&
|
||||
!(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
bw = BW_40;
|
||||
else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||
bw = BW_80;
|
||||
else
|
||||
bw = BW_20;
|
||||
|
||||
mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
|
||||
vht_cap->vht_mcs.tx_mcs_map);
|
||||
|
||||
if (mi->groups[i].supported)
|
||||
n_supported++;
|
||||
|
@ -13,10 +13,32 @@
|
||||
* The number of streams can be changed to 2 to reduce code
|
||||
* size and memory footprint.
|
||||
*/
|
||||
#define MINSTREL_MAX_STREAMS 3
|
||||
#define MINSTREL_STREAM_GROUPS 4
|
||||
#define MINSTREL_MAX_STREAMS 3
|
||||
#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */
|
||||
#else
|
||||
#define MINSTREL_VHT_STREAM_GROUPS 0
|
||||
#endif
|
||||
|
||||
#define MCS_GROUP_RATES 8
|
||||
#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
|
||||
MINSTREL_HT_STREAM_GROUPS)
|
||||
#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
|
||||
MINSTREL_VHT_STREAM_GROUPS)
|
||||
#define MINSTREL_CCK_GROUPS_NB 1
|
||||
#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \
|
||||
MINSTREL_VHT_GROUPS_NB + \
|
||||
MINSTREL_CCK_GROUPS_NB)
|
||||
|
||||
#define MINSTREL_HT_GROUP_0 0
|
||||
#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
|
||||
#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1)
|
||||
|
||||
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
|
||||
#define MCS_GROUP_RATES 10
|
||||
#else
|
||||
#define MCS_GROUP_RATES 8
|
||||
#endif
|
||||
|
||||
struct mcs_group {
|
||||
u32 flags;
|
||||
@ -31,11 +53,11 @@ struct minstrel_mcs_group_data {
|
||||
u8 column;
|
||||
|
||||
/* bitfield of supported MCS rates of this group */
|
||||
u8 supported;
|
||||
u16 supported;
|
||||
|
||||
/* sorted rate set within a MCS group*/
|
||||
u8 max_group_tp_rate[MAX_THR_RATES];
|
||||
u8 max_group_prob_rate;
|
||||
u16 max_group_tp_rate[MAX_THR_RATES];
|
||||
u16 max_group_prob_rate;
|
||||
|
||||
/* MCS rate statistics */
|
||||
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
|
||||
@ -52,8 +74,8 @@ struct minstrel_ht_sta {
|
||||
unsigned int avg_ampdu_len;
|
||||
|
||||
/* overall sorted rate set */
|
||||
u8 max_tp_rate[MAX_THR_RATES];
|
||||
u8 max_prob_rate;
|
||||
u16 max_tp_rate[MAX_THR_RATES];
|
||||
u16 max_prob_rate;
|
||||
|
||||
/* time of last status update */
|
||||
unsigned long stats_update;
|
||||
@ -80,7 +102,7 @@ struct minstrel_ht_sta {
|
||||
u8 cck_supported_short;
|
||||
|
||||
/* MCS rate group info and statistics */
|
||||
struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
|
||||
struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
|
||||
};
|
||||
|
||||
struct minstrel_ht_sta_priv {
|
||||
|
@ -18,19 +18,23 @@
|
||||
static char *
|
||||
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||
{
|
||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
||||
const struct mcs_group *mg;
|
||||
unsigned int j, tp, prob, eprob;
|
||||
char htmode = '2';
|
||||
char gimode = 'L';
|
||||
u32 gflags;
|
||||
|
||||
if (!mi->groups[i].supported)
|
||||
return p;
|
||||
|
||||
mg = &minstrel_mcs_groups[i];
|
||||
if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
gflags = mg->flags;
|
||||
|
||||
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
htmode = '4';
|
||||
if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
|
||||
else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||
htmode = '8';
|
||||
if (gflags & IEEE80211_TX_RC_SHORT_GI)
|
||||
gimode = 'S';
|
||||
|
||||
for (j = 0; j < MCS_GROUP_RATES; j++) {
|
||||
@ -41,10 +45,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||
if (!(mi->groups[i].supported & BIT(j)))
|
||||
continue;
|
||||
|
||||
if (i == max_mcs)
|
||||
p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
|
||||
if (gflags & IEEE80211_TX_RC_MCS)
|
||||
p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
|
||||
else if (gflags & IEEE80211_TX_RC_VHT_MCS)
|
||||
p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
|
||||
else
|
||||
p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
|
||||
p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S');
|
||||
|
||||
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
|
||||
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
|
||||
@ -52,19 +58,22 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
|
||||
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
|
||||
|
||||
if (i == max_mcs) {
|
||||
int r = bitrates[j % 4];
|
||||
p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
|
||||
if (gflags & IEEE80211_TX_RC_MCS) {
|
||||
p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
|
||||
} else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
|
||||
} else {
|
||||
p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
|
||||
int r = bitrates[j % 4];
|
||||
|
||||
p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
|
||||
}
|
||||
|
||||
tp = mr->cur_tp / 10;
|
||||
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
|
||||
eprob = MINSTREL_TRUNC(mr->probability * 1000);
|
||||
|
||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
||||
"%3u %3u(%3u) %8llu %8llu\n",
|
||||
p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
|
||||
"%3u %4u(%4u) %9llu(%9llu)\n",
|
||||
tp / 10, tp % 10,
|
||||
eprob / 10, eprob % 10,
|
||||
prob / 10, prob % 10,
|
||||
@ -85,7 +94,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct minstrel_debugfs_info *ms;
|
||||
unsigned int i;
|
||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
@ -96,17 +104,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL);
|
||||
ms = kmalloc(32768, GFP_KERNEL);
|
||||
if (!ms)
|
||||
return -ENOMEM;
|
||||
|
||||
file->private_data = ms;
|
||||
p = ms->buf;
|
||||
p += sprintf(p, "type rate throughput ewma prob "
|
||||
"this prob retry this succ/attempt success attempts\n");
|
||||
p += sprintf(p, " type rate tpt eprob *prob "
|
||||
"ret *ok(*cum) ok( cum)\n");
|
||||
|
||||
p = minstrel_ht_stats_dump(mi, max_mcs, p);
|
||||
for (i = 0; i < max_mcs; i++)
|
||||
p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
|
||||
for (i = 0; i < MINSTREL_CCK_GROUP; i++)
|
||||
p = minstrel_ht_stats_dump(mi, i, p);
|
||||
for (i++; i < ARRAY_SIZE(mi->groups); i++)
|
||||
p = minstrel_ht_stats_dump(mi, i, p);
|
||||
|
||||
p += sprintf(p, "\nTotal packet count:: ideal %d "
|
||||
@ -118,6 +128,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
|
||||
ms->len = p - ms->buf;
|
||||
|
||||
WARN_ON(ms->len + sizeof(*ms) > 32768);
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
|
@ -1032,6 +1032,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
ieee80211_is_pspoll(hdr->frame_control)) &&
|
||||
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
||||
rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
|
||||
/*
|
||||
* accept port control frames from the AP even when it's not
|
||||
@ -1272,6 +1273,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||
sta->last_rx_rate_vht_nss = status->vht_nss;
|
||||
}
|
||||
}
|
||||
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
|
||||
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
|
||||
NL80211_IFTYPE_OCB);
|
||||
/* OCB uses wild-card BSSID */
|
||||
if (is_broadcast_ether_addr(bssid))
|
||||
sta->last_rx = jiffies;
|
||||
} else if (!is_multicast_ether_addr(hdr->addr1)) {
|
||||
/*
|
||||
* Mesh beacons will update last_rx when if they are found to
|
||||
@ -2820,6 +2827,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
|
||||
|
||||
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||
sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return RX_DROP_MONITOR;
|
||||
|
||||
@ -3130,6 +3138,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
|
||||
BIT(rate_idx));
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
if (!bssid)
|
||||
return false;
|
||||
if (ieee80211_is_beacon(hdr->frame_control)) {
|
||||
return false;
|
||||
} else if (!is_broadcast_ether_addr(bssid)) {
|
||||
ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
|
||||
return false;
|
||||
} else if (!multicast &&
|
||||
!ether_addr_equal(sdata->dev->dev_addr,
|
||||
hdr->addr1)) {
|
||||
/* if we are in promisc mode we also accept
|
||||
* packets not destined for us
|
||||
*/
|
||||
if (!(sdata->dev->flags & IFF_PROMISC))
|
||||
return false;
|
||||
rx->flags &= ~IEEE80211_RX_RA_MATCH;
|
||||
} else if (!rx->sta) {
|
||||
int rate_idx;
|
||||
if (status->flag & RX_FLAG_HT)
|
||||
rate_idx = 0; /* TODO: HT rates */
|
||||
else
|
||||
rate_idx = status->rate_idx;
|
||||
ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
|
||||
BIT(rate_idx));
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
if (!multicast &&
|
||||
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
|
||||
|
@ -501,7 +501,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
|
||||
/* make the station visible */
|
||||
sta_info_hash_add(local, sta);
|
||||
|
||||
list_add_rcu(&sta->list, &local->sta_list);
|
||||
list_add_tail_rcu(&sta->list, &local->sta_list);
|
||||
|
||||
/* notify driver */
|
||||
err = sta_info_insert_drv_state(local, sdata, sta);
|
||||
@ -1531,7 +1531,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
|
||||
break;
|
||||
case 0:
|
||||
/* XXX: what is a good value? */
|
||||
n_frames = 8;
|
||||
n_frames = 128;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -336,6 +336,7 @@ struct ieee80211_tx_latency_stat {
|
||||
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
|
||||
* AP only.
|
||||
* @cipher_scheme: optional cipher scheme for this station
|
||||
* @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
|
@ -704,7 +704,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
|
||||
if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) &&
|
||||
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
|
||||
ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked);
|
||||
ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
|
||||
acked, info->status.tx_time);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
|
||||
if (info->flags & IEEE80211_TX_STAT_ACK) {
|
||||
|
@ -562,8 +562,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
/* infer the initiator if we can, to support old userspace */
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
if (sta)
|
||||
if (sta) {
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
sta->sta.tdls_initiator = false;
|
||||
}
|
||||
/* fall-through */
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
@ -575,8 +577,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
* Make the last packet sent take effect for the initiator
|
||||
* value.
|
||||
*/
|
||||
if (sta)
|
||||
if (sta) {
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
sta->sta.tdls_initiator = true;
|
||||
}
|
||||
/* fall-through */
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
initiator = false;
|
||||
|
@ -987,29 +987,34 @@ TRACE_EVENT(drv_flush,
|
||||
|
||||
TRACE_EVENT(drv_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_switch *ch_switch),
|
||||
|
||||
TP_ARGS(local, ch_switch),
|
||||
TP_ARGS(local, sdata, ch_switch),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
CHANDEF_ENTRY
|
||||
__field(u64, timestamp)
|
||||
__field(u32, device_timestamp)
|
||||
__field(bool, block_tx)
|
||||
__field(u8, count)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
CHANDEF_ASSIGN(&ch_switch->chandef)
|
||||
__entry->timestamp = ch_switch->timestamp;
|
||||
__entry->device_timestamp = ch_switch->device_timestamp;
|
||||
__entry->block_tx = ch_switch->block_tx;
|
||||
__entry->count = ch_switch->count;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
|
||||
LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count
|
||||
LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count
|
||||
)
|
||||
);
|
||||
|
||||
@ -1557,9 +1562,26 @@ DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, drv_restart_complete,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
TP_ARGS(local)
|
||||
TRACE_EVENT(drv_reconfig_complete,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
enum ieee80211_reconfig_type reconfig_type),
|
||||
TP_ARGS(local, reconfig_type),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(u8, reconfig_type)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->reconfig_type = reconfig_type;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT " reconfig_type:%d",
|
||||
LOCAL_PR_ARG, __entry->reconfig_type
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
@ -2106,6 +2128,72 @@ TRACE_EVENT(drv_channel_switch_beacon,
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_pre_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_switch *ch_switch),
|
||||
|
||||
TP_ARGS(local, sdata, ch_switch),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
CHANDEF_ENTRY
|
||||
__field(u64, timestamp)
|
||||
__field(bool, block_tx)
|
||||
__field(u8, count)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
CHANDEF_ASSIGN(&ch_switch->chandef)
|
||||
__entry->timestamp = ch_switch->timestamp;
|
||||
__entry->block_tx = ch_switch->block_tx;
|
||||
__entry->count = ch_switch->count;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to "
|
||||
CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu",
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count,
|
||||
__entry->block_tx, __entry->timestamp
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_get_txpower,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
int dbm, int ret),
|
||||
|
||||
TP_ARGS(local, sdata, dbm, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
__field(int, dbm)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
__entry->dbm = dbm;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d",
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
||||
#undef TRACE_SYSTEM
|
||||
|
@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
|
||||
*/
|
||||
return TX_DROP;
|
||||
|
||||
if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
|
||||
return TX_CONTINUE;
|
||||
|
||||
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
||||
return TX_CONTINUE;
|
||||
|
||||
@ -2013,6 +2016,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
goto fail_rcu;
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/* DA SA BSSID */
|
||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
eth_broadcast_addr(hdr.addr3);
|
||||
hdrlen = 24;
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
/* DA SA BSSID */
|
||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||
@ -2057,6 +2071,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
* EAPOL frames from the local station.
|
||||
*/
|
||||
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
(sdata->vif.type != NL80211_IFTYPE_OCB) &&
|
||||
!multicast && !authorized &&
|
||||
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||
|
@ -693,6 +693,34 @@ void ieee80211_iterate_active_interfaces_rtnl(
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
||||
|
||||
static void __iterate_stations(struct ieee80211_local *local,
|
||||
void (*iterator)(void *data,
|
||||
struct ieee80211_sta *sta),
|
||||
void *data)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (!sta->uploaded)
|
||||
continue;
|
||||
|
||||
iterator(data, &sta->sta);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
|
||||
void (*iterator)(void *data,
|
||||
struct ieee80211_sta *sta),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
rcu_read_lock();
|
||||
__iterate_stations(local, iterator, data);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
|
||||
|
||||
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
@ -1073,6 +1101,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
int ac;
|
||||
bool use_11b, enable_qos;
|
||||
bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
|
||||
int aCWmin, aCWmax;
|
||||
|
||||
if (!local->ops->conf_tx)
|
||||
@ -1097,6 +1126,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||
|
||||
is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
|
||||
|
||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||
aCWmax = 1023;
|
||||
if (use_11b)
|
||||
@ -1118,7 +1149,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 7;
|
||||
if (is_ocb)
|
||||
qparam.aifs = 9;
|
||||
else
|
||||
qparam.aifs = 7;
|
||||
break;
|
||||
/* never happens but let's not leave undefined */
|
||||
default:
|
||||
@ -1126,21 +1160,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 3;
|
||||
if (is_ocb)
|
||||
qparam.aifs = 6;
|
||||
else
|
||||
qparam.aifs = 3;
|
||||
break;
|
||||
case IEEE80211_AC_VI:
|
||||
qparam.cw_max = aCWmin;
|
||||
qparam.cw_min = (aCWmin + 1) / 2 - 1;
|
||||
if (use_11b)
|
||||
if (is_ocb)
|
||||
qparam.txop = 0;
|
||||
else if (use_11b)
|
||||
qparam.txop = 6016/32;
|
||||
else
|
||||
qparam.txop = 3008/32;
|
||||
qparam.aifs = 2;
|
||||
|
||||
if (is_ocb)
|
||||
qparam.aifs = 3;
|
||||
else
|
||||
qparam.aifs = 2;
|
||||
break;
|
||||
case IEEE80211_AC_VO:
|
||||
qparam.cw_max = (aCWmin + 1) / 2 - 1;
|
||||
qparam.cw_min = (aCWmin + 1) / 4 - 1;
|
||||
if (use_11b)
|
||||
if (is_ocb)
|
||||
qparam.txop = 0;
|
||||
else if (use_11b)
|
||||
qparam.txop = 3264/32;
|
||||
else
|
||||
qparam.txop = 1504/32;
|
||||
@ -1813,6 +1858,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
sdata_unlock(sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
changed |= BSS_CHANGED_OCB;
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
changed |= BSS_CHANGED_IBSS;
|
||||
/* fall through */
|
||||
@ -1949,7 +1998,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
* We may want to change that later, however.
|
||||
*/
|
||||
if (!local->suspended || reconfig_due_to_wowlan)
|
||||
drv_restart_complete(local);
|
||||
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
|
||||
|
||||
if (!local->suspended)
|
||||
return 0;
|
||||
@ -1960,6 +2009,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
mb();
|
||||
local->resuming = false;
|
||||
|
||||
if (!reconfig_due_to_wowlan)
|
||||
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
continue;
|
||||
@ -2052,42 +2104,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_ie_split - split an IE buffer according to ordering
|
||||
*
|
||||
* @ies: the IE buffer
|
||||
* @ielen: the length of the IE buffer
|
||||
* @ids: an array with element IDs that are allowed before
|
||||
* the split
|
||||
* @n_ids: the size of the element ID array
|
||||
* @offset: offset where to start splitting in the buffer
|
||||
*
|
||||
* This function splits an IE buffer by updating the @offset
|
||||
* variable to point to the location where the buffer should be
|
||||
* split.
|
||||
*
|
||||
* It assumes that the given IE buffer is well-formed, this
|
||||
* has to be guaranteed by the caller!
|
||||
*
|
||||
* It also assumes that the IEs in the buffer are ordered
|
||||
* correctly, if not the result of using this function will not
|
||||
* be ordered correctly either, i.e. it does no reordering.
|
||||
*
|
||||
* The function returns the offset where the next part of the
|
||||
* buffer starts, which may be @ielen if the entire (remainder)
|
||||
* of the buffer should be used.
|
||||
*/
|
||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids, size_t offset)
|
||||
size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids,
|
||||
const u8 *after_ric, int n_after_ric,
|
||||
size_t offset)
|
||||
{
|
||||
size_t pos = offset;
|
||||
|
||||
while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
|
||||
pos += 2 + ies[pos + 1];
|
||||
while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
|
||||
if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
|
||||
pos += 2 + ies[pos + 1];
|
||||
|
||||
while (pos < ielen &&
|
||||
!ieee80211_id_in_list(after_ric, n_after_ric,
|
||||
ies[pos]))
|
||||
pos += 2 + ies[pos + 1];
|
||||
} else {
|
||||
pos += 2 + ies[pos + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||
const u8 *ids, int n_ids, size_t offset)
|
||||
{
|
||||
return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_ie_split);
|
||||
|
||||
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
|
||||
{
|
||||
size_t pos = offset;
|
||||
@ -2526,11 +2572,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, radar_detected_work);
|
||||
struct cfg80211_chan_def chandef = local->hw.conf.chandef;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int num_chanctx = 0;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
continue;
|
||||
|
||||
num_chanctx++;
|
||||
chandef = ctx->conf.def;
|
||||
}
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
ieee80211_dfs_cac_cancel(local);
|
||||
|
||||
if (local->use_chanctx)
|
||||
/* currently not handled */
|
||||
if (num_chanctx > 1)
|
||||
/* XXX: multi-channel is not supported yet */
|
||||
WARN_ON(1);
|
||||
else
|
||||
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
|
||||
|
@ -111,8 +111,6 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
|
||||
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
|
||||
return newhdr + hdrlen;
|
||||
|
||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
||||
IEEE80211_WEP_IV_LEN);
|
||||
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
|
||||
return newhdr + hdrlen;
|
||||
}
|
||||
|
@ -54,10 +54,18 @@ static int wme_downgrade_ac(struct sk_buff *skb)
|
||||
}
|
||||
|
||||
static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
struct sta_info *sta, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
|
||||
/* in case we are a client verify acm is not set for this ac */
|
||||
while (unlikely(sdata->wmm_acm & BIT(skb->priority))) {
|
||||
while (sdata->wmm_acm & BIT(skb->priority)) {
|
||||
int ac = ieee802_1d_to_ac[skb->priority];
|
||||
|
||||
if (ifmgd->tx_tspec[ac].admitted_time &&
|
||||
skb->priority == ifmgd->tx_tspec[ac].up)
|
||||
return ac;
|
||||
|
||||
if (wme_downgrade_ac(skb)) {
|
||||
/*
|
||||
* This should not really happen. The AP has marked all
|
||||
@ -96,7 +104,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
||||
p = ieee80211_get_qos_ctl(hdr);
|
||||
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
|
||||
|
||||
return ieee80211_downgrade_queue(sdata, skb);
|
||||
return ieee80211_downgrade_queue(sdata, NULL, skb);
|
||||
}
|
||||
|
||||
/* Indicate which queue to use. */
|
||||
@ -108,6 +116,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *ra = NULL;
|
||||
bool qos = false;
|
||||
struct mac80211_qos_map *qos_map;
|
||||
u16 ret;
|
||||
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
|
||||
skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||
@ -139,6 +148,10 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
ra = skb->data;
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
/* all stations are required to support WME */
|
||||
qos = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -148,27 +161,29 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
if (sta)
|
||||
qos = sta->sta.wme;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!qos) {
|
||||
skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||
return IEEE80211_AC_BE;
|
||||
ret = IEEE80211_AC_BE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (skb->protocol == sdata->control_port_protocol) {
|
||||
skb->priority = 7;
|
||||
return ieee80211_downgrade_queue(sdata, skb);
|
||||
goto downgrade;
|
||||
}
|
||||
|
||||
/* use the data classifier to determine what 802.1d tag the
|
||||
* data frame has */
|
||||
rcu_read_lock();
|
||||
qos_map = rcu_dereference(sdata->qos_map);
|
||||
skb->priority = cfg80211_classify8021d(skb, qos_map ?
|
||||
&qos_map->qos_map : NULL);
|
||||
rcu_read_unlock();
|
||||
|
||||
return ieee80211_downgrade_queue(sdata, skb);
|
||||
downgrade:
|
||||
ret = ieee80211_downgrade_queue(sdata, sta, skb);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include "ieee80211_i.h"
|
||||
|
||||
extern const int ieee802_1d_to_ac[8];
|
||||
|
||||
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
struct ieee80211_hdr *hdr);
|
||||
|
@ -209,8 +209,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
|
||||
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
|
||||
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
|
||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
||||
IEEE80211_TKIP_IV_LEN);
|
||||
pos += hdrlen;
|
||||
|
||||
/* the HW only needs room for the IV, but not the actual IV */
|
||||
@ -434,8 +432,6 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
|
||||
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
|
||||
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
|
||||
skb_set_network_header(skb, skb_network_offset(skb) +
|
||||
IEEE80211_CCMP_HDR_LEN);
|
||||
|
||||
/* the HW only needs room for the IV, but not the actual IV */
|
||||
if (info->control.hw_key &&
|
||||
@ -575,7 +571,6 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
|
||||
|
||||
pos = skb_push(skb, cs->hdr_len);
|
||||
memmove(pos, pos + cs->hdr_len, hdrlen);
|
||||
skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
|
||||
|
||||
return TX_CONTINUE;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o
|
||||
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
|
||||
|
||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
|
||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
|
||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
||||
|
@ -115,7 +115,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
||||
|
||||
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
||||
int *pri40, int *pri80)
|
||||
u32 *pri40, u32 *pri80)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
*radar_detect |= BIT(wdev->chandef.width);
|
||||
}
|
||||
return;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
if (wdev->chandef.chan) {
|
||||
*chan = wdev->chandef.chan;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
|
@ -86,11 +86,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
|
||||
return &rdev->wiphy;
|
||||
}
|
||||
|
||||
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||
char *newname)
|
||||
static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
|
||||
const char *newname)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev2;
|
||||
int wiphy_idx, taken = -1, result, digits;
|
||||
int wiphy_idx, taken = -1, digits;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
@ -109,16 +109,29 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/* Ignore nop renames */
|
||||
if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
|
||||
return 0;
|
||||
|
||||
/* Ensure another device does not already have this name. */
|
||||
list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
|
||||
if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0)
|
||||
if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||
char *newname)
|
||||
{
|
||||
int result;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* Ignore nop renames */
|
||||
if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
|
||||
return 0;
|
||||
|
||||
result = cfg80211_dev_check_name(rdev, newname);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
result = device_rename(&rdev->wiphy.dev, newname);
|
||||
if (result)
|
||||
return result;
|
||||
@ -309,7 +322,8 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)
|
||||
|
||||
/* exported functions */
|
||||
|
||||
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
||||
const char *requested_name)
|
||||
{
|
||||
static atomic_t wiphy_counter = ATOMIC_INIT(0);
|
||||
|
||||
@ -346,7 +360,31 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
rdev->wiphy_idx--;
|
||||
|
||||
/* give it a proper name */
|
||||
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
|
||||
if (requested_name && requested_name[0]) {
|
||||
int rv;
|
||||
|
||||
rtnl_lock();
|
||||
rv = cfg80211_dev_check_name(rdev, requested_name);
|
||||
|
||||
if (rv < 0) {
|
||||
rtnl_unlock();
|
||||
goto use_default_name;
|
||||
}
|
||||
|
||||
rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
|
||||
rtnl_unlock();
|
||||
if (rv)
|
||||
goto use_default_name;
|
||||
} else {
|
||||
use_default_name:
|
||||
/* NOTE: This is *probably* safe w/out holding rtnl because of
|
||||
* the restrictions on phy names. Probably this call could
|
||||
* fail if some other part of the kernel (re)named a device
|
||||
* phyX. But, might should add some locking and check return
|
||||
* value, and use a different name if this one exists?
|
||||
*/
|
||||
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&rdev->wdev_list);
|
||||
INIT_LIST_HEAD(&rdev->beacon_registrations);
|
||||
@ -406,7 +444,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
|
||||
return &rdev->wiphy;
|
||||
}
|
||||
EXPORT_SYMBOL(wiphy_new);
|
||||
EXPORT_SYMBOL(wiphy_new_nm);
|
||||
|
||||
static int wiphy_verify_combinations(struct wiphy *wiphy)
|
||||
{
|
||||
@ -831,7 +869,22 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
__cfg80211_stop_ap(rdev, dev, true);
|
||||
break;
|
||||
default:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
__cfg80211_leave_ocb(rdev, dev);
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
/* must be handled by mac80211/driver, has no APIs */
|
||||
break;
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
/* cannot happen, has no netdev */
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
/* nothing to do */
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
/* invalid */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -290,6 +290,18 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
/* OCB */
|
||||
int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ocb_setup *setup);
|
||||
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ocb_setup *setup);
|
||||
int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev);
|
||||
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev);
|
||||
|
||||
/* AP */
|
||||
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool notify);
|
||||
|
@ -884,7 +884,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
|
||||
if (!wdev->current_bss)
|
||||
return -ENOLINK;
|
||||
break;
|
||||
default:
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NL80211_IFTYPE_OCB:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1514,8 +1519,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
|
||||
CMD(channel_switch, CHANNEL_SWITCH);
|
||||
CMD(set_qos_map, SET_QOS_MAP);
|
||||
if (rdev->wiphy.flags &
|
||||
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
|
||||
if (rdev->wiphy.features &
|
||||
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
|
||||
CMD(add_tx_ts, ADD_TX_TS);
|
||||
}
|
||||
/* add into the if now */
|
||||
@ -2605,7 +2610,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
!(rdev->wiphy.interface_modes & (1 << type)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
|
||||
if ((type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||
rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
|
||||
info->attrs[NL80211_ATTR_MAC]) {
|
||||
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
|
||||
ETH_ALEN);
|
||||
if (!is_valid_ether_addr(params.macaddr))
|
||||
@ -4398,10 +4405,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
u8 *mac_addr = NULL;
|
||||
struct station_del_parameters params;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
if (info->attrs[NL80211_ATTR_MAC])
|
||||
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||
params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
||||
@ -4412,7 +4421,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!rdev->ops->del_station)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return rdev_del_station(rdev, dev, mac_addr);
|
||||
if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
|
||||
params.subtype =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
|
||||
if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
|
||||
params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* Default to Deauthentication frame */
|
||||
params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_REASON_CODE]) {
|
||||
params.reason_code =
|
||||
nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
||||
if (params.reason_code == 0)
|
||||
return -EINVAL; /* 0 is reserved */
|
||||
} else {
|
||||
/* Default to reason code 2 */
|
||||
params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
||||
}
|
||||
|
||||
return rdev_del_station(rdev, dev, ¶ms);
|
||||
}
|
||||
|
||||
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
||||
@ -4624,6 +4654,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
|
||||
return rdev_del_mpath(rdev, dev, dst);
|
||||
}
|
||||
|
||||
static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
int err;
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct mpath_info pinfo;
|
||||
struct sk_buff *msg;
|
||||
u8 *dst = NULL;
|
||||
u8 mpp[ETH_ALEN];
|
||||
|
||||
memset(&pinfo, 0, sizeof(pinfo));
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MAC])
|
||||
return -EINVAL;
|
||||
|
||||
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||
|
||||
if (!rdev->ops->get_mpp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
|
||||
dev, dst, mpp, &pinfo) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int nl80211_dump_mpp(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct mpath_info pinfo;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
u8 dst[ETH_ALEN];
|
||||
u8 mpp[ETH_ALEN];
|
||||
int path_idx = cb->args[2];
|
||||
int err;
|
||||
|
||||
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!rdev->ops->dump_mpp) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
|
||||
mpp, &pinfo);
|
||||
if (err == -ENOENT)
|
||||
break;
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
wdev->netdev, dst, mpp,
|
||||
&pinfo) < 0)
|
||||
goto out;
|
||||
|
||||
path_idx++;
|
||||
}
|
||||
|
||||
out:
|
||||
cb->args[2] = path_idx;
|
||||
err = skb->len;
|
||||
out_err:
|
||||
nl80211_finish_wdev_dump(rdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -5923,10 +6043,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
* function is called under RTNL lock, so this should not be a problem.
|
||||
*/
|
||||
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
||||
u8 radar_detect_width = 0;
|
||||
int err;
|
||||
bool need_new_beacon = false;
|
||||
int len, i;
|
||||
u32 cs_count;
|
||||
|
||||
if (!rdev->ops->channel_switch ||
|
||||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
||||
@ -5963,7 +6083,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
|
||||
return -EINVAL;
|
||||
|
||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
/* Even though the attribute is u32, the specification says
|
||||
* u8, so let's make sure we don't overflow.
|
||||
*/
|
||||
cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
if (cs_count > 255)
|
||||
return -EINVAL;
|
||||
|
||||
params.count = cs_count;
|
||||
|
||||
if (!need_new_beacon)
|
||||
goto skip_beacons;
|
||||
@ -6051,10 +6178,8 @@ skip_beacons:
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (err > 0) {
|
||||
radar_detect_width = BIT(params.chandef.width);
|
||||
if (err > 0)
|
||||
params.radar_required = true;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
||||
params.block_tx = true;
|
||||
@ -8151,6 +8276,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct ocb_setup setup = {};
|
||||
int err;
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return cfg80211_join_ocb(rdev, dev, &setup);
|
||||
}
|
||||
|
||||
static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
|
||||
return cfg80211_leave_ocb(rdev, dev);
|
||||
}
|
||||
|
||||
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -9436,7 +9583,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
||||
u16 admitted_time = 0;
|
||||
int err;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
|
||||
if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
|
||||
@ -9452,12 +9599,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
|
||||
/* WMM uses TIDs 0-7 even for TSPEC */
|
||||
if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
|
||||
/* TODO: handle 802.11 TSPEC/admission control
|
||||
* need more attributes for that (e.g. BA session requirement)
|
||||
* need more attributes for that (e.g. BA session requirement);
|
||||
* change the WMM adminssion test above to allow both then
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -9773,6 +9918,15 @@ static const struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_GET_MPP,
|
||||
.doit = nl80211_get_mpp,
|
||||
.dumpit = nl80211_dump_mpp,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_SET_MPATH,
|
||||
.doit = nl80211_set_mpath,
|
||||
@ -10087,6 +10241,22 @@ static const struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_JOIN_OCB,
|
||||
.doit = nl80211_join_ocb,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_LEAVE_OCB,
|
||||
.doit = nl80211_leave_ocb,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
#ifdef CONFIG_PM
|
||||
{
|
||||
.cmd = NL80211_CMD_GET_WOWLAN,
|
||||
|
88
net/wireless/ocb.c
Normal file
88
net/wireless/ocb.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* OCB mode implementation
|
||||
*
|
||||
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||
* (c) 2014 Volkswagen Group Research
|
||||
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||
* Funded by: Volkswagen Group Research
|
||||
*
|
||||
* 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/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "nl80211.h"
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ocb_setup *setup)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (WARN_ON(!setup->chandef.chan))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdev_join_ocb(rdev, dev, setup);
|
||||
if (!err)
|
||||
wdev->chandef = setup->chandef;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ocb_setup *setup)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_join_ocb(rdev, dev, setup);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rdev->ops->leave_ocb)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = rdev_leave_ocb(rdev, dev);
|
||||
if (!err)
|
||||
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_leave_ocb(rdev, dev);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
@ -178,11 +178,12 @@ static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
|
||||
}
|
||||
|
||||
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *mac)
|
||||
struct net_device *dev,
|
||||
struct station_del_parameters *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_station(&rdev->wiphy, dev, mac);
|
||||
ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
|
||||
trace_rdev_del_station(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->del_station(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
@ -263,6 +264,18 @@ static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
|
||||
|
||||
}
|
||||
|
||||
static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *dst, u8 *mpp,
|
||||
struct mpath_info *pinfo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp);
|
||||
ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo);
|
||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, int idx, u8 *dst,
|
||||
u8 *next_hop, struct mpath_info *pinfo)
|
||||
@ -271,7 +284,20 @@ static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
||||
int ret;
|
||||
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
|
||||
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
|
||||
pinfo);
|
||||
pinfo);
|
||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, int idx, u8 *dst,
|
||||
u8 *mpp, struct mpath_info *pinfo)
|
||||
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp);
|
||||
ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo);
|
||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||
return ret;
|
||||
}
|
||||
@ -322,6 +348,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ocb_setup *setup)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
|
||||
ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_leave_ocb(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct bss_parameters *params)
|
||||
|
@ -80,9 +80,18 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
if (wdev->conn->params.channel)
|
||||
if (wdev->conn->params.channel) {
|
||||
enum ieee80211_band band = wdev->conn->params.channel->band;
|
||||
struct ieee80211_supported_band *sband =
|
||||
wdev->wiphy->bands[band];
|
||||
|
||||
if (!sband) {
|
||||
kfree(request);
|
||||
return -EINVAL;
|
||||
}
|
||||
request->channels[0] = wdev->conn->params.channel;
|
||||
else {
|
||||
request->rates[band] = (1 << sband->n_bitrates) - 1;
|
||||
} else {
|
||||
int i = 0, j;
|
||||
enum ieee80211_band band;
|
||||
struct ieee80211_supported_band *bands;
|
||||
|
@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
|
||||
TP_ARGS(wiphy, netdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||
TP_ARGS(wiphy, netdev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
|
||||
TP_ARGS(wiphy, netdev)
|
||||
@ -680,9 +685,34 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
|
||||
TP_ARGS(wiphy, netdev, mac)
|
||||
DECLARE_EVENT_CLASS(station_del,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
struct station_del_parameters *params),
|
||||
TP_ARGS(wiphy, netdev, params),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
MAC_ENTRY(sta_mac)
|
||||
__field(u8, subtype)
|
||||
__field(u16, reason_code)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(sta_mac, params->mac);
|
||||
__entry->subtype = params->subtype;
|
||||
__entry->reason_code = params->reason_code;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
|
||||
", subtype: %u, reason_code: %u",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
|
||||
__entry->subtype, __entry->reason_code)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(station_del, rdev_del_station,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
struct station_del_parameters *params),
|
||||
TP_ARGS(wiphy, netdev, params)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
|
||||
@ -801,6 +831,51 @@ TRACE_EVENT(rdev_dump_mpath,
|
||||
MAC_PR_ARG(next_hop))
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_get_mpp,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
u8 *dst, u8 *mpp),
|
||||
TP_ARGS(wiphy, netdev, dst, mpp),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
MAC_ENTRY(dst)
|
||||
MAC_ENTRY(mpp)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(dst, dst);
|
||||
MAC_ASSIGN(mpp, mpp);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT
|
||||
", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG,
|
||||
MAC_PR_ARG(dst), MAC_PR_ARG(mpp))
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_dump_mpp,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
|
||||
u8 *dst, u8 *mpp),
|
||||
TP_ARGS(wiphy, netdev, idx, mpp, dst),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
MAC_ENTRY(dst)
|
||||
MAC_ENTRY(mpp)
|
||||
__field(int, idx)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(dst, dst);
|
||||
MAC_ASSIGN(mpp, mpp);
|
||||
__entry->idx = idx;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
|
||||
MAC_PR_FMT ", mpp: " MAC_PR_FMT,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
|
||||
MAC_PR_ARG(mpp))
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_return_int_mpath_info,
|
||||
TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
|
||||
TP_ARGS(wiphy, ret, pinfo),
|
||||
@ -1246,6 +1321,22 @@ TRACE_EVENT(rdev_join_ibss,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_join_ocb,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
const struct ocb_setup *setup),
|
||||
TP_ARGS(wiphy, netdev, setup),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_set_wiphy_params,
|
||||
TP_PROTO(struct wiphy *wiphy, u32 changed),
|
||||
TP_ARGS(wiphy, changed),
|
||||
|
@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
break;
|
||||
case cpu_to_le16(0):
|
||||
if (iftype != NL80211_IFTYPE_ADHOC &&
|
||||
iftype != NL80211_IFTYPE_STATION)
|
||||
iftype != NL80211_IFTYPE_STATION &&
|
||||
iftype != NL80211_IFTYPE_OCB)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
|
||||
memcpy(hdr.addr3, skb->data, ETH_ALEN);
|
||||
hdrlen = 24;
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
/* DA SA BSSID */
|
||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||
@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
|
||||
if (dev->ieee80211_ptr->use_4addr)
|
||||
break;
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_OCB:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
dev->priv_flags |= IFF_DONT_BRIDGE;
|
||||
|
Loading…
Reference in New Issue
Block a user