mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +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
fa5978447c
@ -1709,6 +1709,10 @@ enum ieee80211_eid {
|
||||
WLAN_EID_OPMODE_NOTIF = 199,
|
||||
WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
|
||||
WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
|
||||
WLAN_EID_EXTENDED_BSS_LOAD = 193,
|
||||
WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
|
||||
WLAN_EID_AID = 197,
|
||||
WLAN_EID_QUIET_CHANNEL = 198,
|
||||
|
||||
/* 802.11ad */
|
||||
WLAN_EID_NON_TX_BSSID_CAP = 83,
|
||||
@ -1860,6 +1864,11 @@ enum ieee80211_tdls_actioncode {
|
||||
WLAN_TDLS_DISCOVERY_REQUEST = 10,
|
||||
};
|
||||
|
||||
/* Interworking capabilities are set in 7th bit of 4th byte of the
|
||||
* @WLAN_EID_EXT_CAPABILITY information element
|
||||
*/
|
||||
#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED BIT(7)
|
||||
|
||||
/*
|
||||
* TDLS capabililites to be enabled in the 5th byte of the
|
||||
* @WLAN_EID_EXT_CAPABILITY information element
|
||||
|
@ -665,6 +665,30 @@ struct cfg80211_ap_settings {
|
||||
bool radar_required;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_csa_settings - channel switch settings
|
||||
*
|
||||
* Used for channel switch
|
||||
*
|
||||
* @chandef: defines the channel to use after the switch
|
||||
* @beacon_csa: beacon data while performing the switch
|
||||
* @counter_offset_beacon: offset for the counter within the beacon (tail)
|
||||
* @counter_offset_presp: offset for the counter within the probe response
|
||||
* @beacon_after: beacon data to be used on the new channel
|
||||
* @radar_required: whether radar detection is required on the new channel
|
||||
* @block_tx: whether transmissions should be blocked while changing
|
||||
* @count: number of beacons until switch
|
||||
*/
|
||||
struct cfg80211_csa_settings {
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct cfg80211_beacon_data beacon_csa;
|
||||
u16 counter_offset_beacon, counter_offset_presp;
|
||||
struct cfg80211_beacon_data beacon_after;
|
||||
bool radar_required;
|
||||
bool block_tx;
|
||||
u8 count;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum station_parameters_apply_mask - station parameter values to apply
|
||||
* @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
|
||||
@ -2139,6 +2163,8 @@ struct cfg80211_update_ft_ies_params {
|
||||
* @crit_proto_stop: Indicates critical protocol no longer needs increased link
|
||||
* reliability. This operation can not fail.
|
||||
* @set_coalesce: Set coalesce parameters.
|
||||
*
|
||||
* @channel_switch: initiate channel-switch procedure (with CSA)
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||
@ -2376,6 +2402,10 @@ struct cfg80211_ops {
|
||||
struct wireless_dev *wdev);
|
||||
int (*set_coalesce)(struct wiphy *wiphy,
|
||||
struct cfg80211_coalesce *coalesce);
|
||||
|
||||
int (*channel_switch)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2441,6 +2471,8 @@ struct cfg80211_ops {
|
||||
* @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
|
||||
* @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
|
||||
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
|
||||
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
|
||||
* beaconing mode (AP, IBSS, Mesh, ...).
|
||||
*/
|
||||
enum wiphy_flags {
|
||||
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
|
||||
@ -2465,6 +2497,7 @@ enum wiphy_flags {
|
||||
WIPHY_FLAG_OFFCHAN_TX = BIT(20),
|
||||
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
|
||||
WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
|
||||
WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -152,11 +152,14 @@ struct ieee80211_low_level_stats {
|
||||
* @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
|
||||
* @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
|
||||
* @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
|
||||
* @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
|
||||
* this is used only with channel switching with CSA
|
||||
*/
|
||||
enum ieee80211_chanctx_change {
|
||||
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
|
||||
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
|
||||
IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
|
||||
IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1084,6 +1087,7 @@ enum ieee80211_vif_flags {
|
||||
* @addr: address of this interface
|
||||
* @p2p: indicates whether this AP or STA interface is a p2p
|
||||
* interface, i.e. a GO or p2p-sta respectively
|
||||
* @csa_active: marks whether a channel switch is going on
|
||||
* @driver_flags: flags/capabilities the driver has for this interface,
|
||||
* these need to be set (or cleared) when the interface is added
|
||||
* or, if supported by the driver, the interface type is changed
|
||||
@ -1106,6 +1110,7 @@ struct ieee80211_vif {
|
||||
struct ieee80211_bss_conf bss_conf;
|
||||
u8 addr[ETH_ALEN];
|
||||
bool p2p;
|
||||
bool csa_active;
|
||||
|
||||
u8 cab_queue;
|
||||
u8 hw_queue[IEEE80211_NUM_ACS];
|
||||
@ -2637,6 +2642,16 @@ enum ieee80211_roc_type {
|
||||
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
|
||||
* Currently, this is only called for managed or P2P client interfaces.
|
||||
* This callback is optional; it must not sleep.
|
||||
*
|
||||
* @channel_switch_beacon: Starts a channel switch to a new channel.
|
||||
* Beacons are modified to include CSA or ECSA IEs before calling this
|
||||
* function. The corresponding count fields in these IEs must be
|
||||
* decremented, and when they reach zero the driver must call
|
||||
* ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
|
||||
* get the csa counter decremented by mac80211, but must check if it is
|
||||
* zero using ieee80211_csa_is_complete() after the beacon has been
|
||||
* transmitted and then call ieee80211_csa_finish().
|
||||
*
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2824,6 +2839,9 @@ struct ieee80211_ops {
|
||||
struct ieee80211_vif *vif,
|
||||
struct inet6_dev *idev);
|
||||
#endif
|
||||
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3318,6 +3336,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||
return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_csa_finish - notify mac80211 about channel switch
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
*
|
||||
* After a channel switch announcement was scheduled and the counter in this
|
||||
* announcement hit zero, this function must be called by the driver to
|
||||
* notify mac80211 that the channel can be changed.
|
||||
*/
|
||||
void ieee80211_csa_finish(struct ieee80211_vif *vif);
|
||||
|
||||
/**
|
||||
* ieee80211_csa_is_complete - find out if counters reached zero
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
*
|
||||
* This function returns whether the channel switch counters reached zero.
|
||||
*/
|
||||
bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
|
||||
|
||||
|
||||
/**
|
||||
* ieee80211_proberesp_get - retrieve a Probe Response template
|
||||
* @hw: pointer obtained from ieee80211_alloc_hw().
|
||||
|
@ -676,6 +676,16 @@
|
||||
* @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
|
||||
* @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
|
||||
*
|
||||
* @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
|
||||
* the new channel information (Channel Switch Announcement - CSA)
|
||||
* in the beacon for some time (as defined in the
|
||||
* %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
|
||||
* new channel. Userspace provides the new channel information (using
|
||||
* %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
|
||||
* width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
|
||||
* other station that transmission must be blocked until the channel
|
||||
* switch is complete.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -841,6 +851,8 @@ enum nl80211_commands {
|
||||
NL80211_CMD_GET_COALESCE,
|
||||
NL80211_CMD_SET_COALESCE,
|
||||
|
||||
NL80211_CMD_CHANNEL_SWITCH,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
@ -1469,6 +1481,18 @@ enum nl80211_commands {
|
||||
*
|
||||
* @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
|
||||
*
|
||||
* @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
|
||||
* until the channel switch event.
|
||||
* @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
|
||||
* must be blocked on the current channel (before the channel switch
|
||||
* operation).
|
||||
* @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
|
||||
* for the time while performing a channel switch.
|
||||
* @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
|
||||
* field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
|
||||
* @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
|
||||
* field in the probe response (%NL80211_ATTR_PROBE_RESP).
|
||||
*
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -1771,6 +1795,12 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_COALESCE_RULE,
|
||||
|
||||
NL80211_ATTR_CH_SWITCH_COUNT,
|
||||
NL80211_ATTR_CH_SWITCH_BLOCK_TX,
|
||||
NL80211_ATTR_CSA_IES,
|
||||
NL80211_ATTR_CSA_C_OFF_BEACON,
|
||||
NL80211_ATTR_CSA_C_OFF_PRESP,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -862,8 +862,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_beacon_data *params)
|
||||
int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_beacon_data *params)
|
||||
{
|
||||
struct beacon_data *new, *old;
|
||||
int new_head_len, new_tail_len;
|
||||
@ -1026,6 +1026,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
/* don't allow changing the beacon while CSA is in place - offset
|
||||
* of channel switch counter may change
|
||||
*/
|
||||
if (sdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
old = rtnl_dereference(sdata->u.ap.beacon);
|
||||
if (!old)
|
||||
return -ENOENT;
|
||||
@ -1050,6 +1056,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||
return -ENOENT;
|
||||
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
/* abort any running channel switch */
|
||||
sdata->vif.csa_active = false;
|
||||
cancel_work_sync(&sdata->csa_finalize_work);
|
||||
|
||||
/* 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);
|
||||
@ -2777,6 +2787,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cfg80211_beacon_data *
|
||||
cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
|
||||
{
|
||||
struct cfg80211_beacon_data *new_beacon;
|
||||
u8 *pos;
|
||||
int len;
|
||||
|
||||
len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
|
||||
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
|
||||
beacon->probe_resp_len;
|
||||
|
||||
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
|
||||
if (!new_beacon)
|
||||
return NULL;
|
||||
|
||||
pos = (u8 *)(new_beacon + 1);
|
||||
if (beacon->head_len) {
|
||||
new_beacon->head_len = beacon->head_len;
|
||||
new_beacon->head = pos;
|
||||
memcpy(pos, beacon->head, beacon->head_len);
|
||||
pos += beacon->head_len;
|
||||
}
|
||||
if (beacon->tail_len) {
|
||||
new_beacon->tail_len = beacon->tail_len;
|
||||
new_beacon->tail = pos;
|
||||
memcpy(pos, beacon->tail, beacon->tail_len);
|
||||
pos += beacon->tail_len;
|
||||
}
|
||||
if (beacon->beacon_ies_len) {
|
||||
new_beacon->beacon_ies_len = beacon->beacon_ies_len;
|
||||
new_beacon->beacon_ies = pos;
|
||||
memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
|
||||
pos += beacon->beacon_ies_len;
|
||||
}
|
||||
if (beacon->proberesp_ies_len) {
|
||||
new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
|
||||
new_beacon->proberesp_ies = pos;
|
||||
memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
|
||||
pos += beacon->proberesp_ies_len;
|
||||
}
|
||||
if (beacon->assocresp_ies_len) {
|
||||
new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
|
||||
new_beacon->assocresp_ies = pos;
|
||||
memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
|
||||
pos += beacon->assocresp_ies_len;
|
||||
}
|
||||
if (beacon->probe_resp_len) {
|
||||
new_beacon->probe_resp_len = beacon->probe_resp_len;
|
||||
beacon->probe_resp = pos;
|
||||
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
|
||||
pos += beacon->probe_resp_len;
|
||||
}
|
||||
|
||||
return new_beacon;
|
||||
}
|
||||
|
||||
void ieee80211_csa_finalize_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
csa_finalize_work);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int err, changed;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
|
||||
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
|
||||
return;
|
||||
|
||||
sdata->radar_required = sdata->csa_radar_required;
|
||||
err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
|
||||
&changed);
|
||||
if (WARN_ON(err < 0))
|
||||
return;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
changed |= err;
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
sdata->u.ap.next_beacon = NULL;
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
|
||||
}
|
||||
|
||||
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
int err, num_chanctx;
|
||||
|
||||
if (!list_empty(&local->roc_list) || local->scanning)
|
||||
return -EBUSY;
|
||||
|
||||
if (sdata->wdev.cac_started)
|
||||
return -EBUSY;
|
||||
|
||||
if (cfg80211_chandef_identical(¶ms->chandef,
|
||||
&sdata->vif.bss_conf.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf) {
|
||||
rcu_read_unlock();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
if (chanctx->refcount > 1) {
|
||||
rcu_read_unlock();
|
||||
return -EBUSY;
|
||||
}
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (num_chanctx > 1)
|
||||
return -EBUSY;
|
||||
|
||||
/* don't allow another channel switch if one is already active. */
|
||||
if (sdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
/* only handle AP for now. */
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after);
|
||||
if (!sdata->u.ap.next_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
|
||||
sdata->csa_counter_offset_presp = params->counter_offset_presp;
|
||||
sdata->csa_radar_required = params->radar_required;
|
||||
|
||||
if (params->block_tx)
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
local->csa_chandef = params->chandef;
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
drv_channel_switch_beacon(sdata, ¶ms->chandef);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan, bool offchan,
|
||||
unsigned int wait, const u8 *buf, size_t len,
|
||||
@ -3494,4 +3676,5 @@ struct cfg80211_ops mac80211_config_ops = {
|
||||
.get_et_strings = ieee80211_get_et_strings,
|
||||
.get_channel = ieee80211_cfg_get_channel,
|
||||
.start_radar_detection = ieee80211_start_radar_detection,
|
||||
.channel_switch = ieee80211_channel_switch,
|
||||
};
|
||||
|
@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int ret;
|
||||
u32 chanctx_changed = 0;
|
||||
|
||||
/* should never be called if not performing a channel switch. */
|
||||
if (WARN_ON(!sdata->vif.csa_active))
|
||||
return -EINVAL;
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (ctx->refcount != 1) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width != chandef->width) {
|
||||
chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
|
||||
*changed |= BSS_CHANGED_BANDWIDTH;
|
||||
}
|
||||
|
||||
sdata->vif.bss_conf.chandef = *chandef;
|
||||
ctx->conf.def = *chandef;
|
||||
|
||||
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
|
||||
drv_change_chanctx(local, ctx, chanctx_changed);
|
||||
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = *chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
}
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed)
|
||||
|
@ -455,6 +455,15 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
||||
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
|
||||
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
|
||||
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
sta->debugfs.dir,
|
||||
(u32 *)&sta->driver_buffered_tids);
|
||||
else
|
||||
debugfs_create_x64("driver_buffered_tids", 0400,
|
||||
sta->debugfs.dir,
|
||||
(u64 *)&sta->driver_buffered_tids);
|
||||
|
||||
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
|
||||
}
|
||||
|
||||
|
@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
if (local->ops->channel_switch_beacon) {
|
||||
trace_drv_channel_switch_beacon(local, sdata, chandef);
|
||||
local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
|
||||
chandef);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
|
||||
#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
|
||||
#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)
|
||||
|
||||
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
|
||||
|
||||
@ -740,6 +741,33 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
return active;
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta, *tmp;
|
||||
unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
|
||||
unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
if (sdata != sta->sdata)
|
||||
continue;
|
||||
|
||||
if (time_after(jiffies, sta->last_rx + exp_time) ||
|
||||
(time_after(jiffies, sta->last_rx + exp_rsn_time) &&
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
|
||||
sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED ?
|
||||
"not authorized " : "", sta->sta.addr);
|
||||
|
||||
WARN_ON(__sta_info_destroy(sta));
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called with state == IEEE80211_IBSS_MLME_JOINED
|
||||
*/
|
||||
@ -754,7 +782,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
mod_timer(&ifibss->timer,
|
||||
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
|
||||
|
||||
ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
|
||||
ieee80211_ibss_sta_expire(sdata);
|
||||
|
||||
if (time_before(jiffies, ifibss->last_scan_completed +
|
||||
IEEE80211_IBSS_MERGE_INTERVAL))
|
||||
|
@ -259,6 +259,8 @@ struct ieee80211_if_ap {
|
||||
struct beacon_data __rcu *beacon;
|
||||
struct probe_resp __rcu *probe_resp;
|
||||
|
||||
/* to be used after channel switch. */
|
||||
struct cfg80211_beacon_data *next_beacon;
|
||||
struct list_head vlans;
|
||||
|
||||
struct ps_data ps;
|
||||
@ -716,6 +718,11 @@ struct ieee80211_sub_if_data {
|
||||
|
||||
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
|
||||
|
||||
struct work_struct csa_finalize_work;
|
||||
int csa_counter_offset_beacon;
|
||||
int csa_counter_offset_presp;
|
||||
bool csa_radar_required;
|
||||
|
||||
/* used to reconfigure hardware SM PS */
|
||||
struct work_struct recalc_smps;
|
||||
|
||||
@ -1094,7 +1101,6 @@ struct ieee80211_local {
|
||||
u32 dot11TransmittedFrameCount;
|
||||
|
||||
#ifdef CONFIG_MAC80211_LEDS
|
||||
int tx_led_counter, rx_led_counter;
|
||||
struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
|
||||
struct tpt_led_trigger *tpt_led_trigger;
|
||||
char tx_led_name[32], rx_led_name[32],
|
||||
@ -1373,6 +1379,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
|
||||
void ieee80211_sw_roc_work(struct work_struct *work);
|
||||
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
|
||||
|
||||
/* channel switch handling */
|
||||
void ieee80211_csa_finalize_work(struct work_struct *work);
|
||||
|
||||
/* interface handling */
|
||||
int ieee80211_iface_init(void);
|
||||
void ieee80211_iface_exit(void);
|
||||
@ -1394,6 +1403,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
|
||||
|
||||
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_beacon_data *params);
|
||||
|
||||
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
@ -1655,6 +1666,11 @@ int __must_check
|
||||
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed);
|
||||
/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
|
||||
int __must_check
|
||||
ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed);
|
||||
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -274,6 +274,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
|
||||
if (iftype == NL80211_IFTYPE_ADHOC &&
|
||||
nsdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return -EBUSY;
|
||||
/*
|
||||
* will not add another interface while any channel
|
||||
* switch is active.
|
||||
*/
|
||||
if (nsdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* The remaining checks are only performed for interfaces
|
||||
@ -804,6 +810,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
cancel_work_sync(&local->dynamic_ps_enable_work);
|
||||
|
||||
cancel_work_sync(&sdata->recalc_smps);
|
||||
sdata->vif.csa_active = false;
|
||||
cancel_work_sync(&sdata->csa_finalize_work);
|
||||
|
||||
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
||||
|
||||
@ -1267,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||
skb_queue_head_init(&sdata->skb_queue);
|
||||
INIT_WORK(&sdata->work, ieee80211_iface_work);
|
||||
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
|
||||
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
|
||||
|
||||
switch (type) {
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
|
@ -12,27 +12,22 @@
|
||||
#include <linux/export.h>
|
||||
#include "led.h"
|
||||
|
||||
#define MAC80211_BLINK_DELAY 50 /* ms */
|
||||
|
||||
void ieee80211_led_rx(struct ieee80211_local *local)
|
||||
{
|
||||
unsigned long led_delay = MAC80211_BLINK_DELAY;
|
||||
if (unlikely(!local->rx_led))
|
||||
return;
|
||||
if (local->rx_led_counter++ % 2 == 0)
|
||||
led_trigger_event(local->rx_led, LED_OFF);
|
||||
else
|
||||
led_trigger_event(local->rx_led, LED_FULL);
|
||||
led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0);
|
||||
}
|
||||
|
||||
/* q is 1 if a packet was enqueued, 0 if it has been transmitted */
|
||||
void ieee80211_led_tx(struct ieee80211_local *local, int q)
|
||||
void ieee80211_led_tx(struct ieee80211_local *local)
|
||||
{
|
||||
unsigned long led_delay = MAC80211_BLINK_DELAY;
|
||||
if (unlikely(!local->tx_led))
|
||||
return;
|
||||
/* not sure how this is supposed to work ... */
|
||||
local->tx_led_counter += 2*q-1;
|
||||
if (local->tx_led_counter % 2 == 0)
|
||||
led_trigger_event(local->tx_led, LED_OFF);
|
||||
else
|
||||
led_trigger_event(local->tx_led, LED_FULL);
|
||||
led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0);
|
||||
}
|
||||
|
||||
void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef CONFIG_MAC80211_LEDS
|
||||
void ieee80211_led_rx(struct ieee80211_local *local);
|
||||
void ieee80211_led_tx(struct ieee80211_local *local, int q);
|
||||
void ieee80211_led_tx(struct ieee80211_local *local);
|
||||
void ieee80211_led_assoc(struct ieee80211_local *local,
|
||||
bool associated);
|
||||
void ieee80211_led_radio(struct ieee80211_local *local,
|
||||
@ -27,7 +27,7 @@ void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
|
||||
static inline void ieee80211_led_rx(struct ieee80211_local *local)
|
||||
{
|
||||
}
|
||||
static inline void ieee80211_led_tx(struct ieee80211_local *local, int q)
|
||||
static inline void ieee80211_led_tx(struct ieee80211_local *local)
|
||||
{
|
||||
}
|
||||
static inline void ieee80211_led_assoc(struct ieee80211_local *local,
|
||||
|
@ -235,7 +235,8 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
|
||||
|
||||
/* IEEE80211_RADIOTAP_RATE rate */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS)))
|
||||
len += 2;
|
||||
|
||||
/* IEEE80211_RADIOTAP_TX_FLAGS */
|
||||
@ -244,16 +245,21 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
|
||||
/* IEEE80211_RADIOTAP_DATA_RETRIES */
|
||||
len += 1;
|
||||
|
||||
/* IEEE80211_TX_RC_MCS */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
|
||||
len += 3;
|
||||
/* IEEE80211_RADIOTAP_MCS
|
||||
* IEEE80211_RADIOTAP_VHT */
|
||||
if (info->status.rates[0].idx >= 0) {
|
||||
if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
|
||||
len += 3;
|
||||
else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
len = ALIGN(len, 2) + 12;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
|
||||
ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct sk_buff *skb, int retry_count,
|
||||
int rtap_len, int shift)
|
||||
{
|
||||
@ -280,7 +286,8 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
|
||||
|
||||
/* IEEE80211_RADIOTAP_RATE */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS))) {
|
||||
u16 rate;
|
||||
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
|
||||
@ -310,9 +317,12 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
|
||||
*pos = retry_count;
|
||||
pos++;
|
||||
|
||||
/* IEEE80211_TX_RC_MCS */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
|
||||
if (info->status.rates[0].idx < 0)
|
||||
return;
|
||||
|
||||
/* IEEE80211_RADIOTAP_MCS
|
||||
* IEEE80211_RADIOTAP_VHT */
|
||||
if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
|
||||
pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_GI |
|
||||
@ -325,8 +335,48 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
|
||||
pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF;
|
||||
pos[2] = info->status.rates[0].idx;
|
||||
pos += 3;
|
||||
}
|
||||
} else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
u16 known = local->hw.radiotap_vht_details &
|
||||
(IEEE80211_RADIOTAP_VHT_KNOWN_GI |
|
||||
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
|
||||
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
|
||||
|
||||
/* required alignment from rthdr */
|
||||
pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
|
||||
|
||||
/* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
|
||||
put_unaligned_le16(known, pos);
|
||||
pos += 2;
|
||||
|
||||
/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
|
||||
if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
|
||||
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
|
||||
pos++;
|
||||
|
||||
/* u8 bandwidth */
|
||||
if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
*pos = 1;
|
||||
else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
||||
*pos = 4;
|
||||
else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
|
||||
*pos = 11;
|
||||
else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */
|
||||
*pos = 0;
|
||||
pos++;
|
||||
|
||||
/* u8 mcs_nss[4] */
|
||||
*pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) |
|
||||
ieee80211_rate_get_vht_nss(&info->status.rates[0]);
|
||||
pos += 4;
|
||||
|
||||
/* u8 coding */
|
||||
pos++;
|
||||
/* u8 group_id */
|
||||
pos++;
|
||||
/* u16 partial_aid */
|
||||
pos += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
@ -564,7 +614,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_led_tx(local, 0);
|
||||
ieee80211_led_tx(local);
|
||||
|
||||
/* SNMP counters
|
||||
* Fragments are passed to low-level drivers as separate skbs, so these
|
||||
@ -631,8 +681,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len,
|
||||
shift);
|
||||
ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
|
||||
rtap_len, shift);
|
||||
|
||||
/* XXX: is this sufficient for BPF? */
|
||||
skb_set_mac_header(skb, 0);
|
||||
|
@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected,
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_channel_switch_beacon,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_chan_def *chandef),
|
||||
|
||||
TP_ARGS(local, sdata, chandef),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
CHANDEF_ENTRY
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
CHANDEF_ASSIGN(chandef);
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM mac80211_msg
|
||||
|
@ -1300,7 +1300,6 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
|
||||
txpending);
|
||||
|
||||
ieee80211_tpt_led_trig_tx(local, fc, led_len);
|
||||
ieee80211_led_tx(local, 1);
|
||||
|
||||
WARN_ON_ONCE(!skb_queue_empty(skbs));
|
||||
|
||||
@ -2339,6 +2338,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_csa_finish(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->csa_finalize_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_csa_finish);
|
||||
|
||||
static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct beacon_data *beacon)
|
||||
{
|
||||
struct probe_resp *resp;
|
||||
int counter_offset_beacon = sdata->csa_counter_offset_beacon;
|
||||
int counter_offset_presp = sdata->csa_counter_offset_presp;
|
||||
|
||||
/* warn if the driver did not check for/react to csa completeness */
|
||||
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
|
||||
return;
|
||||
|
||||
((u8 *)beacon->tail)[counter_offset_beacon]--;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
||||
counter_offset_presp) {
|
||||
rcu_read_lock();
|
||||
resp = rcu_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
/* if nl80211 accepted the offset, this should not happen. */
|
||||
if (WARN_ON(!resp)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
resp->data[counter_offset_presp]--;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct beacon_data *beacon = NULL;
|
||||
u8 *beacon_data;
|
||||
size_t beacon_data_len;
|
||||
int counter_beacon = sdata->csa_counter_offset_beacon;
|
||||
int ret = false;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return false;
|
||||
|
||||
rcu_read_lock();
|
||||
if (vif->type == NL80211_IFTYPE_AP) {
|
||||
struct ieee80211_if_ap *ap = &sdata->u.ap;
|
||||
|
||||
beacon = rcu_dereference(ap->beacon);
|
||||
if (WARN_ON(!beacon || !beacon->tail))
|
||||
goto out;
|
||||
beacon_data = beacon->tail;
|
||||
beacon_data_len = beacon->tail_len;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WARN_ON(counter_beacon > beacon_data_len))
|
||||
goto out;
|
||||
|
||||
if (beacon_data[counter_beacon] == 0)
|
||||
ret = true;
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_csa_is_complete);
|
||||
|
||||
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
u16 *tim_offset, u16 *tim_length)
|
||||
@ -2369,6 +2443,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
struct beacon_data *beacon = rcu_dereference(ap->beacon);
|
||||
|
||||
if (beacon) {
|
||||
if (sdata->vif.csa_active)
|
||||
ieee80211_update_csa(sdata, beacon);
|
||||
|
||||
/*
|
||||
* headroom, head length,
|
||||
* tail length and maximum TIM length
|
||||
|
@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
||||
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
|
||||
.len = IEEE80211_MAX_DATA_LEN },
|
||||
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
|
||||
[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -1424,6 +1429,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
|
||||
if (state->split) {
|
||||
CMD(crit_proto_start, CRIT_PROTOCOL_START);
|
||||
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
|
||||
if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
|
||||
CMD(channel_switch, CHANNEL_SWITCH);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NL80211_TESTMODE
|
||||
@ -5615,6 +5622,111 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_csa_settings params;
|
||||
/* csa_attrs is defined static to avoid waste of stack size - this
|
||||
* function is called under RTNL lock, so this should not be a problem.
|
||||
*/
|
||||
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
||||
u8 radar_detect_width = 0;
|
||||
int err;
|
||||
|
||||
if (!rdev->ops->channel_switch ||
|
||||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* may add IBSS support later */
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
|
||||
!info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
|
||||
return -EINVAL;
|
||||
|
||||
/* only important for AP, IBSS and mesh create IEs internally */
|
||||
if (!info->attrs[NL80211_ATTR_CSA_IES])
|
||||
return -EINVAL;
|
||||
|
||||
/* useless if AP is not running */
|
||||
if (!wdev->beacon_interval)
|
||||
return -EINVAL;
|
||||
|
||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
|
||||
info->attrs[NL80211_ATTR_CSA_IES],
|
||||
nl80211_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
|
||||
return -EINVAL;
|
||||
|
||||
params.counter_offset_beacon =
|
||||
nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
|
||||
if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
|
||||
return -EINVAL;
|
||||
|
||||
/* sanity check - counters should be the same */
|
||||
if (params.beacon_csa.tail[params.counter_offset_beacon] !=
|
||||
params.count)
|
||||
return -EINVAL;
|
||||
|
||||
if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
|
||||
params.counter_offset_presp =
|
||||
nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
|
||||
if (params.counter_offset_presp >=
|
||||
params.beacon_csa.probe_resp_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
|
||||
params.count)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
} else if (err) {
|
||||
radar_detect_width = BIT(params.chandef.width);
|
||||
params.radar_required = true;
|
||||
}
|
||||
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
params.chandef.chan,
|
||||
CHAN_MODE_SHARED,
|
||||
radar_detect_width);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
||||
params.block_tx = true;
|
||||
|
||||
return rdev_channel_switch(rdev, dev, ¶ms);
|
||||
}
|
||||
|
||||
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
||||
u32 seq, int flags,
|
||||
struct cfg80211_registered_device *rdev,
|
||||
@ -9365,7 +9477,15 @@ static struct genl_ops nl80211_ops[] = {
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
}
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_CHANNEL_SWITCH,
|
||||
.doit = nl80211_channel_switch,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
||||
|
@ -923,4 +923,16 @@ static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev,
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_channel_switch(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __CFG80211_RDEV_OPS */
|
||||
|
@ -1841,6 +1841,39 @@ TRACE_EVENT(rdev_crit_proto_stop,
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_channel_switch,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
struct cfg80211_csa_settings *params),
|
||||
TP_ARGS(wiphy, netdev, params),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
CHAN_DEF_ENTRY
|
||||
__field(u16, counter_offset_beacon)
|
||||
__field(u16, counter_offset_presp)
|
||||
__field(bool, radar_required)
|
||||
__field(bool, block_tx)
|
||||
__field(u8, count)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
CHAN_DEF_ASSIGN(¶ms->chandef);
|
||||
__entry->counter_offset_beacon = params->counter_offset_beacon;
|
||||
__entry->counter_offset_presp = params->counter_offset_presp;
|
||||
__entry->radar_required = params->radar_required;
|
||||
__entry->block_tx = params->block_tx;
|
||||
__entry->count = params->count;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
|
||||
", block_tx: %d, count: %u, radar_required: %d"
|
||||
", counter offsets (beacon/presp): %u/%u",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
|
||||
__entry->block_tx, __entry->count, __entry->radar_required,
|
||||
__entry->counter_offset_beacon,
|
||||
__entry->counter_offset_presp)
|
||||
);
|
||||
|
||||
/*************************************************************
|
||||
* cfg80211 exported functions traces *
|
||||
*************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user