Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
commit
e9517fecf2
@ -1391,8 +1391,8 @@ struct ieee80211_vht_operation {
|
||||
#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
|
||||
#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000
|
||||
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000
|
||||
#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX 0x0000e000
|
||||
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00070000
|
||||
#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
|
||||
#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
|
||||
#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000
|
||||
|
@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_required - checks if radar detection is required
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
|
||||
*/
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
|
||||
/**
|
||||
* ieee80211_chandef_rate_flags - returns rate flags for a channel
|
||||
*
|
||||
|
@ -829,6 +829,15 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
||||
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
|
||||
* @RX_FLAG_10MHZ: 10 MHz (half channel) was used
|
||||
* @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
|
||||
* @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
|
||||
* subframes instead of a one huge frame for performance reasons.
|
||||
* All, but the last MSDU from an A-MSDU should have this flag set. E.g.
|
||||
* if an A-MSDU has 3 frames, the first 2 must have the flag set, while
|
||||
* the 3rd (last) one must not have this flag set. The flag is used to
|
||||
* deal with retransmission/duplication recovery properly since A-MSDU
|
||||
* subframes share the same sequence number. Reported subframes can be
|
||||
* either regular MSDU or singly A-MSDUs. Subframes must not be
|
||||
* interleaved with other frames.
|
||||
*/
|
||||
enum mac80211_rx_flags {
|
||||
RX_FLAG_MMIC_ERROR = BIT(0),
|
||||
@ -859,6 +868,7 @@ enum mac80211_rx_flags {
|
||||
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
|
||||
RX_FLAG_10MHZ = BIT(28),
|
||||
RX_FLAG_5MHZ = BIT(29),
|
||||
RX_FLAG_AMSDU_MORE = BIT(30),
|
||||
};
|
||||
|
||||
#define RX_FLAG_STBC_SHIFT 26
|
||||
@ -1492,6 +1502,11 @@ struct ieee80211_tx_control {
|
||||
*
|
||||
* @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
|
||||
* only, to allow getting TBTT of a DTIM beacon.
|
||||
*
|
||||
* @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
|
||||
* for a single active channel while using channel contexts. When support
|
||||
* is not enabled the default action is to disconnect when getting the
|
||||
* CSA frame.
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
|
||||
@ -1522,6 +1537,7 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
|
||||
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
|
||||
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
|
||||
IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2666,6 +2682,10 @@ enum ieee80211_roc_type {
|
||||
* zero using ieee80211_csa_is_complete() after the beacon has been
|
||||
* transmitted and then call ieee80211_csa_finish().
|
||||
*
|
||||
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
|
||||
* information in bss_conf is set up and the beacon can be retrieved. A
|
||||
* channel context is bound before this is called.
|
||||
* @leave_ibss: Leave the IBSS again.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2857,6 +2877,9 @@ struct ieee80211_ops {
|
||||
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3919,6 +3942,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
|
||||
*
|
||||
* This function iterates over the interfaces associated with a given
|
||||
* hardware that are currently active and calls the callback for them.
|
||||
* This version can only be used while holding the RTNL.
|
||||
*
|
||||
* @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_rtnl(struct ieee80211_hw *hw,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data,
|
||||
u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
||||
*
|
||||
|
@ -2865,30 +2865,43 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
||||
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;
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
}
|
||||
|
||||
changed |= err;
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
sdata->u.ap.next_beacon = NULL;
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
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;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
ieee80211_ibss_finish_csa(sdata);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2936,20 +2949,56 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (sdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
/* only handle AP for now. */
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
sdata->csa_counter_offset_beacon =
|
||||
params->counter_offset_beacon;
|
||||
sdata->csa_counter_offset_presp = params->counter_offset_presp;
|
||||
sdata->u.ap.next_beacon =
|
||||
cfg80211_beacon_dup(¶ms->beacon_after);
|
||||
if (!sdata->u.ap.next_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
||||
if (err < 0) {
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (!sdata->vif.bss_conf.ibss_joined)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->chandef.width != sdata->u.ibss.chandef.width)
|
||||
return -EINVAL;
|
||||
|
||||
switch (params->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
if (cfg80211_get_chandef_type(¶ms->chandef) !=
|
||||
cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
|
||||
return -EINVAL;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* changes into another band are not supported */
|
||||
if (sdata->u.ibss.chandef.chan->band !=
|
||||
params->chandef.chan->band)
|
||||
return -EINVAL;
|
||||
|
||||
err = ieee80211_ibss_csa_beacon(sdata, params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
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)
|
||||
@ -2957,10 +3006,6 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
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;
|
||||
|
||||
@ -3014,7 +3059,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
need_offchan = true;
|
||||
if (!ieee80211_is_action(mgmt->frame_control) ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
|
||||
break;
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, mgmt->da);
|
||||
|
@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
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);
|
||||
|
@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
|
||||
sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
|
||||
if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
|
||||
sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"HOST_BCAST_PS_BUFFERING\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"HOST_BCAST_PS_BUFFERING\n");
|
||||
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_SLOT_INCAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_SLOT_INCAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
|
||||
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
|
||||
sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"NEED_DTIM_BEFORE_ASSOC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
|
||||
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
|
||||
sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"SUPPORTS_DYNAMIC_SMPS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"REPORTS_TX_ACK_STATUS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
||||
sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
|
||||
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
|
||||
sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
|
||||
|
||||
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
kfree(buf);
|
||||
|
@ -1085,4 +1085,31 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
static inline int drv_join_ibss(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
might_sleep();
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
|
||||
if (local->ops->join_ibss)
|
||||
ret = local->ops->join_ibss(&local->hw, &sdata->vif);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_leave_ibss(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
might_sleep();
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_leave_ibss(local, sdata);
|
||||
if (local->ops->leave_ibss)
|
||||
local->ops->leave_ibss(&local->hw, &sdata->vif);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
const int beacon_int, const u32 basic_rates,
|
||||
const u16 capability, u64 tsf,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
bool *have_higher_than_11mbit)
|
||||
bool *have_higher_than_11mbit,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
2 + 8 /* max Supported Rates */ +
|
||||
3 /* max DS params */ +
|
||||
4 /* IBSS params */ +
|
||||
5 /* Channel Switch Announcement */ +
|
||||
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
2 + sizeof(struct ieee80211_ht_cap) +
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
*pos++ = 0;
|
||||
*pos++ = 0;
|
||||
|
||||
if (csa_settings) {
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH;
|
||||
*pos++ = 3;
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0;
|
||||
*pos++ = ieee80211_frequency_to_channel(
|
||||
csa_settings->chandef.chan->center_freq);
|
||||
sdata->csa_counter_offset_beacon = (pos - presp->head);
|
||||
*pos++ = csa_settings->count;
|
||||
}
|
||||
|
||||
/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
|
||||
if (rates_n > 8) {
|
||||
*pos++ = WLAN_EID_EXT_SUPP_RATES;
|
||||
@ -217,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct beacon_data *presp;
|
||||
enum nl80211_bss_scan_width scan_width;
|
||||
bool have_higher_than_11mbit;
|
||||
int err;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -235,6 +248,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_bss_info_change_notify(sdata,
|
||||
BSS_CHANGED_IBSS |
|
||||
BSS_CHANGED_BEACON_ENABLED);
|
||||
drv_leave_ibss(local, sdata);
|
||||
}
|
||||
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
@ -276,7 +290,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
|
||||
capability, tsf, &chandef,
|
||||
&have_higher_than_11mbit);
|
||||
&have_higher_than_11mbit, NULL);
|
||||
if (!presp)
|
||||
return;
|
||||
|
||||
@ -317,11 +331,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
else
|
||||
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
|
||||
|
||||
ieee80211_set_wmm_default(sdata, true);
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = true;
|
||||
sdata->vif.bss_conf.ibss_creator = creator;
|
||||
ieee80211_bss_info_change_notify(sdata, bss_change);
|
||||
|
||||
ieee80211_set_wmm_default(sdata, true);
|
||||
err = drv_join_ibss(local, sdata);
|
||||
if (err) {
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
RCU_INIT_POINTER(ifibss->presp, NULL);
|
||||
kfree_rcu(presp, rcu_head);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
|
||||
err);
|
||||
return;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, bss_change);
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_JOINED;
|
||||
mod_timer(&ifibss->timer,
|
||||
@ -416,6 +445,169 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
tsf, false);
|
||||
}
|
||||
|
||||
static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int freq;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
u8 *pos;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + hdr_len +
|
||||
5 + /* channel switch announcement element */
|
||||
3); /* secondary channel offset element */
|
||||
if (!skb)
|
||||
return -1;
|
||||
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_ACTION);
|
||||
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
|
||||
mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
|
||||
pos = skb_put(skb, 5);
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
|
||||
*pos++ = 3; /* IE length */
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
|
||||
freq = csa_settings->chandef.chan->center_freq;
|
||||
*pos++ = ieee80211_frequency_to_channel(freq); /* channel */
|
||||
*pos++ = csa_settings->count; /* count */
|
||||
|
||||
if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
|
||||
enum nl80211_channel_type ch_type;
|
||||
|
||||
skb_put(skb, 3);
|
||||
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
|
||||
*pos++ = 1; /* IE length */
|
||||
ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
|
||||
if (ch_type == NL80211_CHAN_HT40PLUS)
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
else
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
}
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct beacon_data *presp, *old_presp;
|
||||
struct cfg80211_bss *cbss;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
u16 capability;
|
||||
u64 tsf;
|
||||
int ret = 0;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (WARN_ON(!cbss)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(cbss->ies);
|
||||
tsf = ies->tsf;
|
||||
rcu_read_unlock();
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
|
||||
old_presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
|
||||
presp = ieee80211_ibss_build_presp(sdata,
|
||||
sdata->vif.bss_conf.beacon_int,
|
||||
sdata->vif.bss_conf.basic_rates,
|
||||
capability, tsf, &ifibss->chandef,
|
||||
NULL, csa_settings);
|
||||
if (!presp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(ifibss->presp, presp);
|
||||
if (old_presp)
|
||||
kfree_rcu(old_presp, rcu_head);
|
||||
|
||||
/* it might not send the beacon for a while. send an action frame
|
||||
* immediately to announce the channel switch.
|
||||
*/
|
||||
if (csa_settings)
|
||||
ieee80211_send_action_csa(sdata, csa_settings);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct cfg80211_bss *cbss;
|
||||
int err;
|
||||
u16 capability;
|
||||
|
||||
sdata_lock(sdata);
|
||||
/* update cfg80211 bss information with the new channel */
|
||||
if (!is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
|
||||
ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
/* XXX: should not really modify cfg80211 data */
|
||||
if (cbss) {
|
||||
cbss->channel = sdata->local->csa_chandef.chan;
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->chandef = sdata->local->csa_chandef;
|
||||
|
||||
/* generate the beacon */
|
||||
err = ieee80211_ibss_csa_beacon(sdata, NULL);
|
||||
sdata_unlock(sdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
cancel_work_sync(&ifibss->csa_connection_drop_work);
|
||||
}
|
||||
|
||||
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
|
||||
__acquires(RCU)
|
||||
{
|
||||
@ -499,6 +691,295 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
|
||||
return ieee80211_ibss_finish_sta(sta);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int active = 0;
|
||||
struct sta_info *sta;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sta->sdata == sdata &&
|
||||
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
|
||||
jiffies)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct cfg80211_bss *cbss;
|
||||
struct beacon_data *presp;
|
||||
struct sta_info *sta;
|
||||
int active_ibss;
|
||||
u16 capability;
|
||||
|
||||
active_ibss = ieee80211_sta_active_ibss(sdata);
|
||||
|
||||
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (cbss) {
|
||||
cfg80211_unlink_bss(local->hw.wiphy, cbss);
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
|
||||
sta_info_flush(sdata);
|
||||
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
while (!list_empty(&ifibss->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifibss->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
sta_info_free(local, sta);
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
|
||||
/* remove beacon */
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
|
||||
if (presp)
|
||||
kfree_rcu(presp, rcu_head);
|
||||
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
|
||||
BSS_CHANGED_IBSS);
|
||||
drv_leave_ibss(local, sdata);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.ibss.csa_connection_drop_work);
|
||||
|
||||
ieee80211_ibss_disconnect(sdata);
|
||||
synchronize_rcu();
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
/* trigger a scan to find another IBSS network to join */
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
bool beacon)
|
||||
{
|
||||
struct cfg80211_csa_settings params;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum nl80211_channel_type ch_type;
|
||||
int err, num_chanctx;
|
||||
u32 sta_flags;
|
||||
u8 mode;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
return true;
|
||||
|
||||
if (!sdata->vif.bss_conf.ibss_joined)
|
||||
return false;
|
||||
|
||||
sta_flags = IEEE80211_STA_DISABLE_VHT;
|
||||
switch (ifibss->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_HT;
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
|
||||
ifibss->chandef.chan->band,
|
||||
sta_flags, ifibss->bssid,
|
||||
¶ms.count, &mode,
|
||||
¶ms.chandef);
|
||||
|
||||
/* can't switch to destination channel, fail */
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
|
||||
/* did not contain a CSA */
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
if (ifibss->chandef.chan->band != params.chandef.chan->band)
|
||||
goto disconnect;
|
||||
|
||||
switch (ifibss->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
/* keep our current HT mode (HT20/HT40+/HT40-), even if
|
||||
* another mode has been announced. The mode is not adopted
|
||||
* within the beacon while doing CSA and we should therefore
|
||||
* keep the mode which we announce.
|
||||
*/
|
||||
ch_type = cfg80211_get_chandef_type(&ifibss->chandef);
|
||||
cfg80211_chandef_create(¶ms.chandef, params.chandef.chan,
|
||||
ch_type);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
if (params.chandef.width != ifibss->chandef.width) {
|
||||
sdata_info(sdata,
|
||||
"IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifibss->bssid,
|
||||
params.chandef.chan->center_freq,
|
||||
params.chandef.width,
|
||||
params.chandef.center_freq1,
|
||||
params.chandef.center_freq2);
|
||||
goto disconnect;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* should not happen, sta_flags should prevent VHT modes. */
|
||||
WARN_ON(1);
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifibss->bssid,
|
||||
params.chandef.chan->center_freq,
|
||||
params.chandef.width,
|
||||
params.chandef.center_freq1,
|
||||
params.chandef.center_freq2);
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
|
||||
¶ms.chandef);
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
if (err) {
|
||||
params.radar_required = true;
|
||||
|
||||
/* TODO: IBSS-DFS not (yet) supported, disconnect. */
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
if (chanctx->refcount > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* all checks done, now perform the channel switch. */
|
||||
ibss_dbg(sdata,
|
||||
"received channel switch announcement to go to channel %d MHz\n",
|
||||
params.chandef.chan->center_freq);
|
||||
|
||||
params.block_tx = !!mode;
|
||||
|
||||
ieee80211_ibss_csa_beacon(sdata, ¶ms);
|
||||
sdata->csa_radar_required = params.radar_required;
|
||||
|
||||
if (params.block_tx)
|
||||
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
sdata->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 true;
|
||||
disconnect:
|
||||
ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifibss->csa_connection_drop_work);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len,
|
||||
struct ieee80211_rx_status *rx_status,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
int required_len;
|
||||
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
return;
|
||||
|
||||
/* CSA is the only action we handle for now */
|
||||
if (mgmt->u.action.u.measurement.action_code !=
|
||||
WLAN_ACTION_SPCT_CHL_SWITCH)
|
||||
return;
|
||||
|
||||
required_len = IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
if (len < required_len)
|
||||
return;
|
||||
|
||||
ieee80211_ibss_process_chanswitch(sdata, elems, false);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
@ -661,10 +1142,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* check if we need to merge IBSS */
|
||||
|
||||
/* we use a fixed BSSID */
|
||||
if (sdata->u.ibss.fixed_bssid)
|
||||
goto put_bss;
|
||||
|
||||
/* not an IBSS */
|
||||
if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
|
||||
goto put_bss;
|
||||
@ -680,10 +1157,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->u.ibss.ssid_len))
|
||||
goto put_bss;
|
||||
|
||||
/* process channel switch */
|
||||
if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
|
||||
goto put_bss;
|
||||
|
||||
/* same BSSID */
|
||||
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
|
||||
goto put_bss;
|
||||
|
||||
/* we use a fixed BSSID */
|
||||
if (sdata->u.ibss.fixed_bssid)
|
||||
goto put_bss;
|
||||
|
||||
if (ieee80211_have_rx_timestamp(rx_status)) {
|
||||
/* time when timestamp field was received */
|
||||
rx_timestamp =
|
||||
@ -775,30 +1260,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int active = 0;
|
||||
struct sta_info *sta;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sta->sdata == sdata &&
|
||||
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
|
||||
jiffies)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@ -1076,6 +1537,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_rx_status *rx_status;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
u16 fc;
|
||||
struct ieee802_11_elems elems;
|
||||
int ies_len;
|
||||
|
||||
rx_status = IEEE80211_SKB_RXCB(skb);
|
||||
mgmt = (struct ieee80211_mgmt *) skb->data;
|
||||
@ -1101,6 +1564,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
case IEEE80211_STYPE_DEAUTH:
|
||||
ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
|
||||
break;
|
||||
case IEEE80211_STYPE_ACTION:
|
||||
switch (mgmt->u.action.category) {
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
ies_len = skb->len -
|
||||
offsetof(struct ieee80211_mgmt,
|
||||
u.action.u.chan_switch.variable);
|
||||
|
||||
if (ies_len < 0)
|
||||
break;
|
||||
|
||||
ieee802_11_parse_elems(
|
||||
mgmt->u.action.u.chan_switch.variable,
|
||||
ies_len, true, &elems);
|
||||
|
||||
if (elems.parse_error)
|
||||
break;
|
||||
|
||||
ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
|
||||
rx_status, &elems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mgmt_out:
|
||||
@ -1167,6 +1651,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
(unsigned long) sdata);
|
||||
INIT_LIST_HEAD(&ifibss->incomplete_stations);
|
||||
spin_lock_init(&ifibss->incomplete_lock);
|
||||
INIT_WORK(&ifibss->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
}
|
||||
|
||||
/* scan finished notification */
|
||||
@ -1265,73 +1751,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct cfg80211_bss *cbss;
|
||||
u16 capability;
|
||||
int active_ibss;
|
||||
struct sta_info *sta;
|
||||
struct beacon_data *presp;
|
||||
|
||||
active_ibss = ieee80211_sta_active_ibss(sdata);
|
||||
|
||||
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (cbss) {
|
||||
cfg80211_unlink_bss(local->hw.wiphy, cbss);
|
||||
cfg80211_put_bss(local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
memset(ifibss->bssid, 0, ETH_ALEN);
|
||||
ieee80211_ibss_disconnect(sdata);
|
||||
ifibss->ssid_len = 0;
|
||||
|
||||
sta_info_flush(sdata);
|
||||
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
while (!list_empty(&ifibss->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifibss->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
sta_info_free(local, sta);
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
memset(ifibss->bssid, 0, ETH_ALEN);
|
||||
|
||||
/* remove beacon */
|
||||
kfree(sdata->u.ibss.ie);
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
|
||||
|
||||
/* on the next join, re-program HT parameters */
|
||||
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
|
||||
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
|
||||
BSS_CHANGED_IBSS);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
synchronize_rcu();
|
||||
kfree(presp);
|
||||
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
|
@ -322,7 +322,6 @@ struct ieee80211_roc_work {
|
||||
|
||||
/* flags used in struct ieee80211_if_managed.flags */
|
||||
enum ieee80211_sta_flags {
|
||||
IEEE80211_STA_BEACON_POLL = BIT(0),
|
||||
IEEE80211_STA_CONNECTION_POLL = BIT(1),
|
||||
IEEE80211_STA_CONTROL_PORT = BIT(2),
|
||||
IEEE80211_STA_DISABLE_HT = BIT(4),
|
||||
@ -487,6 +486,7 @@ struct ieee80211_if_managed {
|
||||
|
||||
struct ieee80211_if_ibss {
|
||||
struct timer_list timer;
|
||||
struct work_struct csa_connection_drop_work;
|
||||
|
||||
unsigned long last_scan_completed;
|
||||
|
||||
@ -1330,6 +1330,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings);
|
||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* mesh code */
|
||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||
@ -1481,6 +1485,29 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
/**
|
||||
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
|
||||
* @sdata: the sdata of the interface which has received the frame
|
||||
* @elems: parsed 802.11 elements received with the frame
|
||||
* @beacon: indicates if the frame was a beacon or probe response
|
||||
* @current_band: indicates the current band
|
||||
* @sta_flags: contains information about own capabilities and restrictions
|
||||
* to decide which channel switch announcements can be accepted. Only the
|
||||
* following subset of &enum ieee80211_sta_flags are evaluated:
|
||||
* %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
|
||||
* %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
|
||||
* %IEEE80211_STA_DISABLE_160MHZ.
|
||||
* @count: to be filled with the counter until the switch (on success only)
|
||||
* @bssid: the currently connected bssid (for reporting)
|
||||
* @mode: to be filled with CSA mode (on success only)
|
||||
* @new_chandef: to be filled with destination chandef (on success only)
|
||||
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
|
||||
*/
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef);
|
||||
|
||||
/* Suspend/resume and hw reconfiguration */
|
||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
@ -1654,6 +1681,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
|
||||
|
||||
int __must_check
|
||||
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -766,6 +766,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
ieee80211_mgd_stop(sdata);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
ieee80211_ibss_stop(sdata);
|
||||
|
||||
|
||||
/*
|
||||
* Remove all stations associated with this interface.
|
||||
*
|
||||
|
@ -879,7 +879,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
|
||||
keyconf->keylen, keyconf->key,
|
||||
0, NULL);
|
||||
if (IS_ERR(key))
|
||||
return ERR_PTR(PTR_ERR(key));
|
||||
return ERR_CAST(key);
|
||||
|
||||
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
|
||||
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
||||
|
@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
|
||||
return (1 << ecw) - 1;
|
||||
}
|
||||
|
||||
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
{
|
||||
u32 ret;
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
c->width = NL80211_CHAN_WIDTH_20;
|
||||
c->center_freq1 = c->chan->center_freq;
|
||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_40;
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
c->center_freq2 = 0;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P80 */
|
||||
tmp /= 4;
|
||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
default:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
WARN_ON_ONCE(1);
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
WARN_ON_ONCE(1);
|
||||
/* keep c->width */
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32
|
||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
@ -352,7 +292,7 @@ out:
|
||||
break;
|
||||
}
|
||||
|
||||
ret |= chandef_downgrade(chandef);
|
||||
ret |= ieee80211_chandef_downgrade(chandef);
|
||||
}
|
||||
|
||||
if (chandef->width != vht_chandef.width && !tracking)
|
||||
@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
|
||||
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
||||
return 0;
|
||||
@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
@ -937,6 +876,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u32 changed = 0;
|
||||
int ret;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
if (!ifmgd->associated)
|
||||
goto out;
|
||||
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
|
||||
&changed);
|
||||
if (ret) {
|
||||
sdata_info(sdata,
|
||||
"vif channel switch failed, disconnecting\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!local->ops->channel_switch) {
|
||||
/* call "hw_config" only if doing sw channel switch */
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
||||
} else {
|
||||
/* update the device channel directly */
|
||||
local->hw.conf.chandef = local->_oper_chandef;
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
/* Call "hw_config" only if doing sw channel switch.
|
||||
* Otherwise update the channel directly
|
||||
*/
|
||||
if (!local->ops->channel_switch)
|
||||
ieee80211_hw_config(local, 0);
|
||||
else
|
||||
local->hw.conf.chandef = local->_oper_chandef;
|
||||
}
|
||||
|
||||
/* XXX: shouldn't really modify cfg80211-owned data! */
|
||||
ifmgd->associated->channel = local->_oper_chandef.chan;
|
||||
ifmgd->associated->channel = local->csa_chandef.chan;
|
||||
|
||||
/* XXX: wait for a beacon first? */
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
out:
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
@ -1000,20 +956,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct cfg80211_bss *cbss = ifmgd->associated;
|
||||
struct ieee80211_bss *bss;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum ieee80211_band new_band;
|
||||
int new_freq;
|
||||
u8 new_chan_no;
|
||||
enum ieee80211_band current_band;
|
||||
u8 count;
|
||||
u8 mode;
|
||||
struct ieee80211_channel *new_chan;
|
||||
struct cfg80211_chan_def new_chandef = {};
|
||||
struct cfg80211_chan_def new_vht_chandef = {};
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
int secondary_channel_offset = -1;
|
||||
int res;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -1027,162 +975,23 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
||||
return;
|
||||
|
||||
sec_chan_offs = elems->sec_chan_offs;
|
||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||
ht_oper = elems->ht_operation;
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
sec_chan_offs = NULL;
|
||||
wide_bw_chansw_ie = NULL;
|
||||
/* only used for bandwidth here */
|
||||
ht_oper = NULL;
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
||||
wide_bw_chansw_ie = NULL;
|
||||
|
||||
if (elems->ext_chansw_ie) {
|
||||
if (!ieee80211_operating_class_to_band(
|
||||
elems->ext_chansw_ie->new_operating_class,
|
||||
&new_band)) {
|
||||
sdata_info(sdata,
|
||||
"cannot understand ECSA IE operating class %d, disconnecting\n",
|
||||
elems->ext_chansw_ie->new_operating_class);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
}
|
||||
new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
count = elems->ext_chansw_ie->count;
|
||||
mode = elems->ext_chansw_ie->mode;
|
||||
} else if (elems->ch_switch_ie) {
|
||||
new_band = cbss->channel->band;
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
count = elems->ch_switch_ie->count;
|
||||
mode = elems->ch_switch_ie->mode;
|
||||
} else {
|
||||
/* nothing here we understand */
|
||||
return;
|
||||
}
|
||||
|
||||
bss = (void *)cbss->priv;
|
||||
|
||||
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid, new_freq);
|
||||
current_band = cbss->channel->band;
|
||||
res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
|
||||
ifmgd->flags,
|
||||
ifmgd->associated->bssid, &count,
|
||||
&mode, &new_chandef);
|
||||
if (res < 0)
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
if (res)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!beacon && sec_chan_offs) {
|
||||
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
||||
} else if (beacon && ht_oper) {
|
||||
secondary_channel_offset =
|
||||
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
||||
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
||||
/*
|
||||
* If it's not a beacon, HT is enabled and the IE not present,
|
||||
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
||||
* This element [the Secondary Channel Offset Element] is
|
||||
* present when switching to a 40 MHz channel. It may be
|
||||
* present when switching to a 20 MHz channel (in which
|
||||
* case the secondary channel offset is set to SCN).
|
||||
*/
|
||||
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
}
|
||||
|
||||
switch (secondary_channel_offset) {
|
||||
default:
|
||||
/* secondary_channel_offset was present but is invalid */
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT20);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40PLUS);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40MINUS);
|
||||
break;
|
||||
case -1:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
/* keep width for 5/10 MHz channels */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
new_chandef.width = sdata->vif.bss_conf.chandef.width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wide_bw_chansw_ie) {
|
||||
new_vht_chandef.chan = new_chan;
|
||||
new_vht_chandef.center_freq1 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
new_band);
|
||||
|
||||
switch (wide_bw_chansw_ie->new_channel_width) {
|
||||
default:
|
||||
/* hmmm, ignore VHT and use HT if present */
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
new_vht_chandef.chan = NULL;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
/* field is otherwise reserved */
|
||||
new_vht_chandef.center_freq2 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg1,
|
||||
new_band);
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
||||
break;
|
||||
}
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
}
|
||||
|
||||
/* if VHT data is there validate & use it */
|
||||
if (new_vht_chandef.chan) {
|
||||
if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
&new_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM CSA has inconsistent channel data, disconnecting\n",
|
||||
ifmgd->associated->bssid);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
new_chandef = new_vht_chandef;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid, new_freq,
|
||||
ifmgd->associated->bssid,
|
||||
new_chandef.chan->center_freq,
|
||||
new_chandef.width, new_chandef.center_freq1,
|
||||
new_chandef.center_freq2);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
@ -1191,17 +1000,28 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
if (local->use_chanctx) {
|
||||
sdata_info(sdata,
|
||||
"not handling channel switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
if (local->use_chanctx) {
|
||||
u32 num_chanctx = 0;
|
||||
list_for_each_entry(chanctx, &local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1 ||
|
||||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
||||
sdata_info(sdata,
|
||||
"not handling chan-switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return;
|
||||
}
|
||||
@ -1374,8 +1194,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
|
||||
if (!mgd->associated)
|
||||
return false;
|
||||
|
||||
if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
return false;
|
||||
|
||||
if (!mgd->have_beacon)
|
||||
@ -1691,8 +1510,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
lockdep_assert_held(&sdata->local->mtx);
|
||||
|
||||
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
|
||||
IEEE80211_STA_BEACON_POLL);
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
|
||||
ieee80211_run_deferred_scan(sdata->local);
|
||||
}
|
||||
|
||||
@ -1954,11 +1772,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))) {
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
|
||||
goto out;
|
||||
|
||||
__ieee80211_stop_poll(sdata);
|
||||
|
||||
@ -2094,15 +1909,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
||||
* because otherwise we would reset the timer every time and
|
||||
* never check whether we received a probe response!
|
||||
*/
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
already = true;
|
||||
|
||||
if (beacon)
|
||||
ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
|
||||
else
|
||||
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
|
||||
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
|
||||
if (already)
|
||||
@ -2174,6 +1983,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
true, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
sdata->vif.csa_active = false;
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -3061,17 +2871,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
|
||||
mlme_dbg_ratelimited(sdata,
|
||||
"cancelling AP probe due to a received beacon\n");
|
||||
mutex_lock(&local->mtx);
|
||||
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
|
||||
ieee80211_run_deferred_scan(local);
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
ieee80211_reset_ap_probe(sdata);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3543,8 +3346,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
||||
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
|
||||
run_again(sdata, ifmgd->assoc_data->timeout);
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL) &&
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
|
||||
ifmgd->associated) {
|
||||
u8 bssid[ETH_ALEN];
|
||||
int max_tries;
|
||||
@ -3876,7 +3678,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
return ret;
|
||||
|
||||
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
||||
ifmgd->flags |= chandef_downgrade(&chandef);
|
||||
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
}
|
||||
|
@ -203,6 +203,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
|
||||
memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
|
||||
mi->max_prob_rate = tmp_prob_rate;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate[0] = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate[1] = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reset update timer */
|
||||
mi->stats_update = jiffies;
|
||||
|
||||
@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
|
||||
/* increase sum packet counter */
|
||||
mi->packet_count++;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
if (mp->fixed_rate_idx != -1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
delta = (mi->packet_count * sampling_ratio / 100) -
|
||||
(mi->sample_count + mi->sample_deferred / 2);
|
||||
|
||||
|
@ -365,6 +365,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate2 = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
}
|
||||
#endif
|
||||
|
||||
mi->stats_update = jiffies;
|
||||
}
|
||||
@ -774,6 +782,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
info->flags |= mi->tx_flags;
|
||||
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
if (mp->fixed_rate_idx != -1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Don't use EAPOL frames for sampling on non-mrr hw */
|
||||
if (mp->hw->max_rates == 1 &&
|
||||
(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
|
||||
@ -781,16 +794,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
else
|
||||
sample_idx = minstrel_get_sample_rate(mp, mi);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate2 = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
sample_idx = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
mi->total_packets++;
|
||||
|
||||
/* wraparound */
|
||||
|
@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
|
||||
* provide large enough buffers. */
|
||||
length = length < RC_PID_PRINT_BUF_SIZE ?
|
||||
length : RC_PID_PRINT_BUF_SIZE;
|
||||
p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
|
||||
p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
|
||||
switch (ev->type) {
|
||||
case RC_PID_EVENT_TYPE_TX_STATUS:
|
||||
p += snprintf(pb + p, length - p, "tx_status %u %u",
|
||||
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
|
||||
ev->data.tx_status.status.rates[0].idx);
|
||||
p += scnprintf(pb + p, length - p, "tx_status %u %u",
|
||||
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
|
||||
ev->data.tx_status.status.rates[0].idx);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_RATE_CHANGE:
|
||||
p += snprintf(pb + p, length - p, "rate_change %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
p += scnprintf(pb + p, length - p, "rate_change %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_TX_RATE:
|
||||
p += snprintf(pb + p, length - p, "tx_rate %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
p += scnprintf(pb + p, length - p, "tx_rate %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_PF_SAMPLE:
|
||||
p += snprintf(pb + p, length - p,
|
||||
"pf_sample %d %d %d %d",
|
||||
ev->data.pf_sample, ev->data.prop_err,
|
||||
ev->data.int_err, ev->data.der_err);
|
||||
p += scnprintf(pb + p, length - p,
|
||||
"pf_sample %d %d %d %d",
|
||||
ev->data.pf_sample, ev->data.prop_err,
|
||||
ev->data.int_err, ev->data.der_err);
|
||||
break;
|
||||
}
|
||||
p += snprintf(pb + p, length - p, "\n");
|
||||
p += scnprintf(pb + p, length - p, "\n");
|
||||
|
||||
spin_unlock_irqrestore(&events->lock, status);
|
||||
|
||||
|
@ -995,8 +995,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
rx->sta->num_duplicates++;
|
||||
}
|
||||
return RX_DROP_UNUSABLE;
|
||||
} else
|
||||
} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
|
||||
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(rx->skb->len < 16)) {
|
||||
@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
|
||||
@ -2566,31 +2568,46 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
|
||||
goto queue;
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
if (status->band != IEEE80211_BAND_5GHZ)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
break;
|
||||
|
||||
/* verify action_code is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
break;
|
||||
|
||||
switch (mgmt->u.action.u.measurement.action_code) {
|
||||
case WLAN_ACTION_SPCT_MSR_REQ:
|
||||
if (status->band != IEEE80211_BAND_5GHZ)
|
||||
break;
|
||||
|
||||
if (len < (IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.measurement)))
|
||||
break;
|
||||
ieee80211_process_measurement_req(sdata, mgmt, len);
|
||||
goto handled;
|
||||
case WLAN_ACTION_SPCT_CHL_SWITCH:
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
break;
|
||||
|
||||
if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
|
||||
ieee80211_process_measurement_req(sdata, mgmt, len);
|
||||
goto handled;
|
||||
case WLAN_ACTION_SPCT_CHL_SWITCH: {
|
||||
u8 *bssid;
|
||||
if (len < (IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.chan_switch)))
|
||||
break;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
bssid = sdata->u.mgd.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
bssid = sdata->u.ibss.bssid;
|
||||
else
|
||||
break;
|
||||
|
||||
if (!ether_addr_equal(mgmt->bssid, bssid))
|
||||
break;
|
||||
|
||||
goto queue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WLAN_CATEGORY_SA_QUERY:
|
||||
|
@ -391,8 +391,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
|
||||
return false;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -21,6 +21,168 @@
|
||||
#include "sta_info.h"
|
||||
#include "wme.h"
|
||||
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef)
|
||||
{
|
||||
enum ieee80211_band new_band;
|
||||
int new_freq;
|
||||
u8 new_chan_no;
|
||||
struct ieee80211_channel *new_chan;
|
||||
struct cfg80211_chan_def new_vht_chandef = {};
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
int secondary_channel_offset = -1;
|
||||
|
||||
sec_chan_offs = elems->sec_chan_offs;
|
||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||
ht_oper = elems->ht_operation;
|
||||
|
||||
if (sta_flags & (IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
sec_chan_offs = NULL;
|
||||
wide_bw_chansw_ie = NULL;
|
||||
/* only used for bandwidth here */
|
||||
ht_oper = NULL;
|
||||
}
|
||||
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_VHT)
|
||||
wide_bw_chansw_ie = NULL;
|
||||
|
||||
if (elems->ext_chansw_ie) {
|
||||
if (!ieee80211_operating_class_to_band(
|
||||
elems->ext_chansw_ie->new_operating_class,
|
||||
&new_band)) {
|
||||
sdata_info(sdata,
|
||||
"cannot understand ECSA IE operating class %d, disconnecting\n",
|
||||
elems->ext_chansw_ie->new_operating_class);
|
||||
return -EINVAL;
|
||||
}
|
||||
new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
*count = elems->ext_chansw_ie->count;
|
||||
*mode = elems->ext_chansw_ie->mode;
|
||||
} else if (elems->ch_switch_ie) {
|
||||
new_band = current_band;
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
*count = elems->ch_switch_ie->count;
|
||||
*mode = elems->ch_switch_ie->mode;
|
||||
} else {
|
||||
/* nothing here we understand */
|
||||
return 1;
|
||||
}
|
||||
|
||||
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||
bssid, new_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!beacon && sec_chan_offs) {
|
||||
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
||||
} else if (beacon && ht_oper) {
|
||||
secondary_channel_offset =
|
||||
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
||||
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
|
||||
/* If it's not a beacon, HT is enabled and the IE not present,
|
||||
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
||||
* This element [the Secondary Channel Offset Element] is
|
||||
* present when switching to a 40 MHz channel. It may be
|
||||
* present when switching to a 20 MHz channel (in which
|
||||
* case the secondary channel offset is set to SCN).
|
||||
*/
|
||||
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
}
|
||||
|
||||
switch (secondary_channel_offset) {
|
||||
default:
|
||||
/* secondary_channel_offset was present but is invalid */
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT20);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40PLUS);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40MINUS);
|
||||
break;
|
||||
case -1:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
/* keep width for 5/10 MHz channels */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
new_chandef->width = sdata->vif.bss_conf.chandef.width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wide_bw_chansw_ie) {
|
||||
new_vht_chandef.chan = new_chan;
|
||||
new_vht_chandef.center_freq1 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
new_band);
|
||||
|
||||
switch (wide_bw_chansw_ie->new_channel_width) {
|
||||
default:
|
||||
/* hmmm, ignore VHT and use HT if present */
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
new_vht_chandef.chan = NULL;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
/* field is otherwise reserved */
|
||||
new_vht_chandef.center_freq2 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg1,
|
||||
new_band);
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
||||
break;
|
||||
}
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
}
|
||||
|
||||
/* if VHT data is there validate & use it */
|
||||
if (new_vht_chandef.chan) {
|
||||
if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
new_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
|
||||
bssid);
|
||||
return -EINVAL;
|
||||
}
|
||||
*new_chandef = new_vht_chandef;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_msrment_ie *request_ie,
|
||||
const u8 *da, const u8 *bssid,
|
||||
|
@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
|
||||
);
|
||||
#endif
|
||||
|
||||
TRACE_EVENT(drv_join_ibss,
|
||||
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);
|
||||
),
|
||||
|
||||
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);
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracing for API calls that drivers call.
|
||||
*/
|
||||
|
@ -1981,7 +1981,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
* EAPOL frames from the local station.
|
||||
*/
|
||||
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
!is_multicast_ether_addr(hdr.addr1) && !authorized &&
|
||||
!multicast && !authorized &&
|
||||
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
@ -2357,15 +2357,31 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct probe_resp *resp;
|
||||
int counter_offset_beacon = sdata->csa_counter_offset_beacon;
|
||||
int counter_offset_presp = sdata->csa_counter_offset_presp;
|
||||
u8 *beacon_data;
|
||||
size_t beacon_data_len;
|
||||
|
||||
/* warn if the driver did not check for/react to csa completeness */
|
||||
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
beacon_data = beacon->tail;
|
||||
beacon_data_len = beacon->tail_len;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
|
||||
return;
|
||||
|
||||
((u8 *)beacon->tail)[counter_offset_beacon]--;
|
||||
/* warn if the driver did not check for/react to csa completeness */
|
||||
if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
|
||||
return;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
||||
counter_offset_presp) {
|
||||
beacon_data[counter_offset_beacon]--;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
|
||||
rcu_read_lock();
|
||||
resp = rcu_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
@ -2400,6 +2416,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
||||
goto out;
|
||||
beacon_data = beacon->tail;
|
||||
beacon_data_len = beacon->tail_len;
|
||||
} else if (vif->type == NL80211_IFTYPE_ADHOC) {
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
beacon = rcu_dereference(ifibss->presp);
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
@ -2484,6 +2509,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
if (!presp)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
ieee80211_update_csa(sdata, presp);
|
||||
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
@ -567,58 +567,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
||||
}
|
||||
|
||||
void ieee80211_iterate_active_interfaces(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
static void __iterate_active_interfaces(struct ieee80211_local *local,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
|
||||
continue;
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
continue;
|
||||
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_protected(local->monitor_sdata,
|
||||
lockdep_is_held(&local->iflist_mtx));
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_atomic(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
@ -638,16 +594,57 @@ void ieee80211_iterate_active_interfaces_atomic(
|
||||
&sdata->vif);
|
||||
}
|
||||
|
||||
sdata = rcu_dereference(local->monitor_sdata);
|
||||
sdata = rcu_dereference_check(local->monitor_sdata,
|
||||
lockdep_is_held(&local->iflist_mtx) ||
|
||||
lockdep_rtnl_is_held());
|
||||
if (sdata &&
|
||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||
}
|
||||
|
||||
void ieee80211_iterate_active_interfaces(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_atomic(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
rcu_read_lock();
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_rtnl(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
||||
|
||||
/*
|
||||
* Nothing should have been stuffed into the workqueue during
|
||||
* the suspend->resume cycle. If this WARN is seen then there
|
||||
@ -1007,14 +1004,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||
aCWmax = 1023;
|
||||
if (use_11b)
|
||||
aCWmin = 31;
|
||||
else
|
||||
aCWmin = 15;
|
||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||
aCWmax = 1023;
|
||||
if (use_11b)
|
||||
aCWmin = 31;
|
||||
else
|
||||
aCWmin = 15;
|
||||
|
||||
/* Confiure old 802.11b/g medium access rules. */
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 2;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
/* Update if QoS is enabled. */
|
||||
if (enable_qos) {
|
||||
switch (ac) {
|
||||
case IEEE80211_AC_BK:
|
||||
@ -1050,12 +1054,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
qparam.aifs = 2;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Confiure old 802.11b/g medium access rules. */
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 2;
|
||||
}
|
||||
|
||||
qparam.uapsd = false;
|
||||
@ -1084,8 +1082,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
int err;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
sizeof(*mgmt) + 6 + extra_len);
|
||||
/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
@ -2295,3 +2293,63 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
|
||||
ieee80211_queue_work(hw, &local->radar_detected_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_radar_detected);
|
||||
|
||||
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
{
|
||||
u32 ret;
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
c->width = NL80211_CHAN_WIDTH_20;
|
||||
c->center_freq1 = c->chan->center_freq;
|
||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_40;
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
c->center_freq2 = 0;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P80 */
|
||||
tmp /= 4;
|
||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
default:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
WARN_ON_ONCE(1);
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
WARN_ON_ONCE(1);
|
||||
/* keep c->width */
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -185,13 +185,13 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
|
||||
vht_cap->cap |= cap_info &
|
||||
(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
|
||||
IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
|
||||
IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
|
||||
}
|
||||
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
|
||||
vht_cap->cap |= cap_info &
|
||||
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
||||
(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||
IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX);
|
||||
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
|
||||
vht_cap->cap |= cap_info &
|
||||
|
@ -328,6 +328,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
|
||||
width);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
|
||||
|
||||
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
|
@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
enum cfg80211_chan_mode chanmode,
|
||||
u8 radar_detect);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_required - checks if radar detection is required
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
|
||||
*/
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *c);
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state);
|
||||
|
@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan,
|
||||
return 0;
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return snprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d Disabled\n",
|
||||
chan->center_freq);
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d Disabled\n",
|
||||
chan->center_freq);
|
||||
|
||||
return snprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d HT40 %c%c\n",
|
||||
chan->center_freq,
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+');
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d HT40 %c%c\n",
|
||||
chan->center_freq,
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
|
||||
' ' : '-',
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
|
||||
' ' : '+');
|
||||
}
|
||||
|
||||
static ssize_t ht40allow_map_read(struct file *file,
|
||||
|
@ -46,6 +46,12 @@ BEGIN {
|
||||
sub(/:/, "", country)
|
||||
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
|
||||
printf "\t.alpha2 = \"%s\",\n", country
|
||||
if ($NF ~ /DFS-ETSI/)
|
||||
printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
|
||||
else if ($NF ~ /DFS-FCC/)
|
||||
printf "\t.dfs_region = NL80211_DFS_FCC,\n"
|
||||
else if ($NF ~ /DFS-JP/)
|
||||
printf "\t.dfs_region = NL80211_DFS_JP,\n"
|
||||
printf "\t.reg_rules = {\n"
|
||||
active = 1
|
||||
regdb = regdb "\t®dom_" country ",\n"
|
||||
|
@ -5591,6 +5591,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (netif_carrier_ok(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (wdev->cac_started)
|
||||
return -EBUSY;
|
||||
|
||||
@ -5634,15 +5637,26 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
||||
u8 radar_detect_width = 0;
|
||||
int err;
|
||||
bool need_new_beacon = false;
|
||||
|
||||
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)
|
||||
switch (dev->ieee80211_ptr->iftype) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
need_new_beacon = true;
|
||||
|
||||
/* useless if AP is not running */
|
||||
if (!wdev->beacon_interval)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
@ -5651,15 +5665,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
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)
|
||||
if (need_new_beacon &&
|
||||
(!info->attrs[NL80211_ATTR_CSA_IES] ||
|
||||
!info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
|
||||
return -EINVAL;
|
||||
|
||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
|
||||
if (!need_new_beacon)
|
||||
goto skip_beacons;
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
||||
if (err)
|
||||
return err;
|
||||
@ -5699,6 +5714,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skip_beacons:
|
||||
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
||||
if (err)
|
||||
return err;
|
||||
@ -5706,12 +5722,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
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;
|
||||
/* DFS channels are only supported for AP/P2P GO ... for now. */
|
||||
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
|
||||
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,
|
||||
@ -10740,7 +10761,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
wdev_lock(wdev);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO))
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
||||
wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
goto out;
|
||||
|
||||
wdev->channel = chandef->chan;
|
||||
|
@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = {
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_NO_OFDM),
|
||||
/* IEEE 802.11a, channel 36..48 */
|
||||
REG_RULE(5180-10, 5240+10, 80, 6, 20,
|
||||
REG_RULE(5180-10, 5240+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS),
|
||||
|
||||
/* NB: 5260 MHz - 5700 MHz requires DFS */
|
||||
/* IEEE 802.11a, channel 52..64 - DFS required */
|
||||
REG_RULE(5260-10, 5320+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_DFS),
|
||||
|
||||
/* IEEE 802.11a, channel 100..144 - DFS required */
|
||||
REG_RULE(5500-10, 5720+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_DFS),
|
||||
|
||||
/* IEEE 802.11a, channel 149..165 */
|
||||
REG_RULE(5745-10, 5825+10, 80, 6, 20,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/dsfield.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
@ -691,6 +692,7 @@ EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
|
||||
unsigned int cfg80211_classify8021d(struct sk_buff *skb)
|
||||
{
|
||||
unsigned int dscp;
|
||||
unsigned char vlan_priority;
|
||||
|
||||
/* skb->priority values from 256->263 are magic values to
|
||||
* directly indicate a specific 802.1d priority. This is used
|
||||
@ -700,6 +702,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)
|
||||
if (skb->priority >= 256 && skb->priority <= 263)
|
||||
return skb->priority - 256;
|
||||
|
||||
if (vlan_tx_tag_present(skb)) {
|
||||
vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK)
|
||||
>> VLAN_PRIO_SHIFT;
|
||||
if (vlan_priority > 0)
|
||||
return vlan_priority;
|
||||
}
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user