mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 15:41:36 +00:00
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
commit
0f62248501
@ -511,8 +511,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
|
||||
ath5k_vif_iter(&iter_data, vif->addr, vif);
|
||||
|
||||
/* Get list of all active MAC addresses */
|
||||
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
|
||||
&iter_data);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath5k_vif_iter, &iter_data);
|
||||
memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN);
|
||||
|
||||
ah->opmode = iter_data.opmode;
|
||||
@ -3045,8 +3046,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah)
|
||||
iter_data.need_set_hw_addr = false;
|
||||
iter_data.found_active = true;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
|
||||
&iter_data);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath5k_vif_iter, &iter_data);
|
||||
return iter_data.any_assoc;
|
||||
}
|
||||
|
||||
|
@ -452,8 +452,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
|
||||
iter_data.hw_macaddr = NULL;
|
||||
iter_data.n_stas = 0;
|
||||
iter_data.need_set_hw_addr = false;
|
||||
ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter,
|
||||
&iter_data);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath5k_vif_iter, &iter_data);
|
||||
|
||||
/* Set up RX Filter */
|
||||
if (iter_data.n_stas > 1) {
|
||||
|
@ -1384,11 +1384,8 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The type nl80211_tx_power_setting replaces the following
|
||||
* data type from 2.6.36 onwards
|
||||
*/
|
||||
static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type,
|
||||
int mbm)
|
||||
{
|
||||
@ -1423,7 +1420,9 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
|
||||
static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
int *dbm)
|
||||
{
|
||||
struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
|
||||
struct ath6kl_vif *vif;
|
||||
|
@ -587,9 +587,9 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv,
|
||||
(priv->num_sta_vif > 1) &&
|
||||
(vif->type == NL80211_IFTYPE_STATION)) {
|
||||
beacon_configured = false;
|
||||
ieee80211_iterate_active_interfaces_atomic(priv->hw,
|
||||
ath9k_htc_beacon_iter,
|
||||
&beacon_configured);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_htc_beacon_iter, &beacon_configured);
|
||||
|
||||
if (beacon_configured) {
|
||||
ath_dbg(common, CONFIG,
|
||||
|
@ -127,8 +127,9 @@ static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
|
||||
priv->rearm_ani = false;
|
||||
priv->reconfig_beacon = false;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(priv->hw,
|
||||
ath9k_htc_vif_iter, priv);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_htc_vif_iter, priv);
|
||||
if (priv->rearm_ani)
|
||||
ath9k_htc_start_ani(priv);
|
||||
|
||||
@ -165,8 +166,9 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
|
||||
ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
|
||||
|
||||
/* Get list of all active MAC addresses */
|
||||
ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
|
||||
&iter_data);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_htc_bssid_iter, &iter_data);
|
||||
|
||||
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
|
||||
ath_hw_setbssidmask(common);
|
||||
@ -1144,8 +1146,9 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
|
||||
*/
|
||||
if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
|
||||
priv->rearm_ani = false;
|
||||
ieee80211_iterate_active_interfaces_atomic(priv->hw,
|
||||
ath9k_htc_vif_iter, priv);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_htc_vif_iter, priv);
|
||||
if (!priv->rearm_ani)
|
||||
ath9k_htc_stop_ani(priv);
|
||||
}
|
||||
@ -1466,8 +1469,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
||||
static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
if (priv->num_sta_assoc_vif == 1) {
|
||||
ieee80211_iterate_active_interfaces_atomic(priv->hw,
|
||||
ath9k_htc_bss_iter, priv);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_htc_bss_iter, priv);
|
||||
ath9k_htc_set_bssid(priv);
|
||||
}
|
||||
}
|
||||
|
@ -924,8 +924,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
|
||||
ath9k_vif_iter(iter_data, vif->addr, vif);
|
||||
|
||||
/* Get list of all active MAC addresses */
|
||||
ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
|
||||
iter_data);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_vif_iter, iter_data);
|
||||
}
|
||||
|
||||
/* Called with sc->mutex held. */
|
||||
@ -975,8 +976,9 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
|
||||
if (ah->opmode == NL80211_IFTYPE_STATION &&
|
||||
old_opmode == NL80211_IFTYPE_AP &&
|
||||
test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
|
||||
ieee80211_iterate_active_interfaces_atomic(sc->hw,
|
||||
ath9k_sta_vif_iter, sc);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_sta_vif_iter, sc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1505,8 +1507,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
|
||||
clear_bit(SC_OP_BEACONS, &sc->sc_flags);
|
||||
}
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(sc->hw,
|
||||
ath9k_bss_assoc_iter, sc);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
ath9k_bss_assoc_iter, sc);
|
||||
|
||||
if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) &&
|
||||
ah->opmode == NL80211_IFTYPE_STATION) {
|
||||
|
@ -1529,7 +1529,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
|
||||
}
|
||||
|
||||
static s32
|
||||
brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
|
||||
brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, s32 mbm)
|
||||
{
|
||||
|
||||
@ -1578,7 +1578,9 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
|
||||
static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
s32 *dbm)
|
||||
{
|
||||
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
|
||||
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
|
||||
|
@ -44,9 +44,9 @@ static int radios = 2;
|
||||
module_param(radios, int, 0444);
|
||||
MODULE_PARM_DESC(radios, "Number of simulated radios");
|
||||
|
||||
static bool fake_hw_scan;
|
||||
module_param(fake_hw_scan, bool, 0444);
|
||||
MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler");
|
||||
static int channels = 1;
|
||||
module_param(channels, int, 0444);
|
||||
MODULE_PARM_DESC(channels, "Number of concurrent channels");
|
||||
|
||||
/**
|
||||
* enum hwsim_regtest - the type of regulatory tests we offer
|
||||
@ -166,7 +166,9 @@ struct hwsim_vif_priv {
|
||||
static inline void hwsim_check_magic(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
|
||||
WARN_ON(vp->magic != HWSIM_VIF_MAGIC);
|
||||
WARN(vp->magic != HWSIM_VIF_MAGIC,
|
||||
"Invalid VIF (%p) magic %#x, %pM, %d/%d\n",
|
||||
vif, vp->magic, vif->addr, vif->type, vif->p2p);
|
||||
}
|
||||
|
||||
static inline void hwsim_set_magic(struct ieee80211_vif *vif)
|
||||
@ -185,7 +187,7 @@ struct hwsim_sta_priv {
|
||||
u32 magic;
|
||||
};
|
||||
|
||||
#define HWSIM_STA_MAGIC 0x6d537748
|
||||
#define HWSIM_STA_MAGIC 0x6d537749
|
||||
|
||||
static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta)
|
||||
{
|
||||
@ -205,6 +207,30 @@ static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta)
|
||||
sp->magic = 0;
|
||||
}
|
||||
|
||||
struct hwsim_chanctx_priv {
|
||||
u32 magic;
|
||||
};
|
||||
|
||||
#define HWSIM_CHANCTX_MAGIC 0x6d53774a
|
||||
|
||||
static inline void hwsim_check_chanctx_magic(struct ieee80211_chanctx_conf *c)
|
||||
{
|
||||
struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
|
||||
WARN_ON(cp->magic != HWSIM_CHANCTX_MAGIC);
|
||||
}
|
||||
|
||||
static inline void hwsim_set_chanctx_magic(struct ieee80211_chanctx_conf *c)
|
||||
{
|
||||
struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
|
||||
cp->magic = HWSIM_CHANCTX_MAGIC;
|
||||
}
|
||||
|
||||
static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c)
|
||||
{
|
||||
struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
|
||||
cp->magic = 0;
|
||||
}
|
||||
|
||||
static struct class *hwsim_class;
|
||||
|
||||
static struct net_device *hwsim_mon; /* global monitor netdev */
|
||||
@ -299,6 +325,13 @@ struct mac80211_hwsim_data {
|
||||
|
||||
struct mac_address addresses[2];
|
||||
|
||||
struct ieee80211_channel *tmp_chan;
|
||||
struct delayed_work roc_done;
|
||||
struct delayed_work hw_scan;
|
||||
struct cfg80211_scan_request *hw_scan_request;
|
||||
struct ieee80211_vif *hw_scan_vif;
|
||||
int scan_chan_idx;
|
||||
|
||||
struct ieee80211_channel *channel;
|
||||
unsigned long beacon_int; /* in jiffies unit */
|
||||
unsigned int rx_filter;
|
||||
@ -396,7 +429,8 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
|
||||
struct sk_buff *tx_skb)
|
||||
struct sk_buff *tx_skb,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
struct sk_buff *skb;
|
||||
@ -423,7 +457,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
|
||||
hdr->rt_tsft = __mac80211_hwsim_get_tsf(data);
|
||||
hdr->rt_flags = 0;
|
||||
hdr->rt_rate = txrate->bitrate / 5;
|
||||
hdr->rt_channel = cpu_to_le16(data->channel->center_freq);
|
||||
hdr->rt_channel = cpu_to_le16(chan->center_freq);
|
||||
flags = IEEE80211_CHAN_2GHZ;
|
||||
if (txrate->flags & IEEE80211_RATE_ERP_G)
|
||||
flags |= IEEE80211_CHAN_OFDM;
|
||||
@ -441,9 +475,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
|
||||
}
|
||||
|
||||
|
||||
static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr)
|
||||
static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
struct sk_buff *skb;
|
||||
struct hwsim_radiotap_hdr *hdr;
|
||||
u16 flags;
|
||||
@ -464,7 +498,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr)
|
||||
(1 << IEEE80211_RADIOTAP_CHANNEL));
|
||||
hdr->rt_flags = 0;
|
||||
hdr->rt_rate = 0;
|
||||
hdr->rt_channel = cpu_to_le16(data->channel->center_freq);
|
||||
hdr->rt_channel = cpu_to_le16(chan->center_freq);
|
||||
flags = IEEE80211_CHAN_2GHZ;
|
||||
hdr->rt_chbitmask = cpu_to_le16(flags);
|
||||
|
||||
@ -537,6 +571,7 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
|
||||
md.ret = false;
|
||||
md.addr = addr;
|
||||
ieee80211_iterate_active_interfaces_atomic(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_addr_iter,
|
||||
&md);
|
||||
|
||||
@ -556,12 +591,6 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||
int i;
|
||||
struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
|
||||
|
||||
if (data->idle) {
|
||||
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
|
||||
dev_kfree_skb(my_skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->ps != PS_DISABLED)
|
||||
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
||||
/* If the queue contains MAX_QUEUE skb's drop some */
|
||||
@ -629,8 +658,38 @@ nla_put_failure:
|
||||
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
|
||||
}
|
||||
|
||||
static bool hwsim_chans_compat(struct ieee80211_channel *c1,
|
||||
struct ieee80211_channel *c2)
|
||||
{
|
||||
if (!c1 || !c2)
|
||||
return false;
|
||||
|
||||
return c1->center_freq == c2->center_freq;
|
||||
}
|
||||
|
||||
struct tx_iter_data {
|
||||
struct ieee80211_channel *channel;
|
||||
bool receive;
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_tx_iter(void *_data, u8 *addr,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct tx_iter_data *data = _data;
|
||||
|
||||
if (!vif->chanctx_conf)
|
||||
return;
|
||||
|
||||
if (!hwsim_chans_compat(data->channel,
|
||||
rcu_dereference(vif->chanctx_conf)->channel))
|
||||
return;
|
||||
|
||||
data->receive = true;
|
||||
}
|
||||
|
||||
static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv, *data2;
|
||||
bool ack = false;
|
||||
@ -639,15 +698,10 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
struct ieee80211_rx_status rx_status;
|
||||
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
|
||||
|
||||
if (data->idle) {
|
||||
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&rx_status, 0, sizeof(rx_status));
|
||||
rx_status.flag |= RX_FLAG_MACTIME_MPDU;
|
||||
rx_status.freq = data->channel->center_freq;
|
||||
rx_status.band = data->channel->band;
|
||||
rx_status.freq = chan->center_freq;
|
||||
rx_status.band = chan->band;
|
||||
rx_status.rate_idx = info->control.rates[0].idx;
|
||||
if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
|
||||
rx_status.flag |= RX_FLAG_HT;
|
||||
@ -673,16 +727,30 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
list_for_each_entry(data2, &hwsim_radios, list) {
|
||||
struct sk_buff *nskb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct tx_iter_data tx_iter_data = {
|
||||
.receive = false,
|
||||
.channel = chan,
|
||||
};
|
||||
|
||||
if (data == data2)
|
||||
continue;
|
||||
|
||||
if (data2->idle || !data2->started ||
|
||||
!hwsim_ps_rx_ok(data2, skb) || !data2->channel ||
|
||||
data->channel->center_freq != data2->channel->center_freq ||
|
||||
!(data->group & data2->group))
|
||||
if (!data2->started || (data2->idle && !data2->tmp_chan) ||
|
||||
!hwsim_ps_rx_ok(data2, skb))
|
||||
continue;
|
||||
|
||||
if (!(data->group & data2->group))
|
||||
continue;
|
||||
|
||||
if (!hwsim_chans_compat(chan, data2->tmp_chan) &&
|
||||
!hwsim_chans_compat(chan, data2->channel)) {
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
data2->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_tx_iter, &tx_iter_data);
|
||||
if (!tx_iter_data.receive)
|
||||
continue;
|
||||
}
|
||||
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (nskb == NULL)
|
||||
continue;
|
||||
@ -713,18 +781,51 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_tx_control *control,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_channel *channel;
|
||||
bool ack;
|
||||
struct ieee80211_tx_info *txi;
|
||||
u32 _portid;
|
||||
|
||||
mac80211_hwsim_monitor_rx(hw, skb);
|
||||
|
||||
if (skb->len < 10) {
|
||||
if (WARN_ON(skb->len < 10)) {
|
||||
/* Should not happen; just a sanity check for addr1 use */
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
channel = data->channel;
|
||||
} else if (txi->hw_queue == 4) {
|
||||
channel = data->tmp_chan;
|
||||
} else {
|
||||
chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf);
|
||||
if (chanctx_conf)
|
||||
channel = chanctx_conf->channel;
|
||||
else
|
||||
channel = NULL;
|
||||
}
|
||||
|
||||
if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->idle && !data->tmp_chan) {
|
||||
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (txi->control.vif)
|
||||
hwsim_check_magic(txi->control.vif);
|
||||
if (control->sta)
|
||||
hwsim_check_sta_magic(control->sta);
|
||||
|
||||
txi->rate_driver_data[0] = channel;
|
||||
|
||||
mac80211_hwsim_monitor_rx(hw, skb, channel);
|
||||
|
||||
/* wmediumd mode check */
|
||||
_portid = ACCESS_ONCE(wmediumd_portid);
|
||||
|
||||
@ -732,15 +833,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
||||
|
||||
/* NO wmediumd detected, perfect medium simulation */
|
||||
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
|
||||
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
|
||||
|
||||
if (ack && skb->len >= 16) {
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
|
||||
mac80211_hwsim_monitor_ack(channel, hdr->addr2);
|
||||
}
|
||||
|
||||
txi = IEEE80211_SKB_CB(skb);
|
||||
|
||||
ieee80211_tx_info_clear_status(txi);
|
||||
|
||||
/* frame was transmitted at most favorable rate at first attempt */
|
||||
@ -778,6 +877,13 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
|
||||
__func__, ieee80211_vif_type_p2p(vif),
|
||||
vif->addr);
|
||||
hwsim_set_magic(vif);
|
||||
|
||||
vif->cab_queue = 0;
|
||||
vif->hw_queue[IEEE80211_AC_VO] = 0;
|
||||
vif->hw_queue[IEEE80211_AC_VI] = 1;
|
||||
vif->hw_queue[IEEE80211_AC_BE] = 2;
|
||||
vif->hw_queue[IEEE80211_AC_BK] = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -807,14 +913,26 @@ static void mac80211_hwsim_remove_interface(
|
||||
hwsim_clear_magic(vif);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||
struct sk_buff *skb,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
u32 _pid = ACCESS_ONCE(wmediumd_portid);
|
||||
|
||||
mac80211_hwsim_monitor_rx(hw, skb, chan);
|
||||
|
||||
if (_pid)
|
||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
|
||||
|
||||
mac80211_hwsim_tx_frame_no_nl(hw, skb, chan);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_hw *hw = arg;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_tx_info *info;
|
||||
u32 _portid;
|
||||
|
||||
hwsim_check_magic(vif);
|
||||
|
||||
@ -826,18 +944,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
|
||||
skb = ieee80211_beacon_get(hw, vif);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
mac80211_hwsim_monitor_rx(hw, skb);
|
||||
|
||||
/* wmediumd mode check */
|
||||
_portid = ACCESS_ONCE(wmediumd_portid);
|
||||
|
||||
if (_portid)
|
||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
||||
|
||||
mac80211_hwsim_tx_frame_no_nl(hw, skb);
|
||||
dev_kfree_skb(skb);
|
||||
mac80211_hwsim_tx_frame(hw, skb,
|
||||
rcu_dereference(vif->chanctx_conf)->channel);
|
||||
}
|
||||
|
||||
|
||||
@ -850,7 +959,8 @@ static void mac80211_hwsim_beacon(unsigned long arg)
|
||||
return;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
hw, mac80211_hwsim_beacon_tx, hw);
|
||||
hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_beacon_tx, hw);
|
||||
|
||||
data->beacon_timer.expires = jiffies + data->beacon_int;
|
||||
add_timer(&data->beacon_timer);
|
||||
@ -877,7 +987,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
|
||||
wiphy_debug(hw->wiphy,
|
||||
"%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
|
||||
__func__,
|
||||
conf->channel->center_freq,
|
||||
conf->channel ? conf->channel->center_freq : 0,
|
||||
hwsim_chantypes[conf->channel_type],
|
||||
!!(conf->flags & IEEE80211_CONF_IDLE),
|
||||
!!(conf->flags & IEEE80211_CONF_PS),
|
||||
@ -886,6 +996,9 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
|
||||
data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
|
||||
|
||||
data->channel = conf->channel;
|
||||
|
||||
WARN_ON(data->channel && channels > 1);
|
||||
|
||||
data->power_level = conf->power_level;
|
||||
if (!data->started || !data->beacon_int)
|
||||
del_timer(&data->beacon_timer);
|
||||
@ -972,6 +1085,9 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
wiphy_debug(hw->wiphy, " BASIC_RATES: 0x%llx\n",
|
||||
(unsigned long long) info->basic_rates);
|
||||
}
|
||||
|
||||
if (changed & BSS_CHANGED_TXPOWER)
|
||||
wiphy_debug(hw->wiphy, " TX Power: %d dBm\n", info->txpower);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw,
|
||||
@ -1166,45 +1282,102 @@ static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
|
||||
/* Not implemented, queues only on kernel side */
|
||||
}
|
||||
|
||||
struct hw_scan_done {
|
||||
struct delayed_work w;
|
||||
struct ieee80211_hw *hw;
|
||||
};
|
||||
|
||||
static void hw_scan_done(struct work_struct *work)
|
||||
static void hw_scan_work(struct work_struct *work)
|
||||
{
|
||||
struct hw_scan_done *hsd =
|
||||
container_of(work, struct hw_scan_done, w.work);
|
||||
struct mac80211_hwsim_data *hwsim =
|
||||
container_of(work, struct mac80211_hwsim_data, hw_scan.work);
|
||||
struct cfg80211_scan_request *req = hwsim->hw_scan_request;
|
||||
int dwell, i;
|
||||
|
||||
ieee80211_scan_completed(hsd->hw, false);
|
||||
kfree(hsd);
|
||||
mutex_lock(&hwsim->mutex);
|
||||
if (hwsim->scan_chan_idx >= req->n_channels) {
|
||||
wiphy_debug(hwsim->hw->wiphy, "hw scan complete\n");
|
||||
ieee80211_scan_completed(hwsim->hw, false);
|
||||
hwsim->hw_scan_request = NULL;
|
||||
hwsim->hw_scan_vif = NULL;
|
||||
hwsim->tmp_chan = NULL;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
wiphy_debug(hwsim->hw->wiphy, "hw scan %d MHz\n",
|
||||
req->channels[hwsim->scan_chan_idx]->center_freq);
|
||||
|
||||
hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx];
|
||||
if (hwsim->tmp_chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
|
||||
!req->n_ssids) {
|
||||
dwell = 120;
|
||||
} else {
|
||||
dwell = 30;
|
||||
/* send probes */
|
||||
for (i = 0; i < req->n_ssids; i++) {
|
||||
struct sk_buff *probe;
|
||||
|
||||
probe = ieee80211_probereq_get(hwsim->hw,
|
||||
hwsim->hw_scan_vif,
|
||||
req->ssids[i].ssid,
|
||||
req->ssids[i].ssid_len,
|
||||
req->ie, req->ie_len);
|
||||
if (!probe)
|
||||
continue;
|
||||
local_bh_disable();
|
||||
mac80211_hwsim_tx_frame(hwsim->hw, probe,
|
||||
hwsim->tmp_chan);
|
||||
local_bh_enable();
|
||||
}
|
||||
}
|
||||
ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan,
|
||||
msecs_to_jiffies(dwell));
|
||||
hwsim->scan_chan_idx++;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req)
|
||||
{
|
||||
struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL);
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
int i;
|
||||
|
||||
if (!hsd)
|
||||
return -ENOMEM;
|
||||
mutex_lock(&hwsim->mutex);
|
||||
if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
hwsim->hw_scan_request = req;
|
||||
hwsim->hw_scan_vif = vif;
|
||||
hwsim->scan_chan_idx = 0;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
|
||||
hsd->hw = hw;
|
||||
INIT_DELAYED_WORK(&hsd->w, hw_scan_done);
|
||||
|
||||
printk(KERN_DEBUG "hwsim hw_scan request\n");
|
||||
wiphy_debug(hw->wiphy, "hwsim hw_scan request\n");
|
||||
for (i = 0; i < req->n_channels; i++)
|
||||
printk(KERN_DEBUG "hwsim hw_scan freq %d\n",
|
||||
req->channels[i]->center_freq);
|
||||
print_hex_dump(KERN_DEBUG, "scan IEs: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, req->ie, req->ie_len, 1);
|
||||
|
||||
ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ);
|
||||
ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwsim cancel_hw_scan\n");
|
||||
|
||||
cancel_delayed_work_sync(&hwsim->hw_scan);
|
||||
|
||||
mutex_lock(&hwsim->mutex);
|
||||
ieee80211_scan_completed(hwsim->hw, true);
|
||||
hwsim->tmp_chan = NULL;
|
||||
hwsim->hw_scan_request = NULL;
|
||||
hwsim->hw_scan_vif = NULL;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
@ -1235,6 +1408,105 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
|
||||
static void hw_roc_done(struct work_struct *work)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim =
|
||||
container_of(work, struct mac80211_hwsim_data, roc_done.work);
|
||||
|
||||
mutex_lock(&hwsim->mutex);
|
||||
ieee80211_remain_on_channel_expired(hwsim->hw);
|
||||
hwsim->tmp_chan = NULL;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
|
||||
wiphy_debug(hwsim->hw->wiphy, "hwsim ROC expired\n");
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_channel_type channel_type,
|
||||
int duration)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
mutex_lock(&hwsim->mutex);
|
||||
if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hwsim->tmp_chan = chan;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n",
|
||||
chan->center_freq, duration);
|
||||
|
||||
ieee80211_ready_on_channel(hw);
|
||||
|
||||
ieee80211_queue_delayed_work(hw, &hwsim->roc_done,
|
||||
msecs_to_jiffies(duration));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
cancel_delayed_work_sync(&hwsim->roc_done);
|
||||
|
||||
mutex_lock(&hwsim->mutex);
|
||||
hwsim->tmp_chan = NULL;
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwsim ROC canceled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
hwsim_set_chanctx_magic(ctx);
|
||||
wiphy_debug(hw->wiphy, "add channel context %d MHz/%d\n",
|
||||
ctx->channel->center_freq, ctx->channel_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
wiphy_debug(hw->wiphy, "remove channel context %d MHz/%d\n",
|
||||
ctx->channel->center_freq, ctx->channel_type);
|
||||
hwsim_check_chanctx_magic(ctx);
|
||||
hwsim_clear_chanctx_magic(ctx);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx,
|
||||
u32 changed)
|
||||
{
|
||||
hwsim_check_chanctx_magic(ctx);
|
||||
wiphy_debug(hw->wiphy, "change channel context %#x (%d MHz/%d)\n",
|
||||
changed, ctx->channel->center_freq, ctx->channel_type);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
hwsim_check_magic(vif);
|
||||
hwsim_check_chanctx_magic(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
hwsim_check_magic(vif);
|
||||
hwsim_check_chanctx_magic(ctx);
|
||||
}
|
||||
|
||||
static struct ieee80211_ops mac80211_hwsim_ops =
|
||||
{
|
||||
.tx = mac80211_hwsim_tx,
|
||||
@ -1315,7 +1587,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
|
||||
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_pspoll *pspoll;
|
||||
u32 _portid;
|
||||
|
||||
if (!vp->assoc)
|
||||
return;
|
||||
@ -1335,25 +1606,18 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
|
||||
memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
|
||||
memcpy(pspoll->ta, mac, ETH_ALEN);
|
||||
|
||||
/* wmediumd mode check */
|
||||
_portid = ACCESS_ONCE(wmediumd_portid);
|
||||
|
||||
if (_portid)
|
||||
return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid);
|
||||
|
||||
if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
|
||||
printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
|
||||
dev_kfree_skb(skb);
|
||||
rcu_read_lock();
|
||||
mac80211_hwsim_tx_frame(data->hw, skb,
|
||||
rcu_dereference(vif->chanctx_conf)->channel);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
||||
static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
|
||||
struct ieee80211_vif *vif, int ps)
|
||||
{
|
||||
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u32 _portid;
|
||||
|
||||
if (!vp->assoc)
|
||||
return;
|
||||
@ -1374,15 +1638,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
|
||||
memcpy(hdr->addr2, mac, ETH_ALEN);
|
||||
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
|
||||
|
||||
/* wmediumd mode check */
|
||||
_portid = ACCESS_ONCE(wmediumd_portid);
|
||||
|
||||
if (_portid)
|
||||
return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid);
|
||||
|
||||
if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
|
||||
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
|
||||
dev_kfree_skb(skb);
|
||||
rcu_read_lock();
|
||||
mac80211_hwsim_tx_frame(data->hw, skb,
|
||||
rcu_dereference(vif->chanctx_conf)->channel);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
||||
@ -1423,14 +1682,17 @@ static int hwsim_fops_ps_write(void *dat, u64 val)
|
||||
|
||||
if (val == PS_MANUAL_POLL) {
|
||||
ieee80211_iterate_active_interfaces(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
hwsim_send_ps_poll, data);
|
||||
data->ps_poll_pending = true;
|
||||
} else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
|
||||
ieee80211_iterate_active_interfaces(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
hwsim_send_nullfunc_ps,
|
||||
data);
|
||||
} else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
|
||||
ieee80211_iterate_active_interfaces(data->hw,
|
||||
IEEE80211_IFACE_ITER_NORMAL,
|
||||
hwsim_send_nullfunc_no_ps,
|
||||
data);
|
||||
}
|
||||
@ -1551,7 +1813,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
|
||||
(hwsim_flags & HWSIM_TX_STAT_ACK)) {
|
||||
if (skb->len >= 16) {
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
|
||||
mac80211_hwsim_monitor_ack(txi->rate_driver_data[0],
|
||||
hdr->addr2);
|
||||
}
|
||||
txi->flags |= IEEE80211_TX_STAT_ACK;
|
||||
}
|
||||
@ -1566,7 +1829,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
struct genl_info *info)
|
||||
{
|
||||
|
||||
struct mac80211_hwsim_data *data2;
|
||||
struct mac80211_hwsim_data *data2;
|
||||
struct ieee80211_rx_status rx_status;
|
||||
struct mac_address *dst;
|
||||
int frame_data_len;
|
||||
@ -1574,9 +1837,9 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
struct sk_buff *skb = NULL;
|
||||
|
||||
if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
|
||||
!info->attrs[HWSIM_ATTR_FRAME] ||
|
||||
!info->attrs[HWSIM_ATTR_RX_RATE] ||
|
||||
!info->attrs[HWSIM_ATTR_SIGNAL])
|
||||
!info->attrs[HWSIM_ATTR_FRAME] ||
|
||||
!info->attrs[HWSIM_ATTR_RX_RATE] ||
|
||||
!info->attrs[HWSIM_ATTR_SIGNAL])
|
||||
goto out;
|
||||
|
||||
dst = (struct mac_address *)nla_data(
|
||||
@ -1604,7 +1867,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
|
||||
/* check if radio is configured properly */
|
||||
|
||||
if (data2->idle || !data2->started || !data2->channel)
|
||||
if (data2->idle || !data2->started)
|
||||
goto out;
|
||||
|
||||
/*A frame is received from user space*/
|
||||
@ -1688,6 +1951,11 @@ static struct notifier_block hwsim_netlink_notifier = {
|
||||
static int hwsim_init_netlink(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* userspace test API hasn't been adjusted for multi-channel */
|
||||
if (channels > 1)
|
||||
return 0;
|
||||
|
||||
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
|
||||
|
||||
rc = genl_register_family_with_ops(&hwsim_genl_family,
|
||||
@ -1710,6 +1978,10 @@ static void hwsim_exit_netlink(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* userspace test API hasn't been adjusted for multi-channel */
|
||||
if (channels > 1)
|
||||
return;
|
||||
|
||||
printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
|
||||
/* unregister the notifier */
|
||||
netlink_unregister_notifier(&hwsim_netlink_notifier);
|
||||
@ -1732,7 +2004,7 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_combination hwsim_if_comb = {
|
||||
static struct ieee80211_iface_combination hwsim_if_comb = {
|
||||
.limits = hwsim_if_limits,
|
||||
.n_limits = ARRAY_SIZE(hwsim_if_limits),
|
||||
.max_interfaces = 2048,
|
||||
@ -1750,10 +2022,30 @@ static int __init init_mac80211_hwsim(void)
|
||||
if (radios < 1 || radios > 100)
|
||||
return -EINVAL;
|
||||
|
||||
if (fake_hw_scan) {
|
||||
if (channels < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels > 1) {
|
||||
hwsim_if_comb.num_different_channels = channels;
|
||||
mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan;
|
||||
mac80211_hwsim_ops.cancel_hw_scan =
|
||||
mac80211_hwsim_cancel_hw_scan;
|
||||
mac80211_hwsim_ops.sw_scan_start = NULL;
|
||||
mac80211_hwsim_ops.sw_scan_complete = NULL;
|
||||
mac80211_hwsim_ops.remain_on_channel =
|
||||
mac80211_hwsim_roc;
|
||||
mac80211_hwsim_ops.cancel_remain_on_channel =
|
||||
mac80211_hwsim_croc;
|
||||
mac80211_hwsim_ops.add_chanctx =
|
||||
mac80211_hwsim_add_chanctx;
|
||||
mac80211_hwsim_ops.remove_chanctx =
|
||||
mac80211_hwsim_remove_chanctx;
|
||||
mac80211_hwsim_ops.change_chanctx =
|
||||
mac80211_hwsim_change_chanctx;
|
||||
mac80211_hwsim_ops.assign_vif_chanctx =
|
||||
mac80211_hwsim_assign_vif_chanctx;
|
||||
mac80211_hwsim_ops.unassign_vif_chanctx =
|
||||
mac80211_hwsim_unassign_vif_chanctx;
|
||||
}
|
||||
|
||||
spin_lock_init(&hwsim_radio_lock);
|
||||
@ -1803,13 +2095,18 @@ static int __init init_mac80211_hwsim(void)
|
||||
hw->wiphy->iface_combinations = &hwsim_if_comb;
|
||||
hw->wiphy->n_iface_combinations = 1;
|
||||
|
||||
if (fake_hw_scan) {
|
||||
if (channels > 1) {
|
||||
hw->wiphy->max_scan_ssids = 255;
|
||||
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
||||
hw->wiphy->max_remain_on_channel_duration = 1000;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
|
||||
INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
|
||||
|
||||
hw->channel_change_time = 1;
|
||||
hw->queues = 4;
|
||||
hw->queues = 5;
|
||||
hw->offchannel_tx_hw_queue = 4;
|
||||
hw->wiphy->interface_modes =
|
||||
BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
@ -1824,7 +2121,8 @@ static int __init init_mac80211_hwsim(void)
|
||||
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
|
||||
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
|
||||
IEEE80211_HW_AMPDU_AGGREGATION |
|
||||
IEEE80211_HW_WANT_MONITOR_VIF;
|
||||
IEEE80211_HW_WANT_MONITOR_VIF |
|
||||
IEEE80211_HW_QUEUE_CONTROL;
|
||||
|
||||
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
|
||||
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
||||
|
@ -324,6 +324,7 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
||||
*/
|
||||
static int
|
||||
mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type,
|
||||
int mbm)
|
||||
{
|
||||
|
@ -490,9 +490,12 @@ static int rndis_scan(struct wiphy *wiphy,
|
||||
static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed);
|
||||
|
||||
static int rndis_set_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type,
|
||||
int mbm);
|
||||
static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm);
|
||||
static int rndis_get_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
int *dbm);
|
||||
|
||||
static int rndis_connect(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_connect_params *sme);
|
||||
@ -1903,6 +1906,7 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
}
|
||||
|
||||
static int rndis_set_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type,
|
||||
int mbm)
|
||||
{
|
||||
@ -1930,7 +1934,9 @@ static int rndis_set_tx_power(struct wiphy *wiphy,
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm)
|
||||
static int rndis_get_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
int *dbm)
|
||||
{
|
||||
struct rndis_wlan_private *priv = wiphy_priv(wiphy);
|
||||
struct usbnet *usbdev = priv->usbdev;
|
||||
|
@ -157,6 +157,7 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work)
|
||||
* requested configurations.
|
||||
*/
|
||||
ieee80211_iterate_active_interfaces(rt2x00dev->hw,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
rt2x00lib_intf_scheduled_iter,
|
||||
rt2x00dev);
|
||||
}
|
||||
@ -225,9 +226,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
|
||||
return;
|
||||
|
||||
/* send buffered bc/mc frames out for every bssid */
|
||||
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
|
||||
rt2x00lib_bc_buffer_iter,
|
||||
rt2x00dev);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
rt2x00lib_bc_buffer_iter, rt2x00dev);
|
||||
/*
|
||||
* Devices with pre tbtt interrupt don't need to update the beacon
|
||||
* here as they will fetch the next beacon directly prior to
|
||||
@ -237,9 +238,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
|
||||
return;
|
||||
|
||||
/* fetch next beacon */
|
||||
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
|
||||
rt2x00lib_beaconupdate_iter,
|
||||
rt2x00dev);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
rt2x00lib_beaconupdate_iter, rt2x00dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);
|
||||
|
||||
@ -249,9 +250,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
|
||||
return;
|
||||
|
||||
/* fetch next beacon */
|
||||
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
|
||||
rt2x00lib_beaconupdate_iter,
|
||||
rt2x00dev);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
rt2x00lib_beaconupdate_iter, rt2x00dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
|
||||
|
||||
|
@ -424,9 +424,9 @@ int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
|
||||
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
||||
return 0;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
|
||||
rt2x00mac_set_tim_iter,
|
||||
rt2x00dev);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
rt2x00mac_set_tim_iter, rt2x00dev);
|
||||
|
||||
/* queue work to upodate the beacon template */
|
||||
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work);
|
||||
|
@ -677,7 +677,7 @@ static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->cur_vif = cur_vif;
|
||||
|
||||
ieee80211_iterate_active_interfaces(hw,
|
||||
ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||
wl12xx_vif_count_iter, data);
|
||||
}
|
||||
|
||||
|
@ -905,6 +905,38 @@ struct ieee80211_tdls_data {
|
||||
} u;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Peer-to-Peer IE attribute related definitions.
|
||||
*/
|
||||
/**
|
||||
* enum ieee80211_p2p_attr_id - identifies type of peer-to-peer attribute.
|
||||
*/
|
||||
enum ieee80211_p2p_attr_id {
|
||||
IEEE80211_P2P_ATTR_STATUS = 0,
|
||||
IEEE80211_P2P_ATTR_MINOR_REASON,
|
||||
IEEE80211_P2P_ATTR_CAPABILITY,
|
||||
IEEE80211_P2P_ATTR_DEVICE_ID,
|
||||
IEEE80211_P2P_ATTR_GO_INTENT,
|
||||
IEEE80211_P2P_ATTR_GO_CONFIG_TIMEOUT,
|
||||
IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
|
||||
IEEE80211_P2P_ATTR_GROUP_BSSID,
|
||||
IEEE80211_P2P_ATTR_EXT_LISTEN_TIMING,
|
||||
IEEE80211_P2P_ATTR_INTENDED_IFACE_ADDR,
|
||||
IEEE80211_P2P_ATTR_MANAGABILITY,
|
||||
IEEE80211_P2P_ATTR_CHANNEL_LIST,
|
||||
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
||||
IEEE80211_P2P_ATTR_DEVICE_INFO,
|
||||
IEEE80211_P2P_ATTR_GROUP_INFO,
|
||||
IEEE80211_P2P_ATTR_GROUP_ID,
|
||||
IEEE80211_P2P_ATTR_INTERFACE,
|
||||
IEEE80211_P2P_ATTR_OPER_CHANNEL,
|
||||
IEEE80211_P2P_ATTR_INVITE_FLAGS,
|
||||
/* 19 - 220: Reserved */
|
||||
IEEE80211_P2P_ATTR_VENDOR_SPECIFIC = 221,
|
||||
|
||||
IEEE80211_P2P_ATTR_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_bar - HT Block Ack Request
|
||||
*
|
||||
|
@ -1545,13 +1545,19 @@ struct cfg80211_gtk_rekey_data {
|
||||
* to a merge.
|
||||
* @leave_ibss: Leave the IBSS.
|
||||
*
|
||||
* @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
|
||||
* MESH mode)
|
||||
*
|
||||
* @set_wiphy_params: Notify that wiphy parameters have changed;
|
||||
* @changed bitfield (see &enum wiphy_params_flags) describes which values
|
||||
* have changed. The actual parameter values are available in
|
||||
* struct wiphy. If returning an error, no value should be changed.
|
||||
*
|
||||
* @set_tx_power: set the transmit power according to the parameters,
|
||||
* the power passed is in mBm, to get dBm use MBM_TO_DBM().
|
||||
* the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
|
||||
* wdev may be %NULL if power was set for the wiphy, and will
|
||||
* always be %NULL unless the driver supports per-vif TX power
|
||||
* (as advertised by the nl80211 feature flag.)
|
||||
* @get_tx_power: store the current TX power into the dbm variable;
|
||||
* return 0 if successful
|
||||
*
|
||||
@ -1746,11 +1752,15 @@ struct cfg80211_ops {
|
||||
struct cfg80211_ibss_params *params);
|
||||
int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
|
||||
|
||||
int (*set_mcast_rate)(struct wiphy *wiphy, struct net_device *dev,
|
||||
int rate[IEEE80211_NUM_BANDS]);
|
||||
|
||||
int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
|
||||
|
||||
int (*set_tx_power)(struct wiphy *wiphy,
|
||||
int (*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm);
|
||||
int (*get_tx_power)(struct wiphy *wiphy, int *dbm);
|
||||
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
int *dbm);
|
||||
|
||||
int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *addr);
|
||||
@ -3550,7 +3560,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
|
||||
* @len: length of the frame
|
||||
* @freq: frequency the frame was received on
|
||||
* @sig_dbm: signal strength in mBm, or 0 if unknown
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* Use this function to report to userspace when a beacon was
|
||||
* received. It is not useful to call this when there is no
|
||||
@ -3558,7 +3567,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
|
||||
*/
|
||||
void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
||||
const u8 *frame, size_t len,
|
||||
int freq, int sig_dbm, gfp_t gfp);
|
||||
int freq, int sig_dbm);
|
||||
|
||||
/**
|
||||
* cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used
|
||||
@ -3608,6 +3617,25 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
|
||||
*/
|
||||
void cfg80211_unregister_wdev(struct wireless_dev *wdev);
|
||||
|
||||
/**
|
||||
* cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
|
||||
* @ies: the input IE buffer
|
||||
* @len: the input length
|
||||
* @attr: the attribute ID to find
|
||||
* @buf: output buffer, can be %NULL if the data isn't needed, e.g.
|
||||
* if the function is only called to get the needed buffer size
|
||||
* @bufsize: size of the output buffer
|
||||
*
|
||||
* The function finds a given P2P attribute in the (vendor) IEs and
|
||||
* copies its contents to the given buffer.
|
||||
*
|
||||
* The return value is a negative error code (-%EILSEQ or -%ENOENT) if
|
||||
* the data is malformed or the attribute can't be found (respectively),
|
||||
* or the length of the found attribute (which can be zero).
|
||||
*/
|
||||
unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
|
||||
u8 attr, u8 *buf, unsigned int bufsize);
|
||||
|
||||
/* Logging, debugging and troubleshooting/diagnostic helpers. */
|
||||
|
||||
/* wiphy_printk helpers, similar to dev_printk */
|
||||
|
@ -207,6 +207,9 @@ struct ieee80211_chanctx_conf {
|
||||
* @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode)
|
||||
* @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode)
|
||||
* @BSS_CHANGED_PS: PS changed for this BSS (STA mode)
|
||||
* @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
|
||||
* @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
|
||||
* changed (currently only in P2P client mode, GO mode will be later)
|
||||
*/
|
||||
enum ieee80211_bss_change {
|
||||
BSS_CHANGED_ASSOC = 1<<0,
|
||||
@ -227,6 +230,8 @@ enum ieee80211_bss_change {
|
||||
BSS_CHANGED_SSID = 1<<15,
|
||||
BSS_CHANGED_AP_PROBE_RESP = 1<<16,
|
||||
BSS_CHANGED_PS = 1<<17,
|
||||
BSS_CHANGED_TXPOWER = 1<<18,
|
||||
BSS_CHANGED_P2P_PS = 1<<19,
|
||||
|
||||
/* when adding here, make sure to change ieee80211_reconfig */
|
||||
};
|
||||
@ -309,6 +314,9 @@ enum ieee80211_rssi_event {
|
||||
* @ssid: The SSID of the current vif. Only valid in AP-mode.
|
||||
* @ssid_len: Length of SSID given in @ssid.
|
||||
* @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
|
||||
* @txpower: TX power in dBm
|
||||
* @p2p_ctwindow: P2P CTWindow, only for P2P client interfaces
|
||||
* @p2p_oppps: P2P opportunistic PS is enabled
|
||||
*/
|
||||
struct ieee80211_bss_conf {
|
||||
const u8 *bssid;
|
||||
@ -341,6 +349,9 @@ struct ieee80211_bss_conf {
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
size_t ssid_len;
|
||||
bool hidden_ssid;
|
||||
int txpower;
|
||||
u8 p2p_ctwindow;
|
||||
bool p2p_oppps;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -884,7 +895,8 @@ enum ieee80211_smps_mode {
|
||||
* powersave documentation below. This variable is valid only when
|
||||
* the CONF_PS flag is set.
|
||||
*
|
||||
* @power_level: requested transmit power (in dBm)
|
||||
* @power_level: requested transmit power (in dBm), backward compatibility
|
||||
* value only that is set to the minimum of all interfaces
|
||||
*
|
||||
* @channel: the channel to tune to
|
||||
* @channel_type: the channel (HT) type
|
||||
@ -2381,6 +2393,17 @@ enum ieee80211_rate_control_changed {
|
||||
* to vif. Possible use is for hw queue remapping.
|
||||
* @unassign_vif_chanctx: Notifies device driver about channel context being
|
||||
* unbound from vif.
|
||||
* @start_ap: Start operation on the AP interface, this is called after all the
|
||||
* information in bss_conf is set and beacon can be retrieved. A channel
|
||||
* context is bound before this is called. Note that if the driver uses
|
||||
* software scan or ROC, this (and @stop_ap) isn't called when the AP is
|
||||
* just "paused" for scanning/ROC, which is indicated by the beacon being
|
||||
* 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. This callback may sleep.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2406,6 +2429,9 @@ struct ieee80211_ops {
|
||||
struct ieee80211_bss_conf *info,
|
||||
u32 changed);
|
||||
|
||||
int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
|
||||
u64 (*prepare_multicast)(struct ieee80211_hw *hw,
|
||||
struct netdev_hw_addr_list *mc_list);
|
||||
void (*configure_filter)(struct ieee80211_hw *hw,
|
||||
@ -2539,6 +2565,8 @@ struct ieee80211_ops {
|
||||
void (*unassign_vif_chanctx)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *ctx);
|
||||
|
||||
void (*restart_complete)(struct ieee80211_hw *hw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3384,6 +3412,21 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw);
|
||||
*/
|
||||
void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
|
||||
|
||||
/**
|
||||
* enum ieee80211_interface_iteration_flags - interface iteration flags
|
||||
* @IEEE80211_IFACE_ITER_NORMAL: Iterate over all interfaces that have
|
||||
* been added to the driver; However, note that during hardware
|
||||
* reconfiguration (after restart_hw) it will iterate over a new
|
||||
* interface and over all the existing interfaces even if they
|
||||
* haven't been re-added to the driver yet.
|
||||
* @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all
|
||||
* interfaces, even if they haven't been re-added to the driver yet.
|
||||
*/
|
||||
enum ieee80211_interface_iteration_flags {
|
||||
IEEE80211_IFACE_ITER_NORMAL = 0,
|
||||
IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0),
|
||||
};
|
||||
|
||||
/**
|
||||
* ieee80211_iterate_active_interfaces - iterate active interfaces
|
||||
*
|
||||
@ -3392,13 +3435,15 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
|
||||
* This function allows the iterator function to sleep, when the iterator
|
||||
* function is atomic @ieee80211_iterate_active_interfaces_atomic can
|
||||
* be used.
|
||||
* Does not iterate over a new interface during add_interface()
|
||||
* Does not iterate over a new interface during add_interface().
|
||||
*
|
||||
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
|
||||
* @iterator: the iterator function to call
|
||||
* @data: first argument of the iterator function
|
||||
*/
|
||||
void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
@ -3410,13 +3455,15 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
|
||||
* hardware that are currently active and calls the callback for them.
|
||||
* This function requires the iterator callback function to be atomic,
|
||||
* if that is not desired, use @ieee80211_iterate_active_interfaces instead.
|
||||
* Does not iterate over a new interface during add_interface()
|
||||
* Does not iterate over a new interface during add_interface().
|
||||
*
|
||||
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
|
||||
* @iterator: the iterator function to call, cannot sleep
|
||||
* @data: first argument of the iterator function
|
||||
*/
|
||||
void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data,
|
||||
u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
|
@ -578,6 +578,9 @@
|
||||
* station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
|
||||
* is used for this.
|
||||
*
|
||||
* @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
|
||||
* for IBSS or MESH vif.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -726,6 +729,8 @@ enum nl80211_commands {
|
||||
|
||||
NL80211_CMD_CONN_FAILED,
|
||||
|
||||
NL80211_CMD_SET_MCAST_RATE,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
@ -3051,6 +3056,7 @@ enum nl80211_ap_sme_features {
|
||||
* @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
|
||||
* @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
|
||||
* @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
|
||||
* @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
|
||||
*/
|
||||
enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||
@ -3062,6 +3068,7 @@ enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
|
||||
NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
|
||||
NL80211_FEATURE_AP_SCAN = 1 << 8,
|
||||
NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <crypto/aes.h>
|
||||
|
||||
|
@ -922,6 +922,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
||||
return err;
|
||||
changed |= err;
|
||||
|
||||
err = drv_start_ap(sdata->local, sdata);
|
||||
if (err) {
|
||||
old = rtnl_dereference(sdata->u.ap.beacon);
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
netif_carrier_on(dev);
|
||||
@ -953,26 +962,38 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
||||
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata, *vlan;
|
||||
struct beacon_data *old;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_sub_if_data *vlan;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct beacon_data *old_beacon;
|
||||
struct probe_resp *old_probe_resp;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
old = rtnl_dereference(sdata->u.ap.beacon);
|
||||
if (!old)
|
||||
old_beacon = rtnl_dereference(sdata->u.ap.beacon);
|
||||
if (!old_beacon)
|
||||
return -ENOENT;
|
||||
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
/* turn off carrier for this interface and dependent VLANs */
|
||||
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
|
||||
netif_carrier_off(vlan->dev);
|
||||
netif_carrier_off(dev);
|
||||
|
||||
/* remove beacon and probe response */
|
||||
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
|
||||
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
|
||||
kfree_rcu(old_beacon, rcu_head);
|
||||
if (old_probe_resp)
|
||||
kfree_rcu(old_probe_resp, rcu_head);
|
||||
|
||||
kfree_rcu(old, rcu_head);
|
||||
|
||||
sta_info_flush(sdata->local, sdata);
|
||||
sta_info_flush(local, sdata);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
|
||||
|
||||
drv_stop_ap(sdata->local, sdata);
|
||||
|
||||
/* free all potentially still buffered bcast frames */
|
||||
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
|
||||
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
|
||||
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
|
||||
return 0;
|
||||
@ -1933,6 +1954,16 @@ 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_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
||||
int rate[IEEE80211_NUM_BANDS])
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
@ -1971,45 +2002,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
}
|
||||
|
||||
static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
struct ieee80211_channel *chan = local->_oper_channel;
|
||||
u32 changes = 0;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
/* FIXME */
|
||||
if (local->use_chanctx)
|
||||
return -EOPNOTSUPP;
|
||||
if (wdev) {
|
||||
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
|
||||
switch (type) {
|
||||
case NL80211_TX_POWER_AUTOMATIC:
|
||||
sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
break;
|
||||
case NL80211_TX_POWER_LIMITED:
|
||||
case NL80211_TX_POWER_FIXED:
|
||||
if (mbm < 0 || (mbm % 100))
|
||||
return -EOPNOTSUPP;
|
||||
sdata->user_power_level = MBM_TO_DBM(mbm);
|
||||
break;
|
||||
}
|
||||
|
||||
ieee80211_recalc_txpower(sdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NL80211_TX_POWER_AUTOMATIC:
|
||||
local->user_power_level = -1;
|
||||
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
break;
|
||||
case NL80211_TX_POWER_LIMITED:
|
||||
if (mbm < 0 || (mbm % 100))
|
||||
return -EOPNOTSUPP;
|
||||
local->user_power_level = MBM_TO_DBM(mbm);
|
||||
break;
|
||||
case NL80211_TX_POWER_FIXED:
|
||||
if (mbm < 0 || (mbm % 100))
|
||||
return -EOPNOTSUPP;
|
||||
/* TODO: move to cfg80211 when it knows the channel */
|
||||
if (MBM_TO_DBM(mbm) > chan->max_power)
|
||||
return -EINVAL;
|
||||
local->user_power_level = MBM_TO_DBM(mbm);
|
||||
break;
|
||||
}
|
||||
|
||||
ieee80211_hw_config(local, changes);
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
sdata->user_power_level = local->user_power_level;
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
ieee80211_recalc_txpower(sdata);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
|
||||
static int ieee80211_get_tx_power(struct wiphy *wiphy,
|
||||
struct wireless_dev *wdev,
|
||||
int *dbm)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
|
||||
*dbm = local->hw.conf.power_level;
|
||||
if (!local->use_chanctx)
|
||||
*dbm = local->hw.conf.power_level;
|
||||
else
|
||||
*dbm = sdata->vif.bss_conf.txpower;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2341,13 +2392,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
||||
list_add_tail(&roc->list, &local->roc_list);
|
||||
|
||||
/*
|
||||
* cookie is either the roc (for normal roc)
|
||||
* cookie is either the roc cookie (for normal roc)
|
||||
* or the SKB (for mgmt TX)
|
||||
*/
|
||||
if (txskb)
|
||||
if (!txskb) {
|
||||
/* local->mtx protects this */
|
||||
local->roc_cookie_counter++;
|
||||
roc->cookie = local->roc_cookie_counter;
|
||||
/* wow, you wrapped 64 bits ... more likely a bug */
|
||||
if (WARN_ON(roc->cookie == 0)) {
|
||||
roc->cookie = 1;
|
||||
local->roc_cookie_counter++;
|
||||
}
|
||||
*cookie = roc->cookie;
|
||||
} else {
|
||||
*cookie = (unsigned long)txskb;
|
||||
else
|
||||
*cookie = (unsigned long)roc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2382,7 +2442,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
|
||||
struct ieee80211_roc_work *dep, *tmp2;
|
||||
|
||||
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
|
||||
if (!mgmt_tx && (unsigned long)dep != cookie)
|
||||
if (!mgmt_tx && dep->cookie != cookie)
|
||||
continue;
|
||||
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
|
||||
continue;
|
||||
@ -2394,7 +2454,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mgmt_tx && (unsigned long)roc != cookie)
|
||||
if (!mgmt_tx && roc->cookie != cookie)
|
||||
continue;
|
||||
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
|
||||
continue;
|
||||
@ -3130,6 +3190,7 @@ struct cfg80211_ops mac80211_config_ops = {
|
||||
.disassoc = ieee80211_disassoc,
|
||||
.join_ibss = ieee80211_join_ibss,
|
||||
.leave_ibss = ieee80211_leave_ibss,
|
||||
.set_mcast_rate = ieee80211_set_mcast_rate,
|
||||
.set_wiphy_params = ieee80211_set_wiphy_params,
|
||||
.set_tx_power = ieee80211_set_tx_power,
|
||||
.get_tx_power = ieee80211_get_tx_power,
|
||||
|
@ -173,6 +173,8 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
|
||||
rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
|
||||
ctx->refcount++;
|
||||
|
||||
ieee80211_recalc_txpower(sdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
@ -168,6 +169,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
|
||||
IEEE80211_IF_FILE(flags, flags, HEX);
|
||||
IEEE80211_IF_FILE(state, state, LHEX);
|
||||
IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
|
||||
IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
|
||||
IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC);
|
||||
IEEE80211_IF_FILE(user_power_level, user_power_level, DEC);
|
||||
|
||||
static ssize_t
|
||||
ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
|
||||
char *buf, int buflen)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
|
||||
sdata->vif.hw_queue[IEEE80211_AC_VO],
|
||||
sdata->vif.hw_queue[IEEE80211_AC_VI],
|
||||
sdata->vif.hw_queue[IEEE80211_AC_BE],
|
||||
sdata->vif.hw_queue[IEEE80211_AC_BK]);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
|
||||
sdata->vif.cab_queue);
|
||||
|
||||
return len;
|
||||
}
|
||||
__IEEE80211_IF_FILE(hw_queues, NULL);
|
||||
|
||||
/* STA attributes */
|
||||
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
|
||||
@ -245,27 +269,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test(
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int hwaddr_aton(const char *txt, u8 *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ETH_ALEN; i++) {
|
||||
int a, b;
|
||||
|
||||
a = hex_to_bin(*txt++);
|
||||
if (a < 0)
|
||||
return -1;
|
||||
b = hex_to_bin(*txt++);
|
||||
if (b < 0)
|
||||
return -1;
|
||||
*addr++ = (a << 4) | b;
|
||||
if (i < 5 && *txt++ != ':')
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ieee80211_if_parse_tkip_mic_test(
|
||||
struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
|
||||
{
|
||||
@ -275,13 +278,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
|
||||
struct ieee80211_hdr *hdr;
|
||||
__le16 fc;
|
||||
|
||||
/*
|
||||
* Assume colon-delimited MAC address with possible white space
|
||||
* following.
|
||||
*/
|
||||
if (buflen < 3 * ETH_ALEN - 1)
|
||||
return -EINVAL;
|
||||
if (hwaddr_aton(buf, addr) < 0)
|
||||
if (!mac_pton(buf, addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
@ -307,13 +304,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
|
||||
case NL80211_IFTYPE_STATION:
|
||||
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
|
||||
/* BSSID SA DA */
|
||||
if (sdata->vif.bss_conf.bssid == NULL) {
|
||||
mutex_lock(&sdata->u.mgd.mtx);
|
||||
if (!sdata->u.mgd.associated) {
|
||||
mutex_unlock(&sdata->u.mgd.mtx);
|
||||
dev_kfree_skb(skb);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN);
|
||||
memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
|
||||
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(hdr->addr3, addr, ETH_ALEN);
|
||||
mutex_unlock(&sdata->u.mgd.mtx);
|
||||
break;
|
||||
default:
|
||||
dev_kfree_skb(skb);
|
||||
@ -443,7 +443,7 @@ static ssize_t ieee80211_if_parse_tsf(
|
||||
}
|
||||
ret = kstrtoull(buf, 10, &tsf);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
if (tsf_is_delta)
|
||||
tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
|
||||
if (local->ops->set_tsf) {
|
||||
@ -531,6 +531,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
|
||||
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
|
||||
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
|
||||
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
|
||||
DEBUGFS_ADD(hw_queues);
|
||||
}
|
||||
|
||||
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
|
||||
@ -632,6 +633,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
|
||||
DEBUGFS_ADD(flags);
|
||||
DEBUGFS_ADD(state);
|
||||
DEBUGFS_ADD(channel_type);
|
||||
DEBUGFS_ADD(txpower);
|
||||
DEBUGFS_ADD(user_power_level);
|
||||
DEBUGFS_ADD(ap_power_level);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
|
||||
add_common_files(sdata);
|
||||
|
@ -936,4 +936,39 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline int drv_start_ap(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
|
||||
if (local->ops->start_ap)
|
||||
ret = local->ops->start_ap(&local->hw, &sdata->vif);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_stop_ap(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_stop_ap(local, sdata);
|
||||
if (local->ops->stop_ap)
|
||||
local->ops->stop_ap(&local->hw, &sdata->vif);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void drv_restart_complete(struct ieee80211_local *local)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_drv_restart_complete(local);
|
||||
if (local->ops->restart_complete)
|
||||
local->ops->restart_complete(&local->hw);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -56,6 +56,9 @@ struct ieee80211_local;
|
||||
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
|
||||
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
|
||||
|
||||
/* power level hasn't been configured (or set to automatic) */
|
||||
#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
|
||||
|
||||
/*
|
||||
* Some APs experience problems when working with U-APSD. Decrease the
|
||||
* probability of that happening by using legacy mode for all ACs but VO.
|
||||
@ -353,7 +356,7 @@ struct ieee80211_roc_work {
|
||||
|
||||
u32 duration, req_duration;
|
||||
struct sk_buff *frame;
|
||||
u64 mgmt_tx_cookie;
|
||||
u64 cookie, mgmt_tx_cookie;
|
||||
};
|
||||
|
||||
/* flags used in struct ieee80211_if_managed.flags */
|
||||
@ -470,6 +473,8 @@ struct ieee80211_if_managed {
|
||||
|
||||
u8 use_4addr;
|
||||
|
||||
u8 p2p_noa_index;
|
||||
|
||||
/* Signal strength from the last Beacon frame in the current BSS. */
|
||||
int last_beacon_signal;
|
||||
|
||||
@ -743,6 +748,9 @@ struct ieee80211_sub_if_data {
|
||||
u8 needed_rx_chains;
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
int user_power_level; /* in dBm */
|
||||
int ap_power_level; /* in dBm */
|
||||
|
||||
/*
|
||||
* AP this belongs to: self in AP mode and
|
||||
* corresponding AP in VLAN mode, NULL for
|
||||
@ -1117,8 +1125,7 @@ struct ieee80211_local {
|
||||
int dynamic_ps_user_timeout;
|
||||
bool disable_dynamic_ps;
|
||||
|
||||
int user_power_level; /* in dBm */
|
||||
int ap_power_level; /* in dBm */
|
||||
int user_power_level; /* in dBm, for all interfaces */
|
||||
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
@ -1137,6 +1144,7 @@ struct ieee80211_local {
|
||||
struct list_head roc_list;
|
||||
struct work_struct hw_roc_start, hw_roc_done;
|
||||
unsigned long hw_roc_start_time;
|
||||
u64 roc_cookie_counter;
|
||||
|
||||
struct idr ack_status_frames;
|
||||
spinlock_t ack_status_lock;
|
||||
@ -1365,6 +1373,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
|
||||
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
@ -42,6 +42,41 @@
|
||||
* by either the RTNL, the iflist_mtx or RCU.
|
||||
*/
|
||||
|
||||
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
int power;
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf) {
|
||||
rcu_read_unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
power = chanctx_conf->channel->max_power;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
|
||||
power = min(power, sdata->user_power_level);
|
||||
|
||||
if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
|
||||
power = min(power, sdata->ap_power_level);
|
||||
|
||||
if (power != sdata->vif.bss_conf.txpower) {
|
||||
sdata->vif.bss_conf.txpower = power;
|
||||
ieee80211_hw_config(sdata->local, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
if (__ieee80211_recalc_txpower(sdata))
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
|
||||
}
|
||||
|
||||
static u32 ieee80211_idle_off(struct ieee80211_local *local,
|
||||
const char *reason)
|
||||
@ -744,31 +779,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
/* APs need special treatment */
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||||
struct ieee80211_sub_if_data *vlan, *tmpsdata;
|
||||
struct beacon_data *old_beacon =
|
||||
rtnl_dereference(sdata->u.ap.beacon);
|
||||
struct probe_resp *old_probe_resp =
|
||||
rtnl_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
/* sdata_running will return false, so this will disable */
|
||||
ieee80211_bss_info_change_notify(sdata,
|
||||
BSS_CHANGED_BEACON_ENABLED);
|
||||
|
||||
/* remove beacon and probe response */
|
||||
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
|
||||
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
|
||||
synchronize_rcu();
|
||||
kfree(old_beacon);
|
||||
kfree(old_probe_resp);
|
||||
|
||||
/* down all dependent devices, that is VLANs */
|
||||
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
|
||||
u.vlan.list)
|
||||
dev_close(vlan->dev);
|
||||
WARN_ON(!list_empty(&sdata->u.ap.vlans));
|
||||
|
||||
/* free all potentially still buffered bcast frames */
|
||||
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
|
||||
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
ieee80211_mgd_stop(sdata);
|
||||
}
|
||||
@ -1529,6 +1545,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
|
||||
ieee80211_set_default_queues(sdata);
|
||||
|
||||
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
sdata->user_power_level = local->user_power_level;
|
||||
|
||||
/* setup type-dependent data */
|
||||
ieee80211_setup_sdata(sdata, type);
|
||||
|
||||
|
@ -95,11 +95,13 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
|
||||
|
||||
static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_channel *chan;
|
||||
u32 changed = 0;
|
||||
int power;
|
||||
enum nl80211_channel_type channel_type;
|
||||
u32 offchannel_flag;
|
||||
bool scanning = false;
|
||||
|
||||
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
|
||||
if (local->scan_channel) {
|
||||
@ -146,16 +148,18 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
|
||||
changed |= IEEE80211_CONF_CHANGE_SMPS;
|
||||
}
|
||||
|
||||
if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_HW_SCANNING, &local->scanning) ||
|
||||
!local->ap_power_level)
|
||||
power = chan->max_power;
|
||||
else
|
||||
power = min(chan->max_power, local->ap_power_level);
|
||||
scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
|
||||
test_bit(SCAN_HW_SCANNING, &local->scanning);
|
||||
power = chan->max_power;
|
||||
|
||||
if (local->user_power_level >= 0)
|
||||
power = min(power, local->user_power_level);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (!rcu_access_pointer(sdata->vif.chanctx_conf))
|
||||
continue;
|
||||
power = min(power, sdata->vif.bss_conf.txpower);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (local->hw.conf.power_level != power) {
|
||||
changed |= IEEE80211_CONF_CHANGE_POWER;
|
||||
@ -600,7 +604,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
|
||||
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
|
||||
NL80211_FEATURE_SAE |
|
||||
NL80211_FEATURE_HT_IBSS;
|
||||
NL80211_FEATURE_HT_IBSS |
|
||||
NL80211_FEATURE_VIF_TXPOWER;
|
||||
|
||||
if (!ops->hw_scan)
|
||||
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
|
||||
@ -633,7 +638,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_GI |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_BW;
|
||||
local->user_power_level = -1;
|
||||
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
|
||||
|
||||
INIT_LIST_HEAD(&local->interfaces);
|
||||
|
@ -820,10 +820,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
cbss->beacon_interval));
|
||||
}
|
||||
|
||||
static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel *channel,
|
||||
const u8 *country_ie, u8 country_ie_len,
|
||||
const u8 *pwr_constr_elem)
|
||||
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel *channel,
|
||||
const u8 *country_ie, u8 country_ie_len,
|
||||
const u8 *pwr_constr_elem)
|
||||
{
|
||||
struct ieee80211_country_ie_triplet *triplet;
|
||||
int chan = ieee80211_frequency_to_channel(channel->center_freq);
|
||||
@ -832,7 +832,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* Invalid IE */
|
||||
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
triplet = (void *)(country_ie + 3);
|
||||
country_ie_len -= 3;
|
||||
@ -873,19 +873,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
if (!have_chan_pwr)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
|
||||
|
||||
if (sdata->local->ap_power_level == new_ap_level)
|
||||
return;
|
||||
if (sdata->ap_power_level == new_ap_level)
|
||||
return 0;
|
||||
|
||||
sdata_info(sdata,
|
||||
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
|
||||
new_ap_level, chan_pwr, *pwr_constr_elem,
|
||||
sdata->u.mgd.bssid);
|
||||
sdata->local->ap_power_level = new_ap_level;
|
||||
ieee80211_hw_config(sdata->local, 0);
|
||||
sdata->ap_power_level = new_ap_level;
|
||||
if (__ieee80211_recalc_txpower(sdata))
|
||||
return BSS_CHANGED_TXPOWER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
|
||||
@ -1363,6 +1365,22 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
|
||||
|
||||
if (sdata->vif.p2p) {
|
||||
u8 noa[2];
|
||||
int ret;
|
||||
|
||||
ret = cfg80211_get_p2p_attr(cbss->information_elements,
|
||||
cbss->len_information_elements,
|
||||
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
||||
noa, sizeof(noa));
|
||||
if (ret >= 2) {
|
||||
bss_conf->p2p_oppps = noa[1] & 0x80;
|
||||
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
|
||||
bss_info_changed |= BSS_CHANGED_P2P_PS;
|
||||
sdata->u.mgd.p2p_noa_index = noa[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* just to be sure */
|
||||
ieee80211_stop_poll(sdata);
|
||||
|
||||
@ -1485,11 +1503,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
changed |= BSS_CHANGED_ASSOC;
|
||||
sdata->vif.bss_conf.assoc = false;
|
||||
|
||||
sdata->vif.bss_conf.p2p_ctwindow = 0;
|
||||
sdata->vif.bss_conf.p2p_oppps = false;
|
||||
|
||||
/* on the next assoc, re-program HT parameters */
|
||||
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
|
||||
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
||||
|
||||
local->ap_power_level = 0;
|
||||
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
|
||||
del_timer_sync(&local->dynamic_ps_timer);
|
||||
cancel_work_sync(&local->dynamic_ps_enable_work);
|
||||
@ -2592,6 +2613,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (sdata->vif.p2p) {
|
||||
u8 noa[2];
|
||||
int ret;
|
||||
|
||||
ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
|
||||
len - baselen,
|
||||
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
||||
noa, sizeof(noa));
|
||||
if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
|
||||
bss_conf->p2p_oppps = noa[1] & 0x80;
|
||||
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
|
||||
changed |= BSS_CHANGED_P2P_PS;
|
||||
sdata->u.mgd.p2p_noa_index = noa[0];
|
||||
/*
|
||||
* make sure we update all information, the CRC
|
||||
* mechanism doesn't look at P2P attributes.
|
||||
*/
|
||||
ifmgd->beacon_crc_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
||||
return;
|
||||
ifmgd->beacon_crc = ncrc;
|
||||
@ -2623,10 +2665,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
if (elems.country_elem && elems.pwr_constr_elem &&
|
||||
mgmt->u.probe_resp.capab_info &
|
||||
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
|
||||
ieee80211_handle_pwr_constr(sdata, chan,
|
||||
elems.country_elem,
|
||||
elems.country_elem_len,
|
||||
elems.pwr_constr_elem);
|
||||
changed |= ieee80211_handle_pwr_constr(sdata, chan,
|
||||
elems.country_elem,
|
||||
elems.country_elem_len,
|
||||
elems.pwr_constr_elem);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
}
|
||||
@ -3660,40 +3702,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
bool tx = !req->local_state_change;
|
||||
bool sent_frame = false;
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
|
||||
if (ifmgd->auth_data) {
|
||||
ieee80211_destroy_auth_data(sdata, false);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdata_info(sdata,
|
||||
"deauthenticating from %pM by local choice (reason=%d)\n",
|
||||
req->bssid, req->reason_code);
|
||||
|
||||
if (ifmgd->associated &&
|
||||
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
req->reason_code, tx, frame_buf);
|
||||
} else {
|
||||
if (ifmgd->auth_data) {
|
||||
drv_mgd_prepare_tx(sdata->local, sdata);
|
||||
ieee80211_send_deauth_disassoc(sdata, req->bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
req->reason_code, tx,
|
||||
frame_buf);
|
||||
ieee80211_destroy_auth_data(sdata, false);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
sent_frame = tx;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ifmgd->associated &&
|
||||
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
req->reason_code, tx, frame_buf);
|
||||
sent_frame = tx;
|
||||
}
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
__cfg80211_send_deauth(sdata->dev, frame_buf,
|
||||
IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
out:
|
||||
mutex_lock(&sdata->local->mtx);
|
||||
ieee80211_recalc_idle(sdata->local);
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
|
||||
if (sent_frame)
|
||||
__cfg80211_send_deauth(sdata->dev, frame_buf,
|
||||
IEEE80211_DEAUTH_FRAME_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,7 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
|
||||
roc->frame = NULL;
|
||||
}
|
||||
} else {
|
||||
cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc,
|
||||
cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
|
||||
roc->chan, roc->chan_type,
|
||||
roc->req_duration, GFP_KERNEL);
|
||||
}
|
||||
@ -320,9 +320,8 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
|
||||
|
||||
if (!roc->mgmt_tx_cookie)
|
||||
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
|
||||
(unsigned long)roc,
|
||||
roc->chan, roc->chan_type,
|
||||
GFP_KERNEL);
|
||||
roc->cookie, roc->chan,
|
||||
roc->chan_type, GFP_KERNEL);
|
||||
|
||||
list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
|
||||
ieee80211_roc_notify_destroy(dep);
|
||||
|
@ -135,6 +135,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
||||
ieee80211_bss_info_change_notify(sdata,
|
||||
BSS_CHANGED_BEACON_ENABLED);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
||||
rcu_access_pointer(sdata->u.ap.beacon))
|
||||
drv_stop_ap(local, sdata);
|
||||
|
||||
/* the interface is leaving the channel and is removed */
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
drv_remove_interface(local, sdata);
|
||||
|
@ -54,8 +54,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline int should_drop_frame(struct sk_buff *skb,
|
||||
int present_fcs_len)
|
||||
static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
|
||||
{
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
@ -130,15 +129,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
||||
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
|
||||
rthdr->it_len = cpu_to_le16(rtap_len);
|
||||
|
||||
pos = (unsigned char *)(rthdr+1);
|
||||
pos = (unsigned char *)(rthdr + 1);
|
||||
|
||||
/* the order of the following fields is important */
|
||||
|
||||
/* IEEE80211_RADIOTAP_TSFT */
|
||||
if (status->flag & RX_FLAG_MACTIME_MPDU) {
|
||||
put_unaligned_le64(status->mactime, pos);
|
||||
rthdr->it_present |=
|
||||
cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
@ -374,7 +372,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
return origskb;
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
@ -481,8 +478,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
|
||||
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
|
||||
struct ieee80211_mmie *mmie;
|
||||
|
||||
if (skb->len < 24 + sizeof(*mmie) ||
|
||||
!is_multicast_ether_addr(hdr->da))
|
||||
if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
|
||||
return -1;
|
||||
|
||||
if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
|
||||
@ -497,9 +493,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
|
||||
return le16_to_cpu(mmie->key_id);
|
||||
}
|
||||
|
||||
|
||||
static ieee80211_rx_result
|
||||
ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
|
||||
static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
char *dev_addr = rx->sdata->vif.addr;
|
||||
@ -507,7 +501,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
|
||||
if (ieee80211_is_data(hdr->frame_control)) {
|
||||
if (is_multicast_ether_addr(hdr->addr1)) {
|
||||
if (ieee80211_has_tods(hdr->frame_control) ||
|
||||
!ieee80211_has_fromds(hdr->frame_control))
|
||||
!ieee80211_has_fromds(hdr->frame_control))
|
||||
return RX_DROP_MONITOR;
|
||||
if (ether_addr_equal(hdr->addr3, dev_addr))
|
||||
return RX_DROP_MONITOR;
|
||||
@ -539,7 +533,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
|
||||
mgmt = (struct ieee80211_mgmt *)hdr;
|
||||
category = mgmt->u.action.category;
|
||||
if (category != WLAN_CATEGORY_MESH_ACTION &&
|
||||
category != WLAN_CATEGORY_SELF_PROTECTED)
|
||||
category != WLAN_CATEGORY_SELF_PROTECTED)
|
||||
return RX_DROP_MONITOR;
|
||||
return RX_CONTINUE;
|
||||
}
|
||||
@ -551,7 +545,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
|
||||
return RX_CONTINUE;
|
||||
|
||||
return RX_DROP_MONITOR;
|
||||
|
||||
}
|
||||
|
||||
return RX_CONTINUE;
|
||||
@ -575,7 +568,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
|
||||
return (sq1 - sq2) & SEQ_MASK;
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct tid_ampdu_rx *tid_agg_rx,
|
||||
int index)
|
||||
@ -1585,18 +1577,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
||||
return RX_CONTINUE;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
|
||||
static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
if (unlikely(!rx->sta ||
|
||||
!test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
|
||||
if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
|
||||
static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
|
||||
{
|
||||
struct sk_buff *skb = rx->skb;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
@ -1618,8 +1607,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
|
||||
static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
|
||||
@ -2003,7 +1991,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
|
||||
} else {
|
||||
/* unable to resolve next hop */
|
||||
mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
|
||||
0, reason, fwd_hdr->addr2, sdata);
|
||||
0, reason, fwd_hdr->addr2, sdata);
|
||||
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
|
||||
kfree_skb(fwd_skb);
|
||||
return RX_DROP_MONITOR;
|
||||
@ -2212,7 +2200,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
|
||||
|
||||
cfg80211_report_obss_beacon(rx->local->hw.wiphy,
|
||||
rx->skb->data, rx->skb->len,
|
||||
status->freq, sig, GFP_ATOMIC);
|
||||
status->freq, sig);
|
||||
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
|
||||
}
|
||||
|
||||
@ -2412,7 +2400,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
if (!ieee80211_vif_is_mesh(&sdata->vif))
|
||||
break;
|
||||
if (mesh_action_is_path_sel(mgmt) &&
|
||||
(!mesh_path_sel_is_hwmp(sdata)))
|
||||
!mesh_path_sel_is_hwmp(sdata))
|
||||
break;
|
||||
goto queue;
|
||||
}
|
||||
@ -2468,7 +2456,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
|
||||
return RX_QUEUED;
|
||||
}
|
||||
|
||||
|
||||
return RX_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -325,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
|
||||
|
||||
}
|
||||
|
||||
static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
struct sk_buff *skb, bool dropped)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
bool acked = info->flags & IEEE80211_TX_STAT_ACK;
|
||||
|
||||
if (dropped)
|
||||
acked = false;
|
||||
|
||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||
struct ieee80211_sub_if_data *sdata = NULL;
|
||||
struct ieee80211_sub_if_data *iter_sdata;
|
||||
u64 cookie = (unsigned long)skb;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (skb->dev) {
|
||||
list_for_each_entry_rcu(iter_sdata, &local->interfaces,
|
||||
list) {
|
||||
if (!iter_sdata->dev)
|
||||
continue;
|
||||
|
||||
if (skb->dev == iter_sdata->dev) {
|
||||
sdata = iter_sdata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sdata = rcu_dereference(local->p2p_sdata);
|
||||
}
|
||||
|
||||
if (!sdata)
|
||||
skb->dev = NULL;
|
||||
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||
cookie, acked, GFP_ATOMIC);
|
||||
} else {
|
||||
cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
|
||||
skb->len, acked, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (unlikely(info->ack_frame_id)) {
|
||||
struct sk_buff *ack_skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->ack_status_lock, flags);
|
||||
ack_skb = idr_find(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
if (ack_skb)
|
||||
idr_remove(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
spin_unlock_irqrestore(&local->ack_status_lock, flags);
|
||||
|
||||
if (ack_skb) {
|
||||
if (!dropped) {
|
||||
/* consumes ack_skb */
|
||||
skb_complete_wifi_ack(ack_skb, acked);
|
||||
} else {
|
||||
dev_kfree_skb_any(ack_skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a static threshold for now, best value to be determined
|
||||
* by testing ...
|
||||
@ -516,62 +585,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
msecs_to_jiffies(10));
|
||||
}
|
||||
|
||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||
u64 cookie = (unsigned long)skb;
|
||||
bool found = false;
|
||||
|
||||
acked = info->flags & IEEE80211_TX_STAT_ACK;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (!sdata->dev)
|
||||
continue;
|
||||
|
||||
if (skb->dev != sdata->dev)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skb->dev) {
|
||||
sdata = rcu_dereference(local->p2p_sdata);
|
||||
if (sdata)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
skb->dev = NULL;
|
||||
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||
cookie, acked, GFP_ATOMIC);
|
||||
} else {
|
||||
cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
|
||||
skb->len, acked, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (unlikely(info->ack_frame_id)) {
|
||||
struct sk_buff *ack_skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->ack_status_lock, flags);
|
||||
ack_skb = idr_find(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
if (ack_skb)
|
||||
idr_remove(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
spin_unlock_irqrestore(&local->ack_status_lock, flags);
|
||||
|
||||
/* consumes ack_skb */
|
||||
if (ack_skb)
|
||||
skb_complete_wifi_ack(ack_skb,
|
||||
info->flags & IEEE80211_TX_STAT_ACK);
|
||||
}
|
||||
ieee80211_report_used_skb(local, skb, false);
|
||||
|
||||
/* this was a transmitted frame, but now we want to reuse it */
|
||||
skb_orphan(skb);
|
||||
@ -647,25 +661,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
|
||||
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (unlikely(info->ack_frame_id)) {
|
||||
struct sk_buff *ack_skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->ack_status_lock, flags);
|
||||
ack_skb = idr_find(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
if (ack_skb)
|
||||
idr_remove(&local->ack_status_frames,
|
||||
info->ack_frame_id);
|
||||
spin_unlock_irqrestore(&local->ack_status_lock, flags);
|
||||
|
||||
/* consumes ack_skb */
|
||||
if (ack_skb)
|
||||
dev_kfree_skb_any(ack_skb);
|
||||
}
|
||||
|
||||
ieee80211_report_used_skb(local, skb, true);
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_free_txskb);
|
||||
|
@ -342,6 +342,9 @@ TRACE_EVENT(drv_bss_info_changed,
|
||||
__field(bool, ps);
|
||||
__dynamic_array(u8, ssid, info->ssid_len);
|
||||
__field(bool, hidden_ssid);
|
||||
__field(int, txpower)
|
||||
__field(u8, p2p_ctwindow)
|
||||
__field(bool, p2p_oppps)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@ -376,6 +379,9 @@ TRACE_EVENT(drv_bss_info_changed,
|
||||
__entry->ps = info->ps;
|
||||
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
|
||||
__entry->hidden_ssid = info->hidden_ssid;
|
||||
__entry->txpower = info->txpower;
|
||||
__entry->p2p_ctwindow = info->p2p_ctwindow;
|
||||
__entry->p2p_oppps = info->p2p_oppps;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
@ -1043,34 +1049,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
|
||||
TP_ARGS(local)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_offchannel_tx,
|
||||
TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_channel_type channel_type,
|
||||
unsigned int wait),
|
||||
|
||||
TP_ARGS(local, skb, chan, channel_type, wait),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(int, center_freq)
|
||||
__field(int, channel_type)
|
||||
__field(unsigned int, wait)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->center_freq = chan->center_freq;
|
||||
__entry->channel_type = channel_type;
|
||||
__entry->wait = wait;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
|
||||
LOCAL_PR_ARG, __entry->center_freq, __entry->wait
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_set_ringparam,
|
||||
TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
|
||||
|
||||
@ -1396,6 +1374,48 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
|
||||
TP_ARGS(local, sdata, ctx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_start_ap,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_bss_conf *info),
|
||||
|
||||
TP_ARGS(local, sdata, info),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
__field(u8, dtimper)
|
||||
__field(u16, bcnint)
|
||||
__dynamic_array(u8, ssid, info->ssid_len);
|
||||
__field(bool, hidden_ssid);
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
__entry->dtimper = info->dtim_period;
|
||||
__entry->bcnint = info->beacon_int;
|
||||
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
|
||||
__entry->hidden_ssid = info->hidden_ssid;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, drv_restart_complete,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
TP_ARGS(local)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracing for API calls that drivers call.
|
||||
*/
|
||||
|
@ -2089,6 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
head_need = max_t(int, 0, head_need);
|
||||
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
|
||||
ieee80211_free_txskb(&local->hw, skb);
|
||||
skb = NULL;
|
||||
goto fail_rcu;
|
||||
}
|
||||
}
|
||||
|
@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
|
||||
EXPORT_SYMBOL(ieee80211_wake_queues);
|
||||
|
||||
void ieee80211_iterate_active_interfaces(
|
||||
struct ieee80211_hw *hw,
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
||||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
continue;
|
||||
if (ieee80211_sdata_running(sdata))
|
||||
iterator(data, sdata->vif.addr,
|
||||
&sdata->vif);
|
||||
@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
|
||||
|
||||
sdata = rcu_dereference_protected(local->monitor_sdata,
|
||||
lockdep_is_held(&local->iflist_mtx));
|
||||
if (sdata)
|
||||
if (sdata &&
|
||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_atomic(
|
||||
struct ieee80211_hw *hw,
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
||||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
continue;
|
||||
if (ieee80211_sdata_running(sdata))
|
||||
iterator(data, sdata->vif.addr,
|
||||
&sdata->vif);
|
||||
}
|
||||
|
||||
sdata = rcu_dereference(local->monitor_sdata);
|
||||
if (sdata)
|
||||
if (sdata &&
|
||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||
|
||||
rcu_read_unlock();
|
||||
@ -1412,6 +1422,23 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
WARN_ON(drv_add_chanctx(local, ctx));
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
struct ieee80211_chanctx_conf *ctx_conf;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
continue;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (ctx_conf) {
|
||||
ctx = container_of(ctx_conf, struct ieee80211_chanctx,
|
||||
conf);
|
||||
drv_assign_vif_chanctx(local, sdata, ctx);
|
||||
}
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
}
|
||||
|
||||
/* add STAs back */
|
||||
mutex_lock(&local->sta_mtx);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
@ -1452,22 +1479,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
|
||||
/* Finally also reconfigure all the BSS information */
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
struct ieee80211_chanctx_conf *ctx_conf;
|
||||
u32 changed;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
continue;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (ctx_conf) {
|
||||
ctx = container_of(ctx_conf, struct ieee80211_chanctx,
|
||||
conf);
|
||||
drv_assign_vif_chanctx(local, sdata, ctx);
|
||||
}
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
/* common change flags for all interface types */
|
||||
changed = BSS_CHANGED_ERP_CTS_PROT |
|
||||
BSS_CHANGED_ERP_PREAMBLE |
|
||||
@ -1478,7 +1494,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
BSS_CHANGED_BSSID |
|
||||
BSS_CHANGED_CQM |
|
||||
BSS_CHANGED_QOS |
|
||||
BSS_CHANGED_IDLE;
|
||||
BSS_CHANGED_IDLE |
|
||||
BSS_CHANGED_TXPOWER;
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
@ -1495,9 +1512,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
case NL80211_IFTYPE_AP:
|
||||
changed |= BSS_CHANGED_SSID;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||||
changed |= BSS_CHANGED_AP_PROBE_RESP;
|
||||
|
||||
if (rcu_access_pointer(sdata->u.ap.beacon))
|
||||
drv_start_ap(local, sdata);
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
changed |= BSS_CHANGED_BEACON |
|
||||
@ -1594,8 +1615,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
* If this is for hw restart things are still running.
|
||||
* We may want to change that later, however.
|
||||
*/
|
||||
if (!local->suspended)
|
||||
if (!local->suspended) {
|
||||
drv_restart_complete(local);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* first set suspended false, then resuming */
|
||||
|
@ -28,6 +28,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
if (!err) {
|
||||
wdev->beacon_interval = 0;
|
||||
wdev->channel = NULL;
|
||||
wdev->ssid_len = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -326,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
mutex_init(&rdev->devlist_mtx);
|
||||
mutex_init(&rdev->sched_scan_mtx);
|
||||
INIT_LIST_HEAD(&rdev->wdev_list);
|
||||
INIT_LIST_HEAD(&rdev->beacon_registrations);
|
||||
spin_lock_init(&rdev->beacon_registrations_lock);
|
||||
spin_lock_init(&rdev->bss_lock);
|
||||
INIT_LIST_HEAD(&rdev->bss_list);
|
||||
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
|
||||
@ -698,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister);
|
||||
void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
struct cfg80211_internal_bss *scan, *tmp;
|
||||
struct cfg80211_beacon_registration *reg, *treg;
|
||||
rfkill_destroy(rdev->rfkill);
|
||||
mutex_destroy(&rdev->mtx);
|
||||
mutex_destroy(&rdev->devlist_mtx);
|
||||
mutex_destroy(&rdev->sched_scan_mtx);
|
||||
list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
}
|
||||
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
|
||||
cfg80211_put_bss(&scan->pub);
|
||||
kfree(rdev);
|
||||
|
@ -55,7 +55,8 @@ struct cfg80211_registered_device {
|
||||
int opencount; /* also protected by devlist_mtx */
|
||||
wait_queue_head_t dev_wait;
|
||||
|
||||
u32 ap_beacons_nlportid;
|
||||
struct list_head beacon_registrations;
|
||||
spinlock_t beacon_registrations_lock;
|
||||
|
||||
/* protected by RTNL only */
|
||||
int num_running_ifaces;
|
||||
@ -260,6 +261,10 @@ enum cfg80211_chan_mode {
|
||||
CHAN_MODE_EXCLUSIVE,
|
||||
};
|
||||
|
||||
struct cfg80211_beacon_registration {
|
||||
struct list_head list;
|
||||
u32 nlportid;
|
||||
};
|
||||
|
||||
/* free object */
|
||||
extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
|
||||
|
@ -1110,6 +1110,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
||||
goto nla_put_failure;
|
||||
}
|
||||
CMD(start_p2p_device, START_P2P_DEVICE);
|
||||
CMD(set_mcast_rate, SET_MCAST_RATE);
|
||||
|
||||
#ifdef CONFIG_NL80211_TESTMODE
|
||||
CMD(testmode_cmd, TESTMODE);
|
||||
@ -1516,10 +1517,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
result = 0;
|
||||
|
||||
mutex_lock(&rdev->mtx);
|
||||
} else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
|
||||
} else
|
||||
wdev = netdev->ieee80211_ptr;
|
||||
else
|
||||
wdev = NULL;
|
||||
|
||||
/*
|
||||
* end workaround code, by now the rdev is available
|
||||
@ -1579,15 +1578,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
||||
result = __nl80211_set_channel(rdev, wdev, info);
|
||||
result = __nl80211_set_channel(rdev,
|
||||
nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
|
||||
info);
|
||||
if (result)
|
||||
goto bad_res;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
|
||||
struct wireless_dev *txp_wdev = wdev;
|
||||
enum nl80211_tx_power_setting type;
|
||||
int idx, mbm = 0;
|
||||
|
||||
if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
|
||||
txp_wdev = NULL;
|
||||
|
||||
if (!rdev->ops->set_tx_power) {
|
||||
result = -EOPNOTSUPP;
|
||||
goto bad_res;
|
||||
@ -1607,7 +1612,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
mbm = nla_get_u32(info->attrs[idx]);
|
||||
}
|
||||
|
||||
result = rdev_set_tx_power(rdev, type, mbm);
|
||||
result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
|
||||
if (result)
|
||||
goto bad_res;
|
||||
}
|
||||
@ -1782,6 +1787,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
if (wdev->ssid_len) {
|
||||
if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
return genlmsg_end(msg, hdr);
|
||||
|
||||
nla_put_failure:
|
||||
@ -2644,6 +2654,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
wdev->preset_chantype = params.channel_type;
|
||||
wdev->beacon_interval = params.beacon_interval;
|
||||
wdev->channel = params.channel;
|
||||
wdev->ssid_len = params.ssid_len;
|
||||
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@ -5444,6 +5456,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
|
||||
return cfg80211_leave_ibss(rdev, dev, false);
|
||||
}
|
||||
|
||||
static int nl80211_set_mcast_rate(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];
|
||||
int mcast_rate[IEEE80211_NUM_BANDS];
|
||||
u32 nla_rate;
|
||||
int err;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rdev->ops->set_mcast_rate)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(mcast_rate, 0, sizeof(mcast_rate));
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MCAST_RATE])
|
||||
return -EINVAL;
|
||||
|
||||
nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
|
||||
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_NL80211_TESTMODE
|
||||
static struct genl_multicast_group nl80211_testmode_mcgrp = {
|
||||
.name = "testmode",
|
||||
@ -6899,16 +6941,35 @@ static int nl80211_probe_client(struct sk_buff *skb,
|
||||
static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct cfg80211_beacon_registration *reg, *nreg;
|
||||
int rv;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (rdev->ap_beacons_nlportid)
|
||||
return -EBUSY;
|
||||
nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
|
||||
if (!nreg)
|
||||
return -ENOMEM;
|
||||
|
||||
rdev->ap_beacons_nlportid = info->snd_portid;
|
||||
/* First, check if already registered. */
|
||||
spin_lock_bh(&rdev->beacon_registrations_lock);
|
||||
list_for_each_entry(reg, &rdev->beacon_registrations, list) {
|
||||
if (reg->nlportid == info->snd_portid) {
|
||||
rv = -EALREADY;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
/* Add it to the list */
|
||||
nreg->nlportid = info->snd_portid;
|
||||
list_add(&nreg->list, &rdev->beacon_registrations);
|
||||
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
kfree(nreg);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
|
||||
@ -7625,6 +7686,14 @@ static struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_SET_MCAST_RATE,
|
||||
.doit = nl80211_set_mcast_rate,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
||||
@ -8914,43 +8983,46 @@ EXPORT_SYMBOL(cfg80211_probe_status);
|
||||
|
||||
void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
||||
const u8 *frame, size_t len,
|
||||
int freq, int sig_dbm, gfp_t gfp)
|
||||
int freq, int sig_dbm)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid);
|
||||
struct cfg80211_beacon_registration *reg;
|
||||
|
||||
trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
|
||||
|
||||
if (!nlportid)
|
||||
return;
|
||||
spin_lock_bh(&rdev->beacon_registrations_lock);
|
||||
list_for_each_entry(reg, &rdev->beacon_registrations, list) {
|
||||
msg = nlmsg_new(len + 100, GFP_ATOMIC);
|
||||
if (!msg) {
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
msg = nlmsg_new(len + 100, gfp);
|
||||
if (!msg)
|
||||
return;
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
|
||||
if (!hdr)
|
||||
goto nla_put_failure;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
|
||||
if (!hdr) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
||||
(freq &&
|
||||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
|
||||
(sig_dbm &&
|
||||
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
|
||||
nla_put(msg, NL80211_ATTR_FRAME, len, frame))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
|
||||
}
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
||||
(freq &&
|
||||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
|
||||
(sig_dbm &&
|
||||
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
|
||||
nla_put(msg, NL80211_ATTR_FRAME, len, frame))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
return;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
if (hdr)
|
||||
genlmsg_cancel(msg, hdr);
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
||||
@ -8962,6 +9034,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
||||
struct netlink_notify *notify = _notify;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
struct cfg80211_beacon_registration *reg, *tmp;
|
||||
|
||||
if (state != NETLINK_URELEASE)
|
||||
return NOTIFY_DONE;
|
||||
@ -8971,8 +9044,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
||||
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
|
||||
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
|
||||
cfg80211_mlme_unregister_socket(wdev, notify->portid);
|
||||
if (rdev->ap_beacons_nlportid == notify->portid)
|
||||
rdev->ap_beacons_nlportid = 0;
|
||||
|
||||
spin_lock_bh(&rdev->beacon_registrations_lock);
|
||||
list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
|
||||
list) {
|
||||
if (reg->nlportid == notify->portid) {
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
@ -476,21 +476,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
|
||||
}
|
||||
|
||||
static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_tx_power(&rdev->wiphy, type, mbm);
|
||||
ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
|
||||
trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
|
||||
ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
|
||||
int *dbm)
|
||||
struct wireless_dev *wdev, int *dbm)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_tx_power(&rdev->wiphy);
|
||||
ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm);
|
||||
trace_rdev_get_tx_power(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
|
||||
trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
|
||||
return ret;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
#define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac)
|
||||
|
||||
#define WDEV_ENTRY __field(u32, id)
|
||||
#define WDEV_ASSIGN (__entry->id) = (wdev->identifier)
|
||||
#define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0)
|
||||
#define WDEV_PR_FMT ", wdev id: %u"
|
||||
#define WDEV_PR_ARG (__entry->id)
|
||||
|
||||
@ -260,11 +260,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
|
||||
TP_ARGS(wiphy)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power,
|
||||
TP_PROTO(struct wiphy *wiphy),
|
||||
TP_ARGS(wiphy)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll,
|
||||
TP_PROTO(struct wiphy *wiphy),
|
||||
TP_ARGS(wiphy)
|
||||
@ -1230,22 +1225,29 @@ TRACE_EVENT(rdev_set_wiphy_params,
|
||||
WIPHY_PR_ARG, __entry->changed)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
|
||||
TP_ARGS(wiphy, wdev)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_set_tx_power,
|
||||
TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type,
|
||||
int mbm),
|
||||
TP_ARGS(wiphy, type, mbm),
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm),
|
||||
TP_ARGS(wiphy, wdev, type, mbm),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
WDEV_ENTRY
|
||||
__field(enum nl80211_tx_power_setting, type)
|
||||
__field(int, mbm)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
WDEV_ASSIGN;
|
||||
__entry->type = type;
|
||||
__entry->mbm = mbm;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d",
|
||||
WIPHY_PR_ARG, __entry->type, __entry->mbm)
|
||||
TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", type: %d, mbm: %d",
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_return_int_int,
|
||||
|
@ -980,6 +980,105 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_calculate_bitrate);
|
||||
|
||||
unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
|
||||
u8 attr, u8 *buf, unsigned int bufsize)
|
||||
{
|
||||
u8 *out = buf;
|
||||
u16 attr_remaining = 0;
|
||||
bool desired_attr = false;
|
||||
u16 desired_len = 0;
|
||||
|
||||
while (len > 0) {
|
||||
unsigned int iedatalen;
|
||||
unsigned int copy;
|
||||
const u8 *iedata;
|
||||
|
||||
if (len < 2)
|
||||
return -EILSEQ;
|
||||
iedatalen = ies[1];
|
||||
if (iedatalen + 2 > len)
|
||||
return -EILSEQ;
|
||||
|
||||
if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
|
||||
goto cont;
|
||||
|
||||
if (iedatalen < 4)
|
||||
goto cont;
|
||||
|
||||
iedata = ies + 2;
|
||||
|
||||
/* check WFA OUI, P2P subtype */
|
||||
if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
|
||||
iedata[2] != 0x9a || iedata[3] != 0x09)
|
||||
goto cont;
|
||||
|
||||
iedatalen -= 4;
|
||||
iedata += 4;
|
||||
|
||||
/* check attribute continuation into this IE */
|
||||
copy = min_t(unsigned int, attr_remaining, iedatalen);
|
||||
if (copy && desired_attr) {
|
||||
desired_len += copy;
|
||||
if (out) {
|
||||
memcpy(out, iedata, min(bufsize, copy));
|
||||
out += min(bufsize, copy);
|
||||
bufsize -= min(bufsize, copy);
|
||||
}
|
||||
|
||||
|
||||
if (copy == attr_remaining)
|
||||
return desired_len;
|
||||
}
|
||||
|
||||
attr_remaining -= copy;
|
||||
if (attr_remaining)
|
||||
goto cont;
|
||||
|
||||
iedatalen -= copy;
|
||||
iedata += copy;
|
||||
|
||||
while (iedatalen > 0) {
|
||||
u16 attr_len;
|
||||
|
||||
/* P2P attribute ID & size must fit */
|
||||
if (iedatalen < 3)
|
||||
return -EILSEQ;
|
||||
desired_attr = iedata[0] == attr;
|
||||
attr_len = get_unaligned_le16(iedata + 1);
|
||||
iedatalen -= 3;
|
||||
iedata += 3;
|
||||
|
||||
copy = min_t(unsigned int, attr_len, iedatalen);
|
||||
|
||||
if (desired_attr) {
|
||||
desired_len += copy;
|
||||
if (out) {
|
||||
memcpy(out, iedata, min(bufsize, copy));
|
||||
out += min(bufsize, copy);
|
||||
bufsize -= min(bufsize, copy);
|
||||
}
|
||||
|
||||
if (copy == attr_len)
|
||||
return desired_len;
|
||||
}
|
||||
|
||||
iedata += copy;
|
||||
iedatalen -= copy;
|
||||
attr_remaining = attr_len - copy;
|
||||
}
|
||||
|
||||
cont:
|
||||
len -= ies[1] + 2;
|
||||
ies += ies[1] + 2;
|
||||
}
|
||||
|
||||
if (attr_remaining && desired_attr)
|
||||
return -EILSEQ;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_get_p2p_attr);
|
||||
|
||||
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
|
||||
u32 beacon_int)
|
||||
{
|
||||
|
@ -895,7 +895,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm));
|
||||
return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
|
||||
}
|
||||
|
||||
static int cfg80211_wext_giwtxpower(struct net_device *dev,
|
||||
@ -914,7 +914,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
|
||||
if (!rdev->ops->get_tx_power)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = rdev_get_tx_power(rdev, &val);
|
||||
err = rdev_get_tx_power(rdev, wdev, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user