Commit 66ba271a authored by John W. Linville's avatar John W. Linville
parents 2b5e54e2 6c7c4cbf
...@@ -188,6 +188,8 @@ struct ieee80211_channel { ...@@ -188,6 +188,8 @@ struct ieee80211_channel {
* when used with 802.11g (on the 2.4 GHz band); filled by the * when used with 802.11g (on the 2.4 GHz band); filled by the
* core code when registering the wiphy. * core code when registering the wiphy.
* @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode. * @IEEE80211_RATE_ERP_G: This is an ERP rate in 802.11g mode.
* @IEEE80211_RATE_SUPPORTS_5MHZ: Rate can be used in 5 MHz mode
* @IEEE80211_RATE_SUPPORTS_10MHZ: Rate can be used in 10 MHz mode
*/ */
enum ieee80211_rate_flags { enum ieee80211_rate_flags {
IEEE80211_RATE_SHORT_PREAMBLE = 1<<0, IEEE80211_RATE_SHORT_PREAMBLE = 1<<0,
...@@ -195,6 +197,8 @@ enum ieee80211_rate_flags { ...@@ -195,6 +197,8 @@ enum ieee80211_rate_flags {
IEEE80211_RATE_MANDATORY_B = 1<<2, IEEE80211_RATE_MANDATORY_B = 1<<2,
IEEE80211_RATE_MANDATORY_G = 1<<3, IEEE80211_RATE_MANDATORY_G = 1<<3,
IEEE80211_RATE_ERP_G = 1<<4, IEEE80211_RATE_ERP_G = 1<<4,
IEEE80211_RATE_SUPPORTS_5MHZ = 1<<5,
IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6,
}; };
/** /**
...@@ -432,6 +436,30 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, ...@@ -432,6 +436,30 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
u32 prohibited_flags); u32 prohibited_flags);
/**
* ieee80211_chandef_rate_flags - returns rate flags for a channel
*
* In some channel types, not all rates may be used - for example CCK
* rates may not be used in 5/10 MHz channels.
*
* @chandef: channel definition for the channel
*
* Returns: rate flags which apply for this channel
*/
static inline enum ieee80211_rate_flags
ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
return IEEE80211_RATE_SUPPORTS_5MHZ;
case NL80211_CHAN_WIDTH_10:
return IEEE80211_RATE_SUPPORTS_10MHZ;
default:
break;
}
return 0;
}
/** /**
* enum survey_info_flags - survey information flags * enum survey_info_flags - survey information flags
* *
...@@ -1431,7 +1459,8 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); ...@@ -1431,7 +1459,8 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);
* This structure provides information needed to complete IEEE 802.11 * This structure provides information needed to complete IEEE 802.11
* authentication. * authentication.
* *
* @bss: The BSS to authenticate with. * @bss: The BSS to authenticate with, the callee must obtain a reference
* to it if it needs to keep it.
* @auth_type: Authentication type (algorithm) * @auth_type: Authentication type (algorithm)
* @ie: Extra IEs to add to Authentication frame or %NULL * @ie: Extra IEs to add to Authentication frame or %NULL
* @ie_len: Length of ie buffer in octets * @ie_len: Length of ie buffer in octets
...@@ -1469,11 +1498,10 @@ enum cfg80211_assoc_req_flags { ...@@ -1469,11 +1498,10 @@ enum cfg80211_assoc_req_flags {
* *
* This structure provides information needed to complete IEEE 802.11 * This structure provides information needed to complete IEEE 802.11
* (re)association. * (re)association.
* @bss: The BSS to associate with. If the call is successful the driver * @bss: The BSS to associate with. If the call is successful the driver is
* is given a reference that it must release, normally via a call to * given a reference that it must give back to cfg80211_send_rx_assoc()
* cfg80211_send_rx_assoc(), or, if association timed out, with a * or to cfg80211_assoc_timeout(). To ensure proper refcounting, new
* call to cfg80211_put_bss() (in addition to calling * association requests while already associating must be rejected.
* cfg80211_send_assoc_timeout())
* @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
* @ie_len: Length of ie buffer in octets * @ie_len: Length of ie buffer in octets
* @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
...@@ -2342,6 +2370,7 @@ struct cfg80211_ops { ...@@ -2342,6 +2370,7 @@ struct cfg80211_ops {
* responds to probe-requests in hardware. * responds to probe-requests in hardware.
* @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX. * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
* @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call. * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
*/ */
enum wiphy_flags { enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
...@@ -2365,6 +2394,7 @@ enum wiphy_flags { ...@@ -2365,6 +2394,7 @@ enum wiphy_flags {
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19), WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19),
WIPHY_FLAG_OFFCHAN_TX = BIT(20), WIPHY_FLAG_OFFCHAN_TX = BIT(20),
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21), WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
}; };
/** /**
...@@ -3492,11 +3522,11 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, ...@@ -3492,11 +3522,11 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
/** /**
* cfg80211_assoc_timeout - notification of timed out association * cfg80211_assoc_timeout - notification of timed out association
* @dev: network device * @dev: network device
* @addr: The MAC address of the device with which the association timed out * @bss: The BSS entry with which association timed out.
* *
* This function may sleep. The caller must hold the corresponding wdev's mutex. * This function may sleep. The caller must hold the corresponding wdev's mutex.
*/ */
void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr); void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
/** /**
* cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
......
...@@ -305,6 +305,7 @@ enum ieee80211_rssi_event { ...@@ -305,6 +305,7 @@ enum ieee80211_rssi_event {
* @basic_rates: bitmap of basic rates, each bit stands for an * @basic_rates: bitmap of basic rates, each bit stands for an
* index into the rate table configured by the driver in * index into the rate table configured by the driver in
* the current band. * the current band.
* @beacon_rate: associated AP's beacon TX rate
* @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
* @bssid: The BSSID for this BSS * @bssid: The BSSID for this BSS
* @enable_beacon: whether beaconing should be enabled or not * @enable_beacon: whether beaconing should be enabled or not
...@@ -352,6 +353,7 @@ struct ieee80211_bss_conf { ...@@ -352,6 +353,7 @@ struct ieee80211_bss_conf {
u32 sync_device_ts; u32 sync_device_ts;
u8 sync_dtim_count; u8 sync_dtim_count;
u32 basic_rates; u32 basic_rates;
struct ieee80211_rate *beacon_rate;
int mcast_rate[IEEE80211_NUM_BANDS]; int mcast_rate[IEEE80211_NUM_BANDS];
u16 ht_operation_mode; u16 ht_operation_mode;
s32 cqm_rssi_thold; s32 cqm_rssi_thold;
......
...@@ -2758,6 +2758,8 @@ enum nl80211_channel_type { ...@@ -2758,6 +2758,8 @@ enum nl80211_channel_type {
* and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
* @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
* attribute must be provided as well * attribute must be provided as well
* @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
* @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
*/ */
enum nl80211_chan_width { enum nl80211_chan_width {
NL80211_CHAN_WIDTH_20_NOHT, NL80211_CHAN_WIDTH_20_NOHT,
...@@ -2766,6 +2768,8 @@ enum nl80211_chan_width { ...@@ -2766,6 +2768,8 @@ enum nl80211_chan_width {
NL80211_CHAN_WIDTH_80, NL80211_CHAN_WIDTH_80,
NL80211_CHAN_WIDTH_80P80, NL80211_CHAN_WIDTH_80P80,
NL80211_CHAN_WIDTH_160, NL80211_CHAN_WIDTH_160,
NL80211_CHAN_WIDTH_5,
NL80211_CHAN_WIDTH_10,
}; };
/** /**
......
...@@ -2827,7 +2827,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ...@@ -2827,7 +2827,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
!rcu_access_pointer(sdata->bss->beacon)) !rcu_access_pointer(sdata->bss->beacon))
need_offchan = true; need_offchan = true;
if (!ieee80211_is_action(mgmt->frame_control) || if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
break; break;
rcu_read_lock(); rcu_read_lock();
sta = sta_info_get(sdata, mgmt->da); sta = sta_info_get(sdata, mgmt->da);
...@@ -2930,19 +2931,8 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, ...@@ -2930,19 +2931,8 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
u16 frame_type, bool reg) u16 frame_type, bool reg)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
switch (frame_type) { switch (frame_type) {
case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH:
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
if (reg)
ifibss->auth_frame_registrations++;
else
ifibss->auth_frame_registrations--;
}
break;
case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ: case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
if (reg) if (reg)
local->probe_req_reg++; local->probe_req_reg++;
......
...@@ -281,13 +281,14 @@ void ieee80211_ba_session_work(struct work_struct *work) ...@@ -281,13 +281,14 @@ void ieee80211_ba_session_work(struct work_struct *work)
sta, tid, WLAN_BACK_RECIPIENT, sta, tid, WLAN_BACK_RECIPIENT,
WLAN_REASON_UNSPECIFIED, true); WLAN_REASON_UNSPECIFIED, true);
spin_lock_bh(&sta->lock);
tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
if (tid_tx) { if (tid_tx) {
/* /*
* Assign it over to the normal tid_tx array * Assign it over to the normal tid_tx array
* where it "goes live". * where it "goes live".
*/ */
spin_lock_bh(&sta->lock);
sta->ampdu_mlme.tid_start_tx[tid] = NULL; sta->ampdu_mlme.tid_start_tx[tid] = NULL;
/* could there be a race? */ /* could there be a race? */
...@@ -300,6 +301,7 @@ void ieee80211_ba_session_work(struct work_struct *work) ...@@ -300,6 +301,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
ieee80211_tx_ba_session_handle_start(sta, tid); ieee80211_tx_ba_session_handle_start(sta, tid);
continue; continue;
} }
spin_unlock_bh(&sta->lock);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid); tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
......
...@@ -81,7 +81,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -81,7 +81,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
cfg80211_chandef_create(&chandef, chan, ifibss->channel_type); chandef = ifibss->chandef;
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
chandef.width = NL80211_CHAN_WIDTH_20; chandef.width = NL80211_CHAN_WIDTH_20;
chandef.center_freq1 = chan->center_freq; chandef.center_freq1 = chan->center_freq;
...@@ -176,6 +176,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -176,6 +176,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* add HT capability and information IEs */ /* add HT capability and information IEs */
if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT && if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
chandef.width != NL80211_CHAN_WIDTH_5 &&
chandef.width != NL80211_CHAN_WIDTH_10 &&
sband->ht_cap.ht_supported) { sband->ht_cap.ht_supported) {
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap); sband->ht_cap.cap);
...@@ -298,8 +300,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -298,8 +300,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
tsf, false); tsf, false);
} }
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
bool auth)
__acquires(RCU) __acquires(RCU)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
...@@ -321,20 +322,12 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, ...@@ -321,20 +322,12 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
/* If it fails, maybe we raced another insertion? */ /* If it fails, maybe we raced another insertion? */
if (sta_info_insert_rcu(sta)) if (sta_info_insert_rcu(sta))
return sta_info_get(sdata, addr); return sta_info_get(sdata, addr);
if (auth && !sdata->u.ibss.auth_frame_registrations) {
ibss_dbg(sdata,
"TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
sdata->vif.addr, addr, sdata->u.ibss.bssid);
ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
}
return sta; return sta;
} }
static struct sta_info * static struct sta_info *
ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
const u8 *bssid, const u8 *addr, const u8 *addr, u32 supp_rates)
u32 supp_rates, bool auth)
__acquires(RCU) __acquires(RCU)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
...@@ -385,7 +378,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, ...@@ -385,7 +378,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband); ieee80211_mandatory_rates(sband);
return ieee80211_ibss_finish_sta(sta, auth); return ieee80211_ibss_finish_sta(sta);
} }
static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
...@@ -407,8 +400,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -407,8 +400,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
size_t len) size_t len)
{ {
u16 auth_alg, auth_transaction; u16 auth_alg, auth_transaction;
struct sta_info *sta;
u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
...@@ -425,22 +416,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -425,22 +416,6 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
return; return;
sta_info_destroy_addr(sdata, mgmt->sa);
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
rcu_read_unlock();
/*
* if we have any problem in allocating the new station, we reply with a
* DEAUTH frame to tell the other end that we had a problem
*/
if (!sta) {
ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
IEEE80211_STYPE_DEAUTH,
WLAN_REASON_UNSPECIFIED, true,
deauth_frame_buf);
return;
}
/* /*
* IEEE 802.11 standard does not require authentication in IBSS * IEEE 802.11 standard does not require authentication in IBSS
* networks and most implementations do not seem to use it. * networks and most implementations do not seem to use it.
...@@ -506,7 +481,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -506,7 +481,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
} else { } else {
rcu_read_unlock(); rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates, true); mgmt->sa, supp_rates);
} }
} }
...@@ -514,7 +489,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -514,7 +489,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
set_sta_flag(sta, WLAN_STA_WME); set_sta_flag(sta, WLAN_STA_WME);
if (sta && elems->ht_operation && elems->ht_cap_elem && if (sta && elems->ht_operation && elems->ht_cap_elem &&
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
/* we both use HT */ /* we both use HT */
struct ieee80211_ht_cap htcap_ie; struct ieee80211_ht_cap htcap_ie;
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
...@@ -529,8 +506,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -529,8 +506,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* fall back to HT20 if we don't use or use * fall back to HT20 if we don't use or use
* the other extension channel * the other extension channel
*/ */
if (cfg80211_get_chandef_type(&chandef) != if (chandef.center_freq1 !=
sdata->u.ibss.channel_type) sdata->u.ibss.chandef.center_freq1)
htcap_ie.cap_info &= htcap_ie.cap_info &=
cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
...@@ -569,7 +546,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -569,7 +546,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* different channel */ /* different channel */
if (sdata->u.ibss.fixed_channel && if (sdata->u.ibss.fixed_channel &&
sdata->u.ibss.channel != cbss->channel) sdata->u.ibss.chandef.chan != cbss->channel)
goto put_bss; goto put_bss;
/* different SSID */ /* different SSID */
...@@ -610,7 +587,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -610,7 +587,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_join_ibss(sdata, bss); ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, true); supp_rates);
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -759,7 +736,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) ...@@ -759,7 +736,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
sdata->drop_unencrypted = 0; sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
ifibss->channel, ifibss->basic_rates, ifibss->chandef.chan, ifibss->basic_rates,
capability, 0, true); capability, 0, true);
} }
...@@ -791,7 +768,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) ...@@ -791,7 +768,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
if (ifibss->fixed_bssid) if (ifibss->fixed_bssid)
bssid = ifibss->bssid; bssid = ifibss->bssid;
if (ifibss->fixed_channel) if (ifibss->fixed_channel)
chan = ifibss->channel; chan = ifibss->chandef.chan;
if (!is_zero_ether_addr(ifibss->bssid)) if (!is_zero_ether_addr(ifibss->bssid))
bssid = ifibss->bssid; bssid = ifibss->bssid;
cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
...@@ -982,7 +959,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) ...@@ -982,7 +959,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
list_del(&sta->list); list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock); spin_unlock_bh(&ifibss->incomplete_lock);
ieee80211_ibss_finish_sta(sta, true); ieee80211_ibss_finish_sta(sta);
rcu_read_unlock(); rcu_read_unlock();
spin_lock_bh(&ifibss->incomplete_lock); spin_lock_bh(&ifibss->incomplete_lock);
} }
...@@ -1058,9 +1035,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, ...@@ -1058,9 +1035,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->u.ibss.channel = params->chandef.chan; sdata->u.ibss.chandef = params->chandef;
sdata->u.ibss.channel_type =
cfg80211_get_chandef_type(&params->chandef);
sdata->u.ibss.fixed_channel = params->channel_fixed; sdata->u.ibss.fixed_channel = params->channel_fixed;
if (params->ie) { if (params->ie) {
...@@ -1119,7 +1094,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) ...@@ -1119,7 +1094,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
if (ifibss->privacy) if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY; capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel, cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid, ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS | ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY, WLAN_CAPABILITY_PRIVACY,
......
...@@ -94,6 +94,7 @@ struct ieee80211_bss { ...@@ -94,6 +94,7 @@ struct ieee80211_bss {
#define IEEE80211_MAX_SUPP_RATES 32 #define IEEE80211_MAX_SUPP_RATES 32
u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
size_t supp_rates_len; size_t supp_rates_len;
struct ieee80211_rate *beacon_rate;
/* /*
* During association, we save an ERP value from a probe response so * During association, we save an ERP value from a probe response so
...@@ -497,14 +498,12 @@ struct ieee80211_if_ibss { ...@@ -497,14 +498,12 @@ struct ieee80211_if_ibss {
bool privacy; bool privacy;
bool control_port; bool control_port;
unsigned int auth_frame_registrations;
u8 bssid[ETH_ALEN] __aligned(2); u8 bssid[ETH_ALEN] __aligned(2);
u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len, ie_len; u8 ssid_len, ie_len;
u8 *ie; u8 *ie;
struct ieee80211_channel *channel; struct cfg80211_chan_def chandef;
enum nl80211_channel_type channel_type;
unsigned long ibss_join_req; unsigned long ibss_join_req;
/* probe response/beacon for IBSS */ /* probe response/beacon for IBSS */
...@@ -543,6 +542,7 @@ struct ieee80211_if_mesh { ...@@ -543,6 +542,7 @@ struct ieee80211_if_mesh {
struct timer_list mesh_path_root_timer; struct timer_list mesh_path_root_timer;
unsigned long wrkq_flags; unsigned long wrkq_flags;
unsigned long mbss_changed;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
size_t mesh_id_len; size_t mesh_id_len;
......
...@@ -161,11 +161,8 @@ void mesh_sta_cleanup(struct sta_info *sta) ...@@ -161,11 +161,8 @@ void mesh_sta_cleanup(struct sta_info *sta)
del_timer_sync(&sta->plink_timer); del_timer_sync(&sta->plink_timer);
} }
if (changed) { if (changed)
sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed); ieee80211_mbss_info_change_notify(sdata, changed);
sdata_unlock(sdata);
}
} }
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
...@@ -419,7 +416,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, ...@@ -419,7 +416,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
if (!sband->ht_cap.ht_supported || if (!sband->ht_cap.ht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
return 0; return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
...@@ -719,14 +718,18 @@ ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata) ...@@ -719,14 +718,18 @@ ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed) u32 changed)
{ {
if (sdata->vif.bss_conf.enable_beacon && struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
(changed & (BSS_CHANGED_BEACON | unsigned long bits = changed;
BSS_CHANGED_HT | u32 bit;
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT))) if (!bits)
if (ieee80211_mesh_rebuild_beacon(sdata)) return;
return;
ieee80211_bss_info_change_notify(sdata, changed); /* if we race with running work, worst case this work becomes a noop */
for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
set_bit(bit, &ifmsh->mbss_changed);
set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
} }
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
...@@ -799,6 +802,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) ...@@ -799,6 +802,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_timer); del_timer_sync(&sdata->u.mesh.mesh_path_timer);
/* clear any mesh work (for next join) we may have accrued */
ifmsh->wrkq_flags = 0;
ifmsh->mbss_changed = 0;
local->fif_other_bss--; local->fif_other_bss--;
atomic_dec(&local->iff_allmultis); atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
...@@ -965,6 +972,28 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -965,6 +972,28 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
sdata_unlock(sdata); sdata_unlock(sdata);
} }
static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 bit, changed = 0;
for_each_set_bit(bit, &ifmsh->mbss_changed,
sizeof(changed) * BITS_PER_BYTE) {
clear_bit(bit, &ifmsh->mbss_changed);
changed |= BIT(bit);
}
if (sdata->vif.bss_conf.enable_beacon &&
(changed & (BSS_CHANGED_BEACON |
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT)))
if (ieee80211_mesh_rebuild_beacon(sdata))
return;
ieee80211_bss_info_change_notify(sdata, changed);
}
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
...@@ -995,6 +1024,8 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) ...@@ -995,6 +1024,8 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
mesh_sync_adjust_tbtt(sdata); mesh_sync_adjust_tbtt(sdata);
if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
mesh_bss_info_changed(sdata);
out: out:
sdata_unlock(sdata); sdata_unlock(sdata);
} }
......
...@@ -58,6 +58,7 @@ enum mesh_path_flags { ...@@ -58,6 +58,7 @@ enum mesh_path_flags {
* @MESH_WORK_ROOT: the mesh root station needs to send a frame * @MESH_WORK_ROOT: the mesh root station needs to send a frame
* @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
* mesh nodes * mesh nodes
* @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes
*/ */
enum mesh_deferred_task_flags { enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING, MESH_WORK_HOUSEKEEPING,
...@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags { ...@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags {
MESH_WORK_GROW_MPP_TABLE, MESH_WORK_GROW_MPP_TABLE,
MESH_WORK_ROOT, MESH_WORK_ROOT,
MESH_WORK_DRIFT_ADJUST, MESH_WORK_DRIFT_ADJUST,
MESH_WORK_MBSS_CHANGED,
}; };
/** /**
......
...@@ -154,8 +154,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) ...@@ -154,8 +154,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
u16 ht_opmode; u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false; bool non_ht_sta = false, ht20_sta = false;
if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
return 0; return 0;
default:
break;
}
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) { list_for_each_entry_rcu(sta, &local->sta_list, list) {
......
...@@ -190,6 +190,12 @@ static u32 chandef_downgrade(struct cfg80211_chan_def *c) ...@@ -190,6 +190,12 @@ static u32 chandef_downgrade(struct cfg80211_chan_def *c)
c->width = NL80211_CHAN_WIDTH_20_NOHT; c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break; 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)); WARN_ON_ONCE(!cfg80211_chandef_valid(c));
...@@ -1779,8 +1785,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ...@@ -1779,8 +1785,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
* probably just won't work at all. * probably just won't work at all.
*/ */
bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1; bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
bss_conf->beacon_rate = bss->beacon_rate;
bss_info_changed |= BSS_CHANGED_BEACON_INFO; bss_info_changed |= BSS_CHANGED_BEACON_INFO;
} else { } else {
bss_conf->beacon_rate = NULL;
bss_conf->dtim_period = 0; bss_conf->dtim_period = 0;
} }
...@@ -1903,6 +1911,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1903,6 +1911,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.chswitch_timer); del_timer_sync(&sdata->u.mgd.chswitch_timer);
sdata->vif.bss_conf.dtim_period = 0; sdata->vif.bss_conf.dtim_period = 0;
sdata->vif.bss_conf.beacon_rate = NULL;
ifmgd->have_beacon = false; ifmgd->have_beacon = false;
ifmgd->flags = 0; ifmgd->flags = 0;
...@@ -2785,8 +2795,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ...@@ -2785,8 +2795,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */ /* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, false); ieee80211_destroy_assoc_data(sdata, false);
cfg80211_put_bss(sdata->local->hw.wiphy, bss); cfg80211_assoc_timeout(sdata->dev, bss);
cfg80211_assoc_timeout(sdata->dev, mgmt->bssid);
return; return;
} }
sdata_info(sdata, "associated\n"); sdata_info(sdata, "associated\n");
...@@ -2827,8 +2836,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -2827,8 +2836,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel); channel);
if (bss) if (bss) {
ieee80211_rx_bss_put(local, bss); ieee80211_rx_bss_put(local, bss);
sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
}
if (!sdata->u.mgd.associated || if (!sdata->u.mgd.associated ||
!ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
...@@ -3501,13 +3512,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ...@@ -3501,13 +3512,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
time_after(jiffies, ifmgd->assoc_data->timeout)) { time_after(jiffies, ifmgd->assoc_data->timeout)) {
if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) || if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
ieee80211_do_assoc(sdata)) { ieee80211_do_assoc(sdata)) {
u8 bssid[ETH_ALEN]; struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
memcpy(bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN);
ieee80211_destroy_assoc_data(sdata, false); ieee80211_destroy_assoc_data(sdata, false);
cfg80211_assoc_timeout(sdata->dev, bss);
cfg80211_assoc_timeout(sdata->dev, bssid);
} }
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout); run_again(sdata, ifmgd->assoc_data->timeout);
...@@ -3838,6 +3846,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ...@@ -3838,6 +3846,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
*/ */
ret = ieee80211_vif_use_channel(sdata, &chandef, ret = ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED); IEEE80211_CHANCTX_SHARED);
/* don't downgrade for 5 and 10 MHz channels, though. */
if (chandef.width == NL80211_CHAN_WIDTH_5 ||
chandef.width == NL80211_CHAN_WIDTH_10)
return ret;
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
ifmgd->flags |= chandef_downgrade(&chandef); ifmgd->flags |= chandef_downgrade(&chandef);
ret = ieee80211_vif_use_channel(sdata, &chandef, ret = ieee80211_vif_use_channel(sdata, &chandef,
...@@ -4427,8 +4441,11 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) ...@@ -4427,8 +4441,11 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->chswitch_work); cancel_work_sync(&ifmgd->chswitch_work);
sdata_lock(sdata); sdata_lock(sdata);
if (ifmgd->assoc_data) if (ifmgd->assoc_data) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
ieee80211_destroy_assoc_data(sdata, false); ieee80211_destroy_assoc_data(sdata, false);
cfg80211_assoc_timeout(sdata->dev, bss);
}
if (ifmgd->auth_data) if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false); ieee80211_destroy_auth_data(sdata, false);
del_timer_sync(&ifmgd->timer); del_timer_sync(&ifmgd->timer);
......
...@@ -397,8 +397,14 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, ...@@ -397,8 +397,14 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
return; return;
/* if HT BSS, and we handle a data frame, also try HT rates */ /* if HT BSS, and we handle a data frame, also try HT rates */
if (chan_width == NL80211_CHAN_WIDTH_20_NOHT) switch (chan_width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
return; return;
default:
break;
}
alt_rate.idx = 0; alt_rate.idx = 0;
/* keep protection flags */ /* keep protection flags */
......
...@@ -140,6 +140,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local, ...@@ -140,6 +140,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_WMM; bss->valid_data |= IEEE80211_BSS_VALID_WMM;
} }
if (beacon) {
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[rx_status->band];
if (!(rx_status->flag & RX_FLAG_HT) &&
!(rx_status->flag & RX_FLAG_VHT))
bss->beacon_rate =
&sband->bitrates[rx_status->rate_idx];
}
return bss; return bss;
} }
......
...@@ -149,6 +149,7 @@ static void cleanup_single_sta(struct sta_info *sta) ...@@ -149,6 +149,7 @@ static void cleanup_single_sta(struct sta_info *sta)
* directly by station destruction. * directly by station destruction.
*/ */
for (i = 0; i < IEEE80211_NUM_TIDS; i++) { for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
kfree(sta->ampdu_mlme.tid_start_tx[i]);
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx) if (!tid_tx)
continue; continue;
...@@ -346,6 +347,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, ...@@ -346,6 +347,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (ieee80211_vif_is_mesh(&sdata->vif) && if (ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->u.mesh.user_mpm) !sdata->u.mesh.user_mpm)
init_timer(&sta->plink_timer); init_timer(&sta->plink_timer);
sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
#endif #endif
memcpy(sta->sta.addr, addr, ETH_ALEN); memcpy(sta->sta.addr, addr, ETH_ALEN);
......
...@@ -203,6 +203,7 @@ struct tid_ampdu_rx { ...@@ -203,6 +203,7 @@ struct tid_ampdu_rx {
* driver requested to close until the work for it runs * driver requested to close until the work for it runs
* @mtx: mutex to protect all TX data (except non-NULL assignments * @mtx: mutex to protect all TX data (except non-NULL assignments
* to tid_tx[idx], which are protected by the sta spinlock) * to tid_tx[idx], which are protected by the sta spinlock)
* tid_start_tx is also protected by sta->lock.
*/ */
struct sta_ampdu_mlme { struct sta_ampdu_mlme {
struct mutex mtx; struct mutex mtx;
......
...@@ -396,7 +396,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, ...@@ -396,7 +396,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
new_bw = ieee80211_sta_cur_vht_bw(sta); new_bw = ieee80211_sta_cur_vht_bw(sta);
if (new_bw != sta->sta.bandwidth) { if (new_bw != sta->sta.bandwidth) {
sta->sta.bandwidth = new_bw; sta->sta.bandwidth = new_bw;
changed |= IEEE80211_RC_NSS_CHANGED; changed |= IEEE80211_RC_BW_CHANGED;
} }
change: change:
......
...@@ -54,6 +54,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) ...@@ -54,6 +54,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
control_freq = chandef->chan->center_freq; control_freq = chandef->chan->center_freq;
switch (chandef->width) { switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20_NOHT:
if (chandef->center_freq1 != control_freq) if (chandef->center_freq1 != control_freq)
...@@ -152,6 +154,12 @@ static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) ...@@ -152,6 +154,12 @@ static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
int width; int width;
switch (c->width) { switch (c->width) {
case NL80211_CHAN_WIDTH_5:
width = 5;
break;
case NL80211_CHAN_WIDTH_10:
width = 10;
break;
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20_NOHT:
width = 20; width = 20;
...@@ -194,6 +202,16 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, ...@@ -194,6 +202,16 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
if (c1->width == c2->width) if (c1->width == c2->width)
return NULL; return NULL;
/*
* can't be compatible if one of them is 5 or 10 MHz,
* but they don't have the same width.
*/
if (c1->width == NL80211_CHAN_WIDTH_5 ||
c1->width == NL80211_CHAN_WIDTH_10 ||
c2->width == NL80211_CHAN_WIDTH_5 ||
c2->width == NL80211_CHAN_WIDTH_10)
return NULL;
if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
c1->width == NL80211_CHAN_WIDTH_20) c1->width == NL80211_CHAN_WIDTH_20)
return c2; return c2;
...@@ -264,11 +282,17 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, ...@@ -264,11 +282,17 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 bandwidth) u32 bandwidth)
{ {
struct ieee80211_channel *c; struct ieee80211_channel *c;
u32 freq; u32 freq, start_freq, end_freq;
if (bandwidth <= 20) {
start_freq = center_freq;
end_freq = center_freq;
} else {
start_freq = center_freq - bandwidth/2 + 10;
end_freq = center_freq + bandwidth/2 - 10;
}
for (freq = center_freq - bandwidth/2 + 10; for (freq = start_freq; freq <= end_freq; freq += 20) {
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq); c = ieee80211_get_channel(wiphy, freq);
if (!c) if (!c)
return -EINVAL; return -EINVAL;
...@@ -310,11 +334,17 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, ...@@ -310,11 +334,17 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 prohibited_flags) u32 prohibited_flags)
{ {
struct ieee80211_channel *c; struct ieee80211_channel *c;
u32 freq; u32 freq, start_freq, end_freq;
if (bandwidth <= 20) {
start_freq = center_freq;
end_freq = center_freq;
} else {
start_freq = center_freq - bandwidth/2 + 10;
end_freq = center_freq + bandwidth/2 - 10;
}
for (freq = center_freq - bandwidth/2 + 10; for (freq = start_freq; freq <= end_freq; freq += 20) {
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq); c = ieee80211_get_channel(wiphy, freq);
if (!c) if (!c)
return false; return false;
...@@ -349,6 +379,12 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, ...@@ -349,6 +379,12 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
control_freq = chandef->chan->center_freq; control_freq = chandef->chan->center_freq;
switch (chandef->width) { switch (chandef->width) {
case NL80211_CHAN_WIDTH_5:
width = 5;
break;
case NL80211_CHAN_WIDTH_10:
width = 10;
break;
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
if (!ht_cap->ht_supported) if (!ht_cap->ht_supported)
return false; return false;
...@@ -405,6 +441,11 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, ...@@ -405,6 +441,11 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
if (width > 20) if (width > 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM; prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
/* 5 and 10 MHz are only defined for the OFDM PHY */
if (width < 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
width, prohibited_flags)) width, prohibited_flags))
return false; return false;
......
...@@ -934,6 +934,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ...@@ -934,6 +934,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
* freed. * freed.
*/ */
cfg80211_process_wdev_events(wdev); cfg80211_process_wdev_events(wdev);
if (WARN_ON(wdev->current_bss)) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
}
break; break;
case NETDEV_PRE_UP: case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
......
...@@ -38,6 +38,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, ...@@ -38,6 +38,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
* frame instead of reassoc. * frame instead of reassoc.
*/ */
if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) { if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wiphy, bss); cfg80211_put_bss(wiphy, bss);
return; return;
} }
...@@ -131,16 +132,19 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr) ...@@ -131,16 +132,19 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
} }
EXPORT_SYMBOL(cfg80211_auth_timeout); EXPORT_SYMBOL(cfg80211_auth_timeout);
void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr) void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy; struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
trace_cfg80211_send_assoc_timeout(dev, addr); trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL);
cfg80211_sme_assoc_timeout(wdev); cfg80211_sme_assoc_timeout(wdev);
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wiphy, bss);
} }
EXPORT_SYMBOL(cfg80211_assoc_timeout); EXPORT_SYMBOL(cfg80211_assoc_timeout);
...@@ -307,6 +311,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, ...@@ -307,6 +311,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
goto out; goto out;
err = rdev_assoc(rdev, dev, req); err = rdev_assoc(rdev, dev, req);
if (!err)
cfg80211_hold_bss(bss_from_pub(req->bss));
out: out:
if (err) if (err)
......
...@@ -1111,10 +1111,16 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg, ...@@ -1111,10 +1111,16 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg,
return 0; return 0;
} }
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
long split_start, band_start, chan_start;
bool split;
};
static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
struct sk_buff *msg, u32 portid, u32 seq, struct sk_buff *msg, u32 portid, u32 seq,
int flags, bool split, long *split_start, int flags, struct nl80211_dump_wiphy_state *state)
long *band_start, long *chan_start)
{ {
void *hdr; void *hdr;
struct nlattr *nl_bands, *nl_band; struct nlattr *nl_bands, *nl_band;
...@@ -1125,19 +1131,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1125,19 +1131,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
int i; int i;
const struct ieee80211_txrx_stypes *mgmt_stypes = const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes; dev->wiphy.mgmt_stypes;
long start = 0, start_chan = 0, start_band = 0;
u32 features; u32 features;
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr) if (!hdr)
return -ENOBUFS; return -ENOBUFS;
/* allow always using the variables */ if (WARN_ON(!state))
if (!split) { return -EINVAL;
split_start = &start;
band_start = &start_band;
chan_start = &start_chan;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
...@@ -1146,7 +1147,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1146,7 +1147,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
cfg80211_rdev_list_generation)) cfg80211_rdev_list_generation))
goto nla_put_failure; goto nla_put_failure;
switch (*split_start) { switch (state->split_start) {
case 0: case 0:
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
dev->wiphy.retry_short) || dev->wiphy.retry_short) ||
...@@ -1188,9 +1189,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1188,9 +1189,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure; goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
goto nla_put_failure;
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 1: case 1:
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
...@@ -1234,22 +1238,23 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1234,22 +1238,23 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
} }
} }
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 2: case 2:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
dev->wiphy.interface_modes)) dev->wiphy.interface_modes))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 3: case 3:
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands) if (!nl_bands)
goto nla_put_failure; goto nla_put_failure;
for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) { for (band = state->band_start;
band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
sband = dev->wiphy.bands[band]; sband = dev->wiphy.bands[band];
...@@ -1261,12 +1266,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1261,12 +1266,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (!nl_band) if (!nl_band)
goto nla_put_failure; goto nla_put_failure;
switch (*chan_start) { switch (state->chan_start) {
case 0: case 0:
if (nl80211_send_band_rateinfo(msg, sband)) if (nl80211_send_band_rateinfo(msg, sband))
goto nla_put_failure; goto nla_put_failure;
(*chan_start)++; state->chan_start++;
if (split) if (state->split)
break; break;
default: default:
/* add frequencies */ /* add frequencies */
...@@ -1275,7 +1280,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1275,7 +1280,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (!nl_freqs) if (!nl_freqs)
goto nla_put_failure; goto nla_put_failure;
for (i = *chan_start - 1; for (i = state->chan_start - 1;
i < sband->n_channels; i < sband->n_channels;
i++) { i++) {
nl_freq = nla_nest_start(msg, i); nl_freq = nla_nest_start(msg, i);
...@@ -1284,26 +1289,27 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1284,26 +1289,27 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
chan = &sband->channels[i]; chan = &sband->channels[i];
if (nl80211_msg_put_channel(msg, chan, if (nl80211_msg_put_channel(
split)) msg, chan,
state->split))
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(msg, nl_freq); nla_nest_end(msg, nl_freq);
if (split) if (state->split)
break; break;
} }
if (i < sband->n_channels) if (i < sband->n_channels)
*chan_start = i + 2; state->chan_start = i + 2;
else else
*chan_start = 0; state->chan_start = 0;
nla_nest_end(msg, nl_freqs); nla_nest_end(msg, nl_freqs);
} }
nla_nest_end(msg, nl_band); nla_nest_end(msg, nl_band);
if (split) { if (state->split) {
/* start again here */ /* start again here */
if (*chan_start) if (state->chan_start)
band--; band--;
break; break;
} }
...@@ -1311,14 +1317,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1311,14 +1317,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
nla_nest_end(msg, nl_bands); nla_nest_end(msg, nl_bands);
if (band < IEEE80211_NUM_BANDS) if (band < IEEE80211_NUM_BANDS)
*band_start = band + 1; state->band_start = band + 1;
else else
*band_start = 0; state->band_start = 0;
/* if bands & channels are done, continue outside */ /* if bands & channels are done, continue outside */
if (*band_start == 0 && *chan_start == 0) if (state->band_start == 0 && state->chan_start == 0)
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 4: case 4:
nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
...@@ -1384,7 +1390,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1384,7 +1390,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
} }
CMD(start_p2p_device, START_P2P_DEVICE); CMD(start_p2p_device, START_P2P_DEVICE);
CMD(set_mcast_rate, SET_MCAST_RATE); CMD(set_mcast_rate, SET_MCAST_RATE);
if (split) { if (state->split) {
CMD(crit_proto_start, CRIT_PROTOCOL_START); CMD(crit_proto_start, CRIT_PROTOCOL_START);
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
} }
...@@ -1408,8 +1414,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1408,8 +1414,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
} }
nla_nest_end(msg, nl_cmds); nla_nest_end(msg, nl_cmds);
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 5: case 5:
if (dev->ops->remain_on_channel && if (dev->ops->remain_on_channel &&
...@@ -1425,29 +1431,30 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1425,29 +1431,30 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (nl80211_send_mgmt_stypes(msg, mgmt_stypes)) if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 6: case 6:
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (nl80211_send_wowlan(msg, dev, split)) if (nl80211_send_wowlan(msg, dev, state->split))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
#else #else
(*split_start)++; state->split_start++;
#endif #endif
case 7: case 7:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
dev->wiphy.software_iftypes)) dev->wiphy.software_iftypes))
goto nla_put_failure; goto nla_put_failure;
if (nl80211_put_iface_combinations(&dev->wiphy, msg, split)) if (nl80211_put_iface_combinations(&dev->wiphy, msg,
state->split))
goto nla_put_failure; goto nla_put_failure;
(*split_start)++; state->split_start++;
if (split) if (state->split)
break; break;
case 8: case 8:
if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
...@@ -1461,7 +1468,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1461,7 +1468,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
* dump is split, otherwise it makes it too big. Therefore * dump is split, otherwise it makes it too big. Therefore
* only advertise it in that case. * only advertise it in that case.
*/ */
if (split) if (state->split)
features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features)) if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
goto nla_put_failure; goto nla_put_failure;
...@@ -1488,7 +1495,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1488,7 +1495,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
* case we'll continue with more data in the next round, * case we'll continue with more data in the next round,
* but break unconditionally so unsplit data stops here. * but break unconditionally so unsplit data stops here.
*/ */
(*split_start)++; state->split_start++;
break; break;
case 9: case 9:
if (dev->wiphy.extended_capabilities && if (dev->wiphy.extended_capabilities &&
...@@ -1507,7 +1514,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1507,7 +1514,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
goto nla_put_failure; goto nla_put_failure;
/* done */ /* done */
*split_start = 0; state->split_start = 0;
break; break;
} }
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
...@@ -1517,59 +1524,76 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, ...@@ -1517,59 +1524,76 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
return -EMSGSIZE; return -EMSGSIZE;
} }
static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
struct netlink_callback *cb,
struct nl80211_dump_wiphy_state *state)
{
struct nlattr **tb = nl80211_fam.attrbuf;
int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
tb, nl80211_fam.maxattr, nl80211_policy);
/* ignore parse errors for backward compatibility */
if (ret)
return 0;
state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
if (tb[NL80211_ATTR_WIPHY])
state->filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
if (tb[NL80211_ATTR_WDEV])
state->filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
if (tb[NL80211_ATTR_IFINDEX]) {
struct net_device *netdev;
struct cfg80211_registered_device *rdev;
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
if (!netdev)
return -ENODEV;
if (netdev->ieee80211_ptr) {
rdev = wiphy_to_dev(
netdev->ieee80211_ptr->wiphy);
state->filter_wiphy = rdev->wiphy_idx;
}
dev_put(netdev);
}
return 0;
}
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
{ {
int idx = 0, ret; int idx = 0, ret;
int start = cb->args[0]; struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
struct cfg80211_registered_device *dev; struct cfg80211_registered_device *dev;
s64 filter_wiphy = -1;
bool split = false;
struct nlattr **tb = nl80211_fam.attrbuf;
int res;
rtnl_lock(); rtnl_lock();
res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, if (!state) {
tb, nl80211_fam.maxattr, nl80211_policy); state = kzalloc(sizeof(*state), GFP_KERNEL);
if (res == 0) { if (!state)
split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; return -ENOMEM;
if (tb[NL80211_ATTR_WIPHY]) state->filter_wiphy = -1;
filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); ret = nl80211_dump_wiphy_parse(skb, cb, state);
if (tb[NL80211_ATTR_WDEV]) if (ret) {
filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32; kfree(state);
if (tb[NL80211_ATTR_IFINDEX]) { rtnl_unlock();
struct net_device *netdev; return ret;
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
if (!netdev) {
rtnl_unlock();
return -ENODEV;
}
if (netdev->ieee80211_ptr) {
dev = wiphy_to_dev(
netdev->ieee80211_ptr->wiphy);
filter_wiphy = dev->wiphy_idx;
}
dev_put(netdev);
} }
cb->args[0] = (long)state;
} }
list_for_each_entry(dev, &cfg80211_rdev_list, list) { list_for_each_entry(dev, &cfg80211_rdev_list, list) {
if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
continue; continue;
if (++idx <= start) if (++idx <= state->start)
continue; continue;
if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy) if (state->filter_wiphy != -1 &&
state->filter_wiphy != dev->wiphy_idx)
continue; continue;
/* attempt to fit multiple wiphy data chunks into the skb */ /* attempt to fit multiple wiphy data chunks into the skb */
do { do {
ret = nl80211_send_wiphy(dev, skb, ret = nl80211_send_wiphy(dev, skb,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb->nlh->nlmsg_seq,
NLM_F_MULTI, NLM_F_MULTI, state);
split, &cb->args[1],
&cb->args[2],
&cb->args[3]);
if (ret < 0) { if (ret < 0) {
/* /*
* If sending the wiphy data didn't fit (ENOBUFS * If sending the wiphy data didn't fit (ENOBUFS
...@@ -1594,27 +1618,34 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1594,27 +1618,34 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
idx--; idx--;
break; break;
} }
} while (cb->args[1] > 0); } while (state->split_start > 0);
break; break;
} }
rtnl_unlock(); rtnl_unlock();
cb->args[0] = idx; state->start = idx;
return skb->len; return skb->len;
} }
static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
{
kfree((void *)cb->args[0]);
return 0;
}
static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
{ {
struct sk_buff *msg; struct sk_buff *msg;
struct cfg80211_registered_device *dev = info->user_ptr[0]; struct cfg80211_registered_device *dev = info->user_ptr[0];
struct nl80211_dump_wiphy_state state = {};
msg = nlmsg_new(4096, GFP_KERNEL); msg = nlmsg_new(4096, GFP_KERNEL);
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0, if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
false, NULL, NULL, NULL) < 0) { &state) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return -ENOBUFS; return -ENOBUFS;
} }
...@@ -1731,6 +1762,11 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, ...@@ -1731,6 +1762,11 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
IEEE80211_CHAN_DISABLED)) IEEE80211_CHAN_DISABLED))
return -EINVAL; return -EINVAL;
if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
chandef->width == NL80211_CHAN_WIDTH_10) &&
!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
return -EINVAL;
return 0; return 0;
} }
...@@ -2882,61 +2918,58 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) ...@@ -2882,61 +2918,58 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
return err; return err;
} }
static int nl80211_parse_beacon(struct genl_info *info, static int nl80211_parse_beacon(struct nlattr *attrs[],
struct cfg80211_beacon_data *bcn) struct cfg80211_beacon_data *bcn)
{ {
bool haveinfo = false; bool haveinfo = false;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) || !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
return -EINVAL; return -EINVAL;
memset(bcn, 0, sizeof(*bcn)); memset(bcn, 0, sizeof(*bcn));
if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { if (attrs[NL80211_ATTR_BEACON_HEAD]) {
bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
if (!bcn->head_len) if (!bcn->head_len)
return -EINVAL; return -EINVAL;
haveinfo = true; haveinfo = true;
} }
if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { if (attrs[NL80211_ATTR_BEACON_TAIL]) {
bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
bcn->tail_len = bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
haveinfo = true; haveinfo = true;
} }
if (!haveinfo) if (!haveinfo)
return -EINVAL; return -EINVAL;
if (info->attrs[NL80211_ATTR_IE]) { if (attrs[NL80211_ATTR_IE]) {
bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
} }
if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
bcn->proberesp_ies = bcn->proberesp_ies =
nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
bcn->proberesp_ies_len = bcn->proberesp_ies_len =
nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
} }
if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
bcn->assocresp_ies = bcn->assocresp_ies =
nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
bcn->assocresp_ies_len = bcn->assocresp_ies_len =
nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
} }
if (info->attrs[NL80211_ATTR_PROBE_RESP]) { if (attrs[NL80211_ATTR_PROBE_RESP]) {
bcn->probe_resp = bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
bcn->probe_resp_len =
nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
} }
return 0; return 0;
...@@ -3015,7 +3048,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ...@@ -3015,7 +3048,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
!info->attrs[NL80211_ATTR_BEACON_HEAD]) !info->attrs[NL80211_ATTR_BEACON_HEAD])
return -EINVAL; return -EINVAL;
err = nl80211_parse_beacon(info, &params.beacon); err = nl80211_parse_beacon(info->attrs, &params.beacon);
if (err) if (err)
return err; return err;
...@@ -3167,7 +3200,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) ...@@ -3167,7 +3200,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (!wdev->beacon_interval) if (!wdev->beacon_interval)
return -EINVAL; return -EINVAL;
err = nl80211_parse_beacon(info, &params); err = nl80211_parse_beacon(info->attrs, &params);
if (err) if (err)
return err; return err;
...@@ -6283,11 +6316,16 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ...@@ -6283,11 +6316,16 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
return -EINVAL; return -EINVAL;
if (ibss.chandef.width > NL80211_CHAN_WIDTH_40) switch (ibss.chandef.width) {
return -EINVAL; case NL80211_CHAN_WIDTH_20_NOHT:
if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && break;
!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_40:
if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
break;
default:
return -EINVAL; return -EINVAL;
}
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
...@@ -8401,6 +8439,7 @@ static struct genl_ops nl80211_ops[] = { ...@@ -8401,6 +8439,7 @@ static struct genl_ops nl80211_ops[] = {
.cmd = NL80211_CMD_GET_WIPHY, .cmd = NL80211_CMD_GET_WIPHY,
.doit = nl80211_get_wiphy, .doit = nl80211_get_wiphy,
.dumpit = nl80211_dump_wiphy, .dumpit = nl80211_dump_wiphy,
.done = nl80211_dump_wiphy_done,
.policy = nl80211_policy, .policy = nl80211_policy,
/* can be retrieved by unprivileged users */ /* can be retrieved by unprivileged users */
.internal_flags = NL80211_FLAG_NEED_WIPHY | .internal_flags = NL80211_FLAG_NEED_WIPHY |
...@@ -9021,13 +9060,13 @@ static struct genl_multicast_group nl80211_regulatory_mcgrp = { ...@@ -9021,13 +9060,13 @@ static struct genl_multicast_group nl80211_regulatory_mcgrp = {
void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
{ {
struct sk_buff *msg; struct sk_buff *msg;
struct nl80211_dump_wiphy_state state = {};
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg) if (!msg)
return; return;
if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) {
false, NULL, NULL, NULL) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return; return;
} }
......
...@@ -523,6 +523,7 @@ static int cmp_bss(struct cfg80211_bss *a, ...@@ -523,6 +523,7 @@ static int cmp_bss(struct cfg80211_bss *a,
} }
} }
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, const u8 *bssid,
...@@ -678,6 +679,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, ...@@ -678,6 +679,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
return true; return true;
} }
/* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_internal_bss * static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev, cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *tmp) struct cfg80211_internal_bss *tmp)
...@@ -866,6 +868,7 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, ...@@ -866,6 +868,7 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
return channel; return channel;
} }
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss* struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy, cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
...@@ -923,6 +926,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, ...@@ -923,6 +926,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
} }
EXPORT_SYMBOL(cfg80211_inform_bss); EXPORT_SYMBOL(cfg80211_inform_bss);
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss * struct cfg80211_bss *
cfg80211_inform_bss_frame(struct wiphy *wiphy, cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
......
...@@ -239,6 +239,7 @@ void cfg80211_conn_work(struct work_struct *work) ...@@ -239,6 +239,7 @@ void cfg80211_conn_work(struct work_struct *work)
rtnl_unlock(); rtnl_unlock();
} }
/* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
...@@ -557,6 +558,7 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); ...@@ -557,6 +558,7 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
* SME event handling * SME event handling
*/ */
/* This method must consume bss one way or another */
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len, const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len, const u8 *resp_ie, size_t resp_ie_len,
...@@ -572,8 +574,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, ...@@ -572,8 +574,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
ASSERT_WDEV_LOCK(wdev); ASSERT_WDEV_LOCK(wdev);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
cfg80211_put_bss(wdev->wiphy, bss);
return; return;
}
nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
bssid, req_ie, req_ie_len, bssid, req_ie, req_ie_len,
...@@ -615,19 +619,24 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, ...@@ -615,19 +619,24 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
kfree(wdev->connect_keys); kfree(wdev->connect_keys);
wdev->connect_keys = NULL; wdev->connect_keys = NULL;
wdev->ssid_len = 0; wdev->ssid_len = 0;
cfg80211_put_bss(wdev->wiphy, bss); if (bss) {
cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wdev->wiphy, bss);
}
return; return;
} }
if (!bss) if (!bss) {
WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
wdev->ssid, wdev->ssid_len, wdev->ssid, wdev->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS); WLAN_CAPABILITY_ESS);
if (WARN_ON(!bss)) if (WARN_ON(!bss))
return; return;
cfg80211_hold_bss(bss_from_pub(bss));
}
cfg80211_hold_bss(bss_from_pub(bss));
wdev->current_bss = bss_from_pub(bss); wdev->current_bss = bss_from_pub(bss);
cfg80211_upload_connect_keys(wdev); cfg80211_upload_connect_keys(wdev);
...@@ -691,6 +700,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, ...@@ -691,6 +700,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
} }
EXPORT_SYMBOL(cfg80211_connect_result); EXPORT_SYMBOL(cfg80211_connect_result);
/* Consumes bss object one way or another */
void __cfg80211_roamed(struct wireless_dev *wdev, void __cfg80211_roamed(struct wireless_dev *wdev,
struct cfg80211_bss *bss, struct cfg80211_bss *bss,
const u8 *req_ie, size_t req_ie_len, const u8 *req_ie, size_t req_ie_len,
...@@ -767,6 +777,7 @@ void cfg80211_roamed(struct net_device *dev, ...@@ -767,6 +777,7 @@ void cfg80211_roamed(struct net_device *dev,
} }
EXPORT_SYMBOL(cfg80211_roamed); EXPORT_SYMBOL(cfg80211_roamed);
/* Consumes bss object one way or another */
void cfg80211_roamed_bss(struct net_device *dev, void cfg80211_roamed_bss(struct net_device *dev,
struct cfg80211_bss *bss, const u8 *req_ie, struct cfg80211_bss *bss, const u8 *req_ie,
size_t req_ie_len, const u8 *resp_ie, size_t req_ie_len, const u8 *resp_ie,
......
...@@ -83,6 +83,7 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -83,6 +83,7 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0; return 0;
} }
#ifdef CONFIG_PM
static void cfg80211_leave_all(struct cfg80211_registered_device *rdev) static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
{ {
struct wireless_dev *wdev; struct wireless_dev *wdev;
...@@ -91,7 +92,6 @@ static void cfg80211_leave_all(struct cfg80211_registered_device *rdev) ...@@ -91,7 +92,6 @@ static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
cfg80211_leave(rdev, wdev); cfg80211_leave(rdev, wdev);
} }
#ifdef CONFIG_PM
static int wiphy_suspend(struct device *dev, pm_message_t state) static int wiphy_suspend(struct device *dev, pm_message_t state)
{ {
struct cfg80211_registered_device *rdev = dev_to_rdev(dev); struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment