For 4.11, we seem to have more than in the past few releases:

* socket owner support for connections, so when the wifi
    manager (e.g. wpa_supplicant) is killed, connections are
    torn down - wpa_supplicant is critical to managing certain
    operations, and can opt in to this where applicable
  * minstrel & minstrel_ht updates to be more efficient (time and space)
  * set wifi_acked/wifi_acked_valid for skb->destructor use in the
    kernel, which was already available to userspace
  * don't indicate new mesh peers that might be used if there's no
    room to add them
  * multicast-to-unicast support in mac80211, for better medium usage
    (since unicast frames can use *much* higher rates, by ~3 orders of
    magnitude)
  * add API to read channel (frequency) limitations from DT
  * add infrastructure to allow randomizing public action frames for
    MAC address privacy (still requires driver support)
  * many cleanups and small improvements/fixes across the board
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCgAGBQJYeKu7AAoJEGt7eEactAAdwjEP/RA4bXFMfkC7qUJ++cLrMMwY
 yCvjb8+ULWL2wbCzpfY37acbGJgot3DNoQJzrO2jMQPqyM9nRlTMg5aF49cI7t62
 gU6daNKJaGBe/0yeG7lTJ4n5UtVCDtN45hGc06Yert+ewb9njiJf+XYrtCWetsIJ
 5bOLYQKPWOz/7UyMH7uJ25zrPFaiA3y7XnXKPEudagG/EwEq9ZuUpSSfLwEAEBPi
 6i/2w4fLj32vXRsQMvQT0sU6mjd+1ub8Is7w5l2F06iWwNYPzdSM0IbU+E+ie2tk
 sE6RA70c4ILrp8KisTAz2lJPa4XEpFkLhI3lzRRy8CVzjyyo/OJen92zvr2R7TVb
 /uZG9qfRQ3UitQmgeKd+wS8PsbRAyWUR/xhNxD2r7zARH2vliwyneU+zEpXLeGA1
 Y4PrN1+Fk45Ye4/4XSbPO4cf1MHX7qinN4rjrpsJKPwoYD/gQ1cZvef4AbaKPvq6
 oCKRVrwNoUuSB8NTcMLPqze3WCfhnJyVUhCZTyzHeW4uG81qrHwrvBvM25vcWGcm
 CcSWFktFIpuGML4FCU3byZfb0NkmJtpCD4n7P98WFPGjvsWIEVCMckqlC8x1F7B7
 BqqjGS2mGA17Xy0uLfmN/JempesQJnZhnAnFERdyX1S1YQuKhLwEu7OsYegnStDL
 Cn1wFw2/qcgeTkJfBICB
 =UToW
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-davem-2017-01-13' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg says:

====================
For 4.11, we seem to have more than in the past few releases:
 * socket owner support for connections, so when the wifi
   manager (e.g. wpa_supplicant) is killed, connections are
   torn down - wpa_supplicant is critical to managing certain
   operations, and can opt in to this where applicable
 * minstrel & minstrel_ht updates to be more efficient (time and space)
 * set wifi_acked/wifi_acked_valid for skb->destructor use in the
   kernel, which was already available to userspace
 * don't indicate new mesh peers that might be used if there's no
   room to add them
 * multicast-to-unicast support in mac80211, for better medium usage
   (since unicast frames can use *much* higher rates, by ~3 orders of
   magnitude)
 * add API to read channel (frequency) limitations from DT
 * add infrastructure to allow randomizing public action frames for
   MAC address privacy (still requires driver support)
 * many cleanups and small improvements/fixes across the board
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-01-14 12:02:15 -05:00
commit bb60b8b35a
49 changed files with 1015 additions and 426 deletions

View File

@ -0,0 +1,24 @@
Common IEEE 802.11 properties
This provides documentation of common properties that are valid for all wireless
devices.
Optional properties:
- ieee80211-freq-limit : list of supported frequency ranges in KHz. This can be
used for devices that in a given config support less channels than
normally. It may happen chipset supports a wide wireless band but it is
limited to some part of it due to used antennas or power amplifier.
An example case for this can be tri-band wireless router with two
identical chipsets used for two different 5 GHz subbands. Using them
incorrectly could not work or decrease performance noticeably.
Example:
pcie@0,0 {
reg = <0x0000 0 0 0 0>;
wifi@0,0 {
reg = <0x0000 0 0 0 0>;
ieee80211-freq-limit = <2402000 2482000>,
<5170000 5250000>;
};
};

View File

@ -44,6 +44,9 @@ Device registration
.. kernel-doc:: include/net/cfg80211.h .. kernel-doc:: include/net/cfg80211.h
:functions: wiphy_new :functions: wiphy_new
.. kernel-doc:: include/net/cfg80211.h
:functions: wiphy_read_of_freq_limits
.. kernel-doc:: include/net/cfg80211.h .. kernel-doc:: include/net/cfg80211.h
:functions: wiphy_register :functions: wiphy_register

View File

@ -156,12 +156,12 @@ struct ieee80211_regdomain mydriver_jp_regdom = {
//.alpha2 = "99", /* If I have no alpha2 to map it to */ //.alpha2 = "99", /* If I have no alpha2 to map it to */
.reg_rules = { .reg_rules = {
/* IEEE 802.11b/g, channels 1..14 */ /* IEEE 802.11b/g, channels 1..14 */
REG_RULE(2412-20, 2484+20, 40, 6, 20, 0), REG_RULE(2412-10, 2484+10, 40, 6, 20, 0),
/* IEEE 802.11a, channels 34..48 */ /* IEEE 802.11a, channels 34..48 */
REG_RULE(5170-20, 5240+20, 40, 6, 20, REG_RULE(5170-10, 5240+10, 40, 6, 20,
NL80211_RRF_NO_IR), NL80211_RRF_NO_IR),
/* IEEE 802.11a, channels 52..64 */ /* IEEE 802.11a, channels 52..64 */
REG_RULE(5260-20, 5320+20, 40, 6, 20, REG_RULE(5260-10, 5320+10, 40, 6, 20,
NL80211_RRF_NO_IR| NL80211_RRF_NO_IR|
NL80211_RRF_DFS), NL80211_RRF_DFS),
} }
@ -205,7 +205,7 @@ the data in regdb.c as an alternative to using CRDA.
The file net/wireless/db.txt should be kept up-to-date with the db.txt The file net/wireless/db.txt should be kept up-to-date with the db.txt
file available in the git repository here: file available in the git repository here:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git git://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git
Again, most users in most situations should be using the CRDA package Again, most users in most situations should be using the CRDA package
provided with their distribution, and in most other situations users provided with their distribution, and in most other situations users

View File

@ -2451,8 +2451,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
u32 phymode = __le32_to_cpu(resp->chan_change.phymode); u32 phymode = __le32_to_cpu(resp->chan_change.phymode);
u32 freq = __le32_to_cpu(resp->chan_change.freq); u32 freq = __le32_to_cpu(resp->chan_change.freq);
ar->tgt_oper_chan = ar->tgt_oper_chan = ieee80211_get_channel(ar->hw->wiphy, freq);
__ieee80211_get_channel(ar->hw->wiphy, freq);
ath10k_dbg(ar, ATH10K_DBG_HTT, ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt chan change freq %u phymode %s\n", "htt chan change freq %u phymode %s\n",
freq, ath10k_wmi_phymode_str(phymode)); freq, ath10k_wmi_phymode_str(phymode));

View File

@ -2078,7 +2078,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
ie_len = ie_buf[1] + sizeof(struct ieee_types_header); ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); band = mwifiex_band_to_radio_type(priv->curr_bss_params.band);
chan = __ieee80211_get_channel(priv->wdev.wiphy, chan = ieee80211_get_channel(priv->wdev.wiphy,
ieee80211_channel_to_frequency(bss_info.bss_chan, ieee80211_channel_to_frequency(bss_info.bss_chan,
band)); band));

View File

@ -185,6 +185,8 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
/* number of user priorities 802.11 uses */ /* number of user priorities 802.11 uses */
#define IEEE80211_NUM_UPS 8 #define IEEE80211_NUM_UPS 8
/* number of ACs */
#define IEEE80211_NUM_ACS 4
#define IEEE80211_QOS_CTL_LEN 2 #define IEEE80211_QOS_CTL_LEN 2
/* 1d tag mask */ /* 1d tag mask */

View File

@ -311,6 +311,34 @@ struct ieee80211_supported_band {
struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_vht_cap vht_cap;
}; };
/**
* wiphy_read_of_freq_limits - read frequency limits from device tree
*
* @wiphy: the wireless device to get extra limits for
*
* Some devices may have extra limitations specified in DT. This may be useful
* for chipsets that normally support more bands but are limited due to board
* design (e.g. by antennas or external power amplifier).
*
* This function reads info from DT and uses it to *modify* channels (disable
* unavailable ones). It's usually a *bad* idea to use it in drivers with
* shared channel data as DT limitations are device specific. You should make
* sure to call it only if channels in wiphy are copied and can be modified
* without affecting other devices.
*
* As this function access device node it has to be called after set_wiphy_dev.
* It also modifies channels so they have to be set first.
* If using this helper, call it before wiphy_register().
*/
#ifdef CONFIG_OF
void wiphy_read_of_freq_limits(struct wiphy *wiphy);
#else /* CONFIG_OF */
static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy)
{
}
#endif /* !CONFIG_OF */
/* /*
* Wireless hardware/device configuration structures and methods * Wireless hardware/device configuration structures and methods
*/ */
@ -1591,6 +1619,17 @@ struct cfg80211_sched_scan_plan {
u32 iterations; u32 iterations;
}; };
/**
* struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
*
* @band: band of BSS which should match for RSSI level adjustment.
* @delta: value of RSSI level adjustment.
*/
struct cfg80211_bss_select_adjust {
enum nl80211_band band;
s8 delta;
};
/** /**
* struct cfg80211_sched_scan_request - scheduled scan request description * struct cfg80211_sched_scan_request - scheduled scan request description
* *
@ -1626,6 +1665,16 @@ struct cfg80211_sched_scan_plan {
* cycle. The driver may ignore this parameter and start * cycle. The driver may ignore this parameter and start
* immediately (or at any other time), if this feature is not * immediately (or at any other time), if this feature is not
* supported. * supported.
* @relative_rssi_set: Indicates whether @relative_rssi is set or not.
* @relative_rssi: Relative RSSI threshold in dB to restrict scan result
* reporting in connected state to cases where a matching BSS is determined
* to have better or slightly worse RSSI than the current connected BSS.
* The relative RSSI threshold values are ignored in disconnected state.
* @rssi_adjust: delta dB of RSSI preference to be given to the BSSs that belong
* to the specified band while deciding whether a better BSS is reported
* using @relative_rssi. If delta is a negative number, the BSSs that
* belong to the specified band will be penalized by delta dB in relative
* comparisions.
*/ */
struct cfg80211_sched_scan_request { struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids; struct cfg80211_ssid *ssids;
@ -1645,6 +1694,10 @@ struct cfg80211_sched_scan_request {
u8 mac_addr[ETH_ALEN] __aligned(2); u8 mac_addr[ETH_ALEN] __aligned(2);
u8 mac_addr_mask[ETH_ALEN] __aligned(2); u8 mac_addr_mask[ETH_ALEN] __aligned(2);
bool relative_rssi_set;
s8 relative_rssi;
struct cfg80211_bss_select_adjust rssi_adjust;
/* internal */ /* internal */
struct wiphy *wiphy; struct wiphy *wiphy;
struct net_device *dev; struct net_device *dev;
@ -1952,17 +2005,6 @@ struct cfg80211_ibss_params {
struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_ht_cap ht_capa_mask;
}; };
/**
* struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
*
* @band: band of BSS which should match for RSSI level adjustment.
* @delta: value of RSSI level adjustment.
*/
struct cfg80211_bss_select_adjust {
enum nl80211_band band;
s8 delta;
};
/** /**
* struct cfg80211_bss_selection - connection parameters for BSS selection. * struct cfg80211_bss_selection - connection parameters for BSS selection.
* *
@ -3837,6 +3879,9 @@ struct cfg80211_cached_keys;
* @conn: (private) cfg80211 software SME connection state machine data * @conn: (private) cfg80211 software SME connection state machine data
* @connect_keys: (private) keys to set after connection is established * @connect_keys: (private) keys to set after connection is established
* @conn_bss_type: connecting/connected BSS type * @conn_bss_type: connecting/connected BSS type
* @conn_owner_nlportid: (private) connection owner socket port ID
* @disconnect_wk: (private) auto-disconnect work
* @disconnect_bssid: (private) the BSSID to use for auto-disconnect
* @ibss_fixed: (private) IBSS is using fixed BSSID * @ibss_fixed: (private) IBSS is using fixed BSSID
* @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
* @event_list: (private) list for internal event processing * @event_list: (private) list for internal event processing
@ -3868,6 +3913,10 @@ struct wireless_dev {
struct cfg80211_conn *conn; struct cfg80211_conn *conn;
struct cfg80211_cached_keys *connect_keys; struct cfg80211_cached_keys *connect_keys;
enum ieee80211_bss_type conn_bss_type; enum ieee80211_bss_type conn_bss_type;
u32 conn_owner_nlportid;
struct work_struct disconnect_wk;
u8 disconnect_bssid[ETH_ALEN];
struct list_head event_list; struct list_head event_list;
spinlock_t event_lock; spinlock_t event_lock;
@ -3955,26 +4004,15 @@ int ieee80211_channel_to_frequency(int chan, enum nl80211_band band);
*/ */
int ieee80211_frequency_to_channel(int freq); int ieee80211_frequency_to_channel(int freq);
/*
* Name indirection necessary because the ieee80211 code also has
* a function named "ieee80211_get_channel", so if you include
* cfg80211's header file you get cfg80211's version, if you try
* to include both header files you'll (rightfully!) get a symbol
* clash.
*/
struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
int freq);
/** /**
* ieee80211_get_channel - get channel struct from wiphy for specified frequency * ieee80211_get_channel - get channel struct from wiphy for specified frequency
*
* @wiphy: the struct wiphy to get the channel for * @wiphy: the struct wiphy to get the channel for
* @freq: the center frequency of the channel * @freq: the center frequency of the channel
*
* Return: The channel struct from @wiphy at @freq. * Return: The channel struct from @wiphy at @freq.
*/ */
static inline struct ieee80211_channel * struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq);
ieee80211_get_channel(struct wiphy *wiphy, int freq)
{
return __ieee80211_get_channel(wiphy, freq);
}
/** /**
* ieee80211_get_response_rate - get basic rate for a given rate * ieee80211_get_response_rate - get basic rate for a given rate
@ -5048,20 +5086,32 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
* @req_ie_len: association request IEs length * @req_ie_len: association request IEs length
* @resp_ie: association response IEs (may be %NULL) * @resp_ie: association response IEs (may be %NULL)
* @resp_ie_len: assoc response IEs length * @resp_ie_len: assoc response IEs length
* @status: status code, 0 for successful connection, use * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
* %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
* the real status code for failures. * the real status code for failures. If this call is used to report a
* failure due to a timeout (e.g., not receiving an Authentication frame
* from the AP) instead of an explicit rejection by the AP, -1 is used to
* indicate that this is a failure, but without a status code.
* @timeout_reason is used to report the reason for the timeout in that
* case.
* @gfp: allocation flags * @gfp: allocation flags
* @timeout_reason: reason for connection timeout. This is used when the
* connection fails due to a timeout instead of an explicit rejection from
* the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
* not known. This value is used only if @status < 0 to indicate that the
* failure is due to a timeout and not due to explicit rejection by the AP.
* This value is ignored in other cases (@status >= 0).
* *
* It should be called by the underlying driver whenever connect() has * It should be called by the underlying driver once execution of the connection
* succeeded. This is similar to cfg80211_connect_result(), but with the * request from connect() has been completed. This is similar to
* option of identifying the exact bss entry for the connection. Only one of * cfg80211_connect_result(), but with the option of identifying the exact bss
* these functions should be called. * entry for the connection. Only one of these functions should be called.
*/ */
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
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,
size_t resp_ie_len, int status, gfp_t gfp); size_t resp_ie_len, int status, gfp_t gfp,
enum nl80211_timeout_reason timeout_reason);
/** /**
* cfg80211_connect_result - notify cfg80211 of connection result * cfg80211_connect_result - notify cfg80211 of connection result
@ -5072,13 +5122,15 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
* @req_ie_len: association request IEs length * @req_ie_len: association request IEs length
* @resp_ie: association response IEs (may be %NULL) * @resp_ie: association response IEs (may be %NULL)
* @resp_ie_len: assoc response IEs length * @resp_ie_len: assoc response IEs length
* @status: status code, 0 for successful connection, use * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use
* %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
* the real status code for failures. * the real status code for failures.
* @gfp: allocation flags * @gfp: allocation flags
* *
* It should be called by the underlying driver whenever connect() has * It should be called by the underlying driver once execution of the connection
* succeeded. * request from connect() has been completed. This is similar to
* cfg80211_connect_bss() which allows the exact bss entry to be specified. Only
* one of these functions should be called.
*/ */
static inline void static inline void
cfg80211_connect_result(struct net_device *dev, const u8 *bssid, cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
@ -5087,7 +5139,8 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
u16 status, gfp_t gfp) u16 status, gfp_t gfp)
{ {
cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie, cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie,
resp_ie_len, status, gfp); resp_ie_len, status, gfp,
NL80211_TIMEOUT_UNSPECIFIED);
} }
/** /**
@ -5098,6 +5151,7 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
* @req_ie: association request IEs (maybe be %NULL) * @req_ie: association request IEs (maybe be %NULL)
* @req_ie_len: association request IEs length * @req_ie_len: association request IEs length
* @gfp: allocation flags * @gfp: allocation flags
* @timeout_reason: reason for connection timeout.
* *
* It should be called by the underlying driver whenever connect() has failed * It should be called by the underlying driver whenever connect() has failed
* in a sequence where no explicit authentication/association rejection was * in a sequence where no explicit authentication/association rejection was
@ -5107,10 +5161,11 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
*/ */
static inline void static inline void
cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len, gfp_t gfp) const u8 *req_ie, size_t req_ie_len, gfp_t gfp,
enum nl80211_timeout_reason timeout_reason)
{ {
cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1, cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1,
gfp); gfp, timeout_reason);
} }
/** /**

View File

@ -505,25 +505,8 @@ static inline int iwe_stream_event_len_adjust(struct iw_request_info *info,
/* /*
* Wrapper to add an Wireless Event to a stream of events. * Wrapper to add an Wireless Event to a stream of events.
*/ */
static inline char * char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends, char *ends, struct iw_event *iwe, int event_len);
struct iw_event *iwe, int event_len)
{
int lcp_len = iwe_stream_lcp_len(info);
event_len = iwe_stream_event_len_adjust(info, event_len);
/* Check if it's possible */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
/* Beware of alignement issues on 64 bits */
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + lcp_len, &iwe->u,
event_len - lcp_len);
stream += event_len;
}
return stream;
}
static inline char * static inline char *
iwe_stream_add_event_check(struct iw_request_info *info, char *stream, iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
@ -541,26 +524,8 @@ iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
* Wrapper to add an short Wireless Event containing a pointer to a * Wrapper to add an short Wireless Event containing a pointer to a
* stream of events. * stream of events.
*/ */
static inline char * char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends, char *ends, struct iw_event *iwe, char *extra);
struct iw_event *iwe, char *extra)
{
int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
int point_len = iwe_stream_point_len(info);
int lcp_len = iwe_stream_lcp_len(info);
/* Check if it's possible */
if(likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + lcp_len,
((char *) &iwe->u) + IW_EV_POINT_OFF,
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
memcpy(stream + point_len, extra, iwe->u.data.length);
stream += event_len;
}
return stream;
}
static inline char * static inline char *
iwe_stream_add_point_check(struct iw_request_info *info, char *stream, iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
@ -579,25 +544,8 @@ iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
* Be careful, this one is tricky to use properly : * Be careful, this one is tricky to use properly :
* At the first run, you need to have (value = event + IW_EV_LCP_LEN). * At the first run, you need to have (value = event + IW_EV_LCP_LEN).
*/ */
static inline char * char *iwe_stream_add_value(struct iw_request_info *info, char *event,
iwe_stream_add_value(struct iw_request_info *info, char *event, char *value, char *value, char *ends, struct iw_event *iwe,
char *ends, struct iw_event *iwe, int event_len) int event_len);
{
int lcp_len = iwe_stream_lcp_len(info);
/* Don't duplicate LCP */
event_len -= IW_EV_LCP_LEN;
/* Check if it's possible */
if(likely((value + event_len) < ends)) {
/* Add new value */
memcpy(value, &iwe->u, event_len);
value += event_len;
/* Patch LCP */
iwe->len = value - event;
memcpy(event, (char *) iwe, lcp_len);
}
return value;
}
#endif /* _IW_HANDLER_H */ #endif /* _IW_HANDLER_H */

View File

@ -147,7 +147,6 @@ enum ieee80211_ac_numbers {
IEEE80211_AC_BE = 2, IEEE80211_AC_BE = 2,
IEEE80211_AC_BK = 3, IEEE80211_AC_BK = 3,
}; };
#define IEEE80211_NUM_ACS 4
/** /**
* struct ieee80211_tx_queue_params - transmit queue configuration * struct ieee80211_tx_queue_params - transmit queue configuration
@ -1018,7 +1017,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_DECRYPTED: This frame was decrypted in hardware. * @RX_FLAG_DECRYPTED: This frame was decrypted in hardware.
* @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame, * @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame,
* verification has been done by the hardware. * verification has been done by the hardware.
* @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame. * @RX_FLAG_IV_STRIPPED: The IV and ICV are stripped from this frame.
* If this flag is set, the stack cannot do any replay detection * If this flag is set, the stack cannot do any replay detection
* hence the driver or hardware will have to do that. * hence the driver or hardware will have to do that.
* @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this * @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
@ -1089,6 +1088,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before. * @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
* This is used for AMSDU subframes which can have the same PN as * This is used for AMSDU subframes which can have the same PN as
* the first subframe. * the first subframe.
* @RX_FLAG_ICV_STRIPPED: The ICV is stripped from this frame. CRC checking must
* be done in the hardware.
*/ */
enum mac80211_rx_flags { enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0), RX_FLAG_MMIC_ERROR = BIT(0),
@ -1124,6 +1125,7 @@ enum mac80211_rx_flags {
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31), RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
RX_FLAG_MIC_STRIPPED = BIT_ULL(32), RX_FLAG_MIC_STRIPPED = BIT_ULL(32),
RX_FLAG_ALLOW_SAME_PN = BIT_ULL(33), RX_FLAG_ALLOW_SAME_PN = BIT_ULL(33),
RX_FLAG_ICV_STRIPPED = BIT_ULL(34),
}; };
#define RX_FLAG_STBC_SHIFT 26 #define RX_FLAG_STBC_SHIFT 26

View File

@ -1820,6 +1820,8 @@ enum nl80211_commands {
* and remove functions. NAN notifications will be sent in unicast to that * and remove functions. NAN notifications will be sent in unicast to that
* socket. Without this attribute, any socket can add functions and the * socket. Without this attribute, any socket can add functions and the
* notifications will be sent to the %NL80211_MCGRP_NAN multicast group. * notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
* If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
* station will deauthenticate when the socket is closed.
* *
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator. * the TDLS link initiator.
@ -1980,6 +1982,24 @@ enum nl80211_commands {
* @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
* used in various commands/events for specifying the BSSID. * used in various commands/events for specifying the BSSID.
* *
* @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
* other BSSs has to be better or slightly worse than the current
* connected BSS so that they get reported to user space.
* This will give an opportunity to userspace to consider connecting to
* other matching BSSs which have better or slightly worse RSSI than
* the current connected BSS by using an offloaded operation to avoid
* unnecessary wakeups.
*
* @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
* the specified band is to be adjusted before doing
* %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
* better BSSs. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
*
* @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
* u32 attribute with an &enum nl80211_timeout_reason value. This is used,
* e.g., with %NL80211_CMD_CONNECT event.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
@ -2386,6 +2406,11 @@ enum nl80211_attrs {
NL80211_ATTR_BSSID, NL80211_ATTR_BSSID,
NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
NL80211_ATTR_TIMEOUT_REASON,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -3078,6 +3103,13 @@ enum nl80211_reg_rule_attr {
* how this API was implemented in the past. Also, due to the same problem, * how this API was implemented in the past. Also, due to the same problem,
* the only way to create a matchset with only an RSSI filter (with this * the only way to create a matchset with only an RSSI filter (with this
* attribute) is if there's only a single matchset with the RSSI attribute. * attribute) is if there's only a single matchset with the RSSI attribute.
* @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
* %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
* relative to current bss's RSSI.
* @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
* BSS-es in the specified band is to be adjusted before doing
* RSSI-based BSS selection. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined * attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@ -3087,6 +3119,8 @@ enum nl80211_sched_scan_match_attr {
NL80211_SCHED_SCAN_MATCH_ATTR_SSID, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
/* keep last */ /* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@ -4697,6 +4731,13 @@ enum nl80211_feature_flags {
* configuration (AP/mesh) with VHT rates. * configuration (AP/mesh) with VHT rates.
* @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
* with user space SME (NL80211_CMD_AUTHENTICATE) in station mode. * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
* @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
* in @NL80211_CMD_FRAME while not associated.
* @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
* randomized TA in @NL80211_CMD_FRAME while associated.
* @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
* for reporting BSSs with better RSSI than the current connected BSS
* (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
* *
* @NUM_NL80211_EXT_FEATURES: number of extended features. * @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index. * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@ -4712,6 +4753,9 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_BEACON_RATE_HT, NL80211_EXT_FEATURE_BEACON_RATE_HT,
NL80211_EXT_FEATURE_BEACON_RATE_VHT, NL80211_EXT_FEATURE_BEACON_RATE_VHT,
NL80211_EXT_FEATURE_FILS_STA, NL80211_EXT_FEATURE_FILS_STA,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
/* add new features before the definition below */ /* add new features before the definition below */
NUM_NL80211_EXT_FEATURES, NUM_NL80211_EXT_FEATURES,
@ -4750,6 +4794,21 @@ enum nl80211_connect_failed_reason {
NL80211_CONN_FAIL_BLOCKED_CLIENT, NL80211_CONN_FAIL_BLOCKED_CLIENT,
}; };
/**
* enum nl80211_timeout_reason - timeout reasons
*
* @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
* @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
* @NL80211_TIMEOUT_AUTH: Authentication timed out.
* @NL80211_TIMEOUT_ASSOC: Association timed out.
*/
enum nl80211_timeout_reason {
NL80211_TIMEOUT_UNSPECIFIED,
NL80211_TIMEOUT_SCAN,
NL80211_TIMEOUT_AUTH,
NL80211_TIMEOUT_ASSOC,
};
/** /**
* enum nl80211_scan_flags - scan request control flags * enum nl80211_scan_flags - scan request control flags
* *
@ -4964,8 +5023,9 @@ enum nl80211_sched_scan_plan {
/** /**
* struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters. * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
* *
* @band: band of BSS that must match for RSSI value adjustment. * @band: band of BSS that must match for RSSI value adjustment. The value
* @delta: value used to adjust the RSSI value of matching BSS. * of this field is according to &enum nl80211_band.
* @delta: value used to adjust the RSSI value of matching BSS in dB.
*/ */
struct nl80211_bss_select_rssi_adjust { struct nl80211_bss_select_rssi_adjust {
__u8 band; __u8 band;

View File

@ -3563,6 +3563,17 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
} }
EXPORT_SYMBOL(ieee80211_nan_func_match); EXPORT_SYMBOL(ieee80211_nan_func_match);
static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
struct net_device *dev,
const bool enabled)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
sdata->u.ap.multicast_to_unicast = enabled;
return 0;
}
const struct cfg80211_ops mac80211_config_ops = { const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface, .add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface, .del_virtual_intf = ieee80211_del_iface,
@ -3653,4 +3664,5 @@ const struct cfg80211_ops mac80211_config_ops = {
.nan_change_conf = ieee80211_nan_change_conf, .nan_change_conf = ieee80211_nan_change_conf,
.add_nan_func = ieee80211_add_nan_func, .add_nan_func = ieee80211_add_nan_func,
.del_nan_func = ieee80211_del_nan_func, .del_nan_func = ieee80211_del_nan_func,
.set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
}; };

View File

@ -1270,7 +1270,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata, *sdata_tmp; struct ieee80211_sub_if_data *sdata, *sdata_tmp;
struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
struct ieee80211_chanctx *new_ctx = NULL; struct ieee80211_chanctx *new_ctx = NULL;
int i, err, n_assigned, n_reserved, n_ready; int err, n_assigned, n_reserved, n_ready;
int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
lockdep_assert_held(&local->mtx); lockdep_assert_held(&local->mtx);
@ -1391,8 +1391,6 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
* Update all structures, values and pointers to point to new channel * Update all structures, values and pointers to point to new channel
* context(s). * context(s).
*/ */
i = 0;
list_for_each_entry(ctx, &local->chanctx_list, list) { list_for_each_entry(ctx, &local->chanctx_list, list) {
if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
continue; continue;

View File

@ -243,6 +243,31 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
return rv; return rv;
} }
static ssize_t misc_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
/* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
char *buf = kzalloc(bufsz, GFP_KERNEL);
char *pos = buf, *end = buf + bufsz - 1;
ssize_t rv;
int i;
int ln;
pos += scnprintf(pos, end - pos, "pending:\n");
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
ln = skb_queue_len(&local->pending[i]);
pos += scnprintf(pos, end - pos, "[%i] %d\n",
i, ln);
}
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf);
return rv;
}
static ssize_t queues_read(struct file *file, char __user *user_buf, static ssize_t queues_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
@ -263,6 +288,7 @@ static ssize_t queues_read(struct file *file, char __user *user_buf,
DEBUGFS_READONLY_FILE_OPS(hwflags); DEBUGFS_READONLY_FILE_OPS(hwflags);
DEBUGFS_READONLY_FILE_OPS(queues); DEBUGFS_READONLY_FILE_OPS(queues);
DEBUGFS_READONLY_FILE_OPS(misc);
/* statistics stuff */ /* statistics stuff */
@ -331,6 +357,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(queues); DEBUGFS_ADD(queues);
DEBUGFS_ADD(misc);
#ifdef CONFIG_PM #ifdef CONFIG_PM
DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD_MODE(reset, 0200);
#endif #endif

View File

@ -519,6 +519,8 @@ static ssize_t ieee80211_if_fmt_aqm(
} }
IEEE80211_IF_FILE_R(aqm); IEEE80211_IF_FILE_R(aqm);
IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
/* IBSS attributes */ /* IBSS attributes */
static ssize_t ieee80211_if_fmt_tsf( static ssize_t ieee80211_if_fmt_tsf(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@ -683,6 +685,7 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(dtim_count);
DEBUGFS_ADD(num_buffered_multicast); DEBUGFS_ADD(num_buffered_multicast);
DEBUGFS_ADD_MODE(tkip_mic_test, 0200); DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
DEBUGFS_ADD_MODE(multicast_to_unicast, 0600);
} }
static void add_vlan_files(struct ieee80211_sub_if_data *sdata) static void add_vlan_files(struct ieee80211_sub_if_data *sdata)

View File

@ -297,6 +297,7 @@ struct ieee80211_if_ap {
driver_smps_mode; /* smps mode request */ driver_smps_mode; /* smps mode request */
struct work_struct request_smps_work; struct work_struct request_smps_work;
bool multicast_to_unicast;
}; };
struct ieee80211_if_wds { struct ieee80211_if_wds {
@ -624,8 +625,8 @@ struct ieee80211_mesh_sync_ops {
struct ieee80211_rx_status *rx_status); struct ieee80211_rx_status *rx_status);
/* should be called with beacon_data under RCU read lock */ /* should be called with beacon_data under RCU read lock */
void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata, void (*adjust_tsf)(struct ieee80211_sub_if_data *sdata,
struct beacon_data *beacon); struct beacon_data *beacon);
/* add other framework functions here */ /* add other framework functions here */
}; };
@ -688,7 +689,6 @@ struct ieee80211_if_mesh {
const struct ieee80211_mesh_sync_ops *sync_ops; const struct ieee80211_mesh_sync_ops *sync_ops;
s64 sync_offset_clockdrift_max; s64 sync_offset_clockdrift_max;
spinlock_t sync_offset_lock; spinlock_t sync_offset_lock;
bool adjusting_tbtt;
/* mesh power save */ /* mesh power save */
enum nl80211_mesh_power_mode nonpeer_pm; enum nl80211_mesh_power_mode nonpeer_pm;
int ps_peers_light_sleep; int ps_peers_light_sleep;

View File

@ -279,10 +279,6 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
*pos |= ifmsh->ps_peers_deep_sleep ? *pos |= ifmsh->ps_peers_deep_sleep ?
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
return 0; return 0;
} }
@ -850,7 +846,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ifmsh->mesh_cc_id = 0; /* Disabled */ ifmsh->mesh_cc_id = 0; /* Disabled */
/* register sync ops from extensible synchronization framework */ /* register sync ops from extensible synchronization framework */
ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
ifmsh->adjusting_tbtt = false;
ifmsh->sync_offset_clockdrift_max = 0; ifmsh->sync_offset_clockdrift_max = 0;
set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
ieee80211_mesh_root_setup(ifmsh); ieee80211_mesh_root_setup(ifmsh);
@ -1349,7 +1344,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
ieee80211_mesh_rootpath(sdata); ieee80211_mesh_rootpath(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_tsf(sdata);
if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
mesh_bss_info_changed(sdata); mesh_bss_info_changed(sdata);

View File

@ -341,7 +341,7 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
} }
void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata); void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata);
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata); void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata);
void ieee80211s_stop(void); void ieee80211s_stop(void);
#else #else
static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)

View File

@ -505,12 +505,14 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
/* Userspace handles station allocation */ /* Userspace handles station allocation */
if (sdata->u.mesh.user_mpm || if (sdata->u.mesh.user_mpm ||
sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
cfg80211_notify_new_peer_candidate(sdata->dev, addr, if (mesh_peer_accepts_plinks(elems) &&
elems->ie_start, mesh_plink_availables(sdata))
elems->total_len, cfg80211_notify_new_peer_candidate(sdata->dev, addr,
GFP_KERNEL); elems->ie_start,
else elems->total_len,
GFP_KERNEL);
} else
sta = __mesh_sta_info_alloc(sdata, addr); sta = __mesh_sta_info_alloc(sdata, addr);
return sta; return sta;

View File

@ -12,7 +12,7 @@
#include "mesh.h" #include "mesh.h"
#include "driver-ops.h" #include "driver-ops.h"
/* This is not in the standard. It represents a tolerable tbtt drift below /* This is not in the standard. It represents a tolerable tsf drift below
* which we do no TSF adjustment. * which we do no TSF adjustment.
*/ */
#define TOFFSET_MINIMUM_ADJUSTMENT 10 #define TOFFSET_MINIMUM_ADJUSTMENT 10
@ -46,7 +46,7 @@ static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
} }
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@ -57,12 +57,12 @@ void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
spin_lock_bh(&ifmsh->sync_offset_lock); spin_lock_bh(&ifmsh->sync_offset_lock);
if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting\n", msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting\n",
(long long) ifmsh->sync_offset_clockdrift_max); (long long) ifmsh->sync_offset_clockdrift_max);
tsfdelta = -ifmsh->sync_offset_clockdrift_max; tsfdelta = -ifmsh->sync_offset_clockdrift_max;
ifmsh->sync_offset_clockdrift_max = 0; ifmsh->sync_offset_clockdrift_max = 0;
} else { } else {
msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting by %llu\n", msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting by %llu\n",
(long long) ifmsh->sync_offset_clockdrift_max, (long long) ifmsh->sync_offset_clockdrift_max,
(unsigned long long) beacon_int_fraction); (unsigned long long) beacon_int_fraction);
tsfdelta = -beacon_int_fraction; tsfdelta = -beacon_int_fraction;
@ -123,7 +123,6 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
*/ */
if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", msync_dbg(sdata, "STA %pM : is adjusting TBTT\n",
sta->sta.addr); sta->sta.addr);
goto no_sync; goto no_sync;
@ -168,15 +167,13 @@ no_sync:
rcu_read_unlock(); rcu_read_unlock();
} }
static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, static void mesh_sync_offset_adjust_tsf(struct ieee80211_sub_if_data *sdata,
struct beacon_data *beacon) struct beacon_data *beacon)
{ {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 cap;
WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
WARN_ON(!rcu_read_lock_held()); WARN_ON(!rcu_read_lock_held());
cap = beacon->meshconf->meshconf_cap;
spin_lock_bh(&ifmsh->sync_offset_lock); spin_lock_bh(&ifmsh->sync_offset_lock);
@ -187,24 +184,16 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
* the tsf adjustment to the mesh tasklet * the tsf adjustment to the mesh tasklet
*/ */
msync_dbg(sdata, msync_dbg(sdata,
"TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", "TSF : kicking off TSF adjustment with clockdrift_max=%lld\n",
ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max);
set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags);
ifmsh->adjusting_tbtt = true;
} else { } else {
msync_dbg(sdata, msync_dbg(sdata,
"TBTT : max clockdrift=%lld; too small to adjust\n", "TSF : max clockdrift=%lld; too small to adjust\n",
(long long)ifmsh->sync_offset_clockdrift_max); (long long)ifmsh->sync_offset_clockdrift_max);
ifmsh->sync_offset_clockdrift_max = 0; ifmsh->sync_offset_clockdrift_max = 0;
ifmsh->adjusting_tbtt = false;
} }
spin_unlock_bh(&ifmsh->sync_offset_lock); spin_unlock_bh(&ifmsh->sync_offset_lock);
beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ?
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap :
~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap;
} }
static const struct sync_method sync_methods[] = { static const struct sync_method sync_methods[] = {
@ -212,7 +201,7 @@ static const struct sync_method sync_methods[] = {
.method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.ops = { .ops = {
.rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
.adjust_tbtt = &mesh_sync_offset_adjust_tbtt, .adjust_tsf = &mesh_sync_offset_adjust_tsf,
} }
}, },
}; };

View File

@ -1486,10 +1486,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local)
if (count == 1 && ieee80211_powersave_allowed(found)) { if (count == 1 && ieee80211_powersave_allowed(found)) {
u8 dtimper = found->u.mgd.dtim_period; u8 dtimper = found->u.mgd.dtim_period;
s32 beaconint_us;
beaconint_us = ieee80211_tu_to_usec(
found->vif.bss_conf.beacon_int);
timeout = local->dynamic_ps_forced_timeout; timeout = local->dynamic_ps_forced_timeout;
if (timeout < 0) if (timeout < 0)

View File

@ -159,21 +159,23 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
void void
minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
{ {
unsigned int cur_prob;
if (unlikely(mrs->attempts > 0)) { if (unlikely(mrs->attempts > 0)) {
mrs->sample_skipped = 0; mrs->sample_skipped = 0;
mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
if (unlikely(!mrs->att_hist)) { if (unlikely(!mrs->att_hist)) {
mrs->prob_ewma = mrs->cur_prob; mrs->prob_ewma = cur_prob;
} else { } else {
/* update exponential weighted moving variance */ /* update exponential weighted moving variance */
mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd, mrs->prob_ewmv = minstrel_ewmv(mrs->prob_ewmv,
mrs->cur_prob, cur_prob,
mrs->prob_ewma, mrs->prob_ewma,
EWMA_LEVEL); EWMA_LEVEL);
/*update exponential weighted moving avarage */ /*update exponential weighted moving avarage */
mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
mrs->cur_prob, cur_prob,
EWMA_LEVEL); EWMA_LEVEL);
} }
mrs->att_hist += mrs->attempts; mrs->att_hist += mrs->attempts;
@ -365,6 +367,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
return; return;
#endif #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))
return;
delta = (mi->total_packets * sampling_ratio / 100) - delta = (mi->total_packets * sampling_ratio / 100) -
(mi->sample_packets + mi->sample_deferred / 2); (mi->sample_packets + mi->sample_deferred / 2);

View File

@ -14,7 +14,7 @@
#define SAMPLE_COLUMNS 10 /* number of columns in sample table */ #define SAMPLE_COLUMNS 10 /* number of columns in sample table */
/* scaled fraction values */ /* scaled fraction values */
#define MINSTREL_SCALE 16 #define MINSTREL_SCALE 12
#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
@ -36,21 +36,16 @@ minstrel_ewma(int old, int new, int weight)
} }
/* /*
* Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation * Perform EWMV (Exponentially Weighted Moving Variance) calculation
*/ */
static inline int static inline int
minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight) minstrel_ewmv(int old_ewmv, int cur_prob, int prob_ewma, int weight)
{ {
int diff, incr, tmp_var; int diff, incr;
/* calculate exponential weighted moving variance */ diff = cur_prob - prob_ewma;
diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000);
incr = (EWMA_DIV - weight) * diff / EWMA_DIV; incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
tmp_var = old_ewmsd * old_ewmsd; return weight * (old_ewmv + MINSTREL_TRUNC(diff * incr)) / EWMA_DIV;
tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV;
/* return standard deviation */
return (u16) int_sqrt(tmp_var);
} }
struct minstrel_rate_stats { struct minstrel_rate_stats {
@ -59,15 +54,13 @@ struct minstrel_rate_stats {
u16 success, last_success; u16 success, last_success;
/* total attempts/success counters */ /* total attempts/success counters */
u64 att_hist, succ_hist; u32 att_hist, succ_hist;
/* statistis of packet delivery probability /* statistis of packet delivery probability
* cur_prob - current prob within last update intervall
* prob_ewma - exponential weighted moving average of prob * prob_ewma - exponential weighted moving average of prob
* prob_ewmsd - exp. weighted moving standard deviation of prob */ * prob_ewmsd - exp. weighted moving standard deviation of prob */
unsigned int cur_prob; u16 prob_ewma;
unsigned int prob_ewma; u16 prob_ewmv;
u16 prob_ewmsd;
/* maximum retry counts */ /* maximum retry counts */
u8 retry_count; u8 retry_count;
@ -153,6 +146,14 @@ struct minstrel_debugfs_info {
char buf[]; char buf[];
}; };
/* Get EWMSD (Exponentially Weighted Moving Standard Deviation) * 10 */
static inline int
minstrel_get_ewmsd10(struct minstrel_rate_stats *mrs)
{
unsigned int ewmv = mrs->prob_ewmv;
return int_sqrt(MINSTREL_TRUNC(ewmv * 1000 * 1000));
}
extern const struct rate_control_ops mac80211_minstrel; extern const struct rate_control_ops mac80211_minstrel;
void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);

View File

@ -75,7 +75,7 @@ minstrel_stats_open(struct inode *inode, struct file *file)
{ {
struct minstrel_sta_info *mi = inode->i_private; struct minstrel_sta_info *mi = inode->i_private;
struct minstrel_debugfs_info *ms; struct minstrel_debugfs_info *ms;
unsigned int i, tp_max, tp_avg, prob, eprob; unsigned int i, tp_max, tp_avg, eprob;
char *p; char *p;
ms = kmalloc(2048, GFP_KERNEL); ms = kmalloc(2048, GFP_KERNEL);
@ -86,13 +86,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
p = ms->buf; p = ms->buf;
p += sprintf(p, "\n"); p += sprintf(p, "\n");
p += sprintf(p, p += sprintf(p,
"best __________rate_________ ________statistics________ ________last_______ ______sum-of________\n"); "best __________rate_________ ________statistics________ ____last_____ ______sum-of________\n");
p += sprintf(p, p += sprintf(p,
"rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [prob.|retry|suc|att] [#success | #attempts]\n"); "rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [retry|suc|att] [#success | #attempts]\n");
for (i = 0; i < mi->n_rates; i++) { for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats; struct minstrel_rate_stats *mrs = &mi->r[i].stats;
unsigned int prob_ewmsd;
*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
@ -107,17 +108,16 @@ minstrel_stats_open(struct inode *inode, struct file *file)
tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
prob_ewmsd = minstrel_get_ewmsd10(mrs);
p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
" %3u.%1u %3u %3u %-3u " " %3u %3u %-3u "
"%9llu %-9llu\n", "%9llu %-9llu\n",
tp_max / 10, tp_max % 10, tp_max / 10, tp_max % 10,
tp_avg / 10, tp_avg % 10, tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10, eprob / 10, eprob % 10,
mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob_ewmsd / 10, prob_ewmsd % 10,
prob / 10, prob % 10,
mrs->retry_count, mrs->retry_count,
mrs->last_success, mrs->last_success,
mrs->last_attempts, mrs->last_attempts,
@ -148,7 +148,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
{ {
struct minstrel_sta_info *mi = inode->i_private; struct minstrel_sta_info *mi = inode->i_private;
struct minstrel_debugfs_info *ms; struct minstrel_debugfs_info *ms;
unsigned int i, tp_max, tp_avg, prob, eprob; unsigned int i, tp_max, tp_avg, eprob;
char *p; char *p;
ms = kmalloc(2048, GFP_KERNEL); ms = kmalloc(2048, GFP_KERNEL);
@ -161,6 +161,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
for (i = 0; i < mi->n_rates; i++) { for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats; struct minstrel_rate_stats *mrs = &mi->r[i].stats;
unsigned int prob_ewmsd;
p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : "")); p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : "")); p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
@ -175,16 +176,15 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file)
tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
prob_ewmsd = minstrel_get_ewmsd10(mrs);
p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
"%llu,%llu,%d,%d\n", "%llu,%llu,%d,%d\n",
tp_max / 10, tp_max % 10, tp_max / 10, tp_max % 10,
tp_avg / 10, tp_avg % 10, tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10, eprob / 10, eprob % 10,
mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob_ewmsd / 10, prob_ewmsd % 10,
prob / 10, prob % 10,
mrs->retry_count, mrs->retry_count,
mrs->last_success, mrs->last_success,
mrs->last_attempts, mrs->last_attempts,

View File

@ -14,6 +14,7 @@
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include "rate.h" #include "rate.h"
#include "sta_info.h"
#include "rc80211_minstrel.h" #include "rc80211_minstrel.h"
#include "rc80211_minstrel_ht.h" #include "rc80211_minstrel_ht.h"
@ -154,67 +155,47 @@ MODULE_PARM_DESC(minstrel_vht_only,
const struct mcs_group minstrel_mcs_groups[] = { const struct mcs_group minstrel_mcs_groups[] = {
MCS_GROUP(1, 0, BW_20), MCS_GROUP(1, 0, BW_20),
MCS_GROUP(2, 0, BW_20), MCS_GROUP(2, 0, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP(3, 0, BW_20), MCS_GROUP(3, 0, BW_20),
#endif
MCS_GROUP(1, 1, BW_20), MCS_GROUP(1, 1, BW_20),
MCS_GROUP(2, 1, BW_20), MCS_GROUP(2, 1, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP(3, 1, BW_20), MCS_GROUP(3, 1, BW_20),
#endif
MCS_GROUP(1, 0, BW_40), MCS_GROUP(1, 0, BW_40),
MCS_GROUP(2, 0, BW_40), MCS_GROUP(2, 0, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP(3, 0, BW_40), MCS_GROUP(3, 0, BW_40),
#endif
MCS_GROUP(1, 1, BW_40), MCS_GROUP(1, 1, BW_40),
MCS_GROUP(2, 1, BW_40), MCS_GROUP(2, 1, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP(3, 1, BW_40), MCS_GROUP(3, 1, BW_40),
#endif
CCK_GROUP, CCK_GROUP,
#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT #ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
VHT_GROUP(1, 0, BW_20), VHT_GROUP(1, 0, BW_20),
VHT_GROUP(2, 0, BW_20), VHT_GROUP(2, 0, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 0, BW_20), VHT_GROUP(3, 0, BW_20),
#endif
VHT_GROUP(1, 1, BW_20), VHT_GROUP(1, 1, BW_20),
VHT_GROUP(2, 1, BW_20), VHT_GROUP(2, 1, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 1, BW_20), VHT_GROUP(3, 1, BW_20),
#endif
VHT_GROUP(1, 0, BW_40), VHT_GROUP(1, 0, BW_40),
VHT_GROUP(2, 0, BW_40), VHT_GROUP(2, 0, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 0, BW_40), VHT_GROUP(3, 0, BW_40),
#endif
VHT_GROUP(1, 1, BW_40), VHT_GROUP(1, 1, BW_40),
VHT_GROUP(2, 1, BW_40), VHT_GROUP(2, 1, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 1, BW_40), VHT_GROUP(3, 1, BW_40),
#endif
VHT_GROUP(1, 0, BW_80), VHT_GROUP(1, 0, BW_80),
VHT_GROUP(2, 0, BW_80), VHT_GROUP(2, 0, BW_80),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 0, BW_80), VHT_GROUP(3, 0, BW_80),
#endif
VHT_GROUP(1, 1, BW_80), VHT_GROUP(1, 1, BW_80),
VHT_GROUP(2, 1, BW_80), VHT_GROUP(2, 1, BW_80),
#if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP(3, 1, BW_80), VHT_GROUP(3, 1, BW_80),
#endif #endif
#endif
}; };
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
@ -301,7 +282,7 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
break; break;
/* short preamble */ /* short preamble */
if (!(mi->groups[group].supported & BIT(idx))) if (!(mi->supported[group] & BIT(idx)))
idx += 4; idx += 4;
} }
return &mi->groups[group].rates[idx]; return &mi->groups[group].rates[idx];
@ -486,7 +467,7 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
MCS_GROUP_RATES].streams; MCS_GROUP_RATES].streams;
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group]; mg = &mi->groups[group];
if (!mg->supported || group == MINSTREL_CCK_GROUP) if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
continue; continue;
tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
@ -540,7 +521,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group]; mg = &mi->groups[group];
if (!mg->supported) if (!mi->supported[group])
continue; continue;
mi->sample_count++; mi->sample_count++;
@ -550,7 +531,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
tmp_group_tp_rate[j] = group; tmp_group_tp_rate[j] = group;
for (i = 0; i < MCS_GROUP_RATES; i++) { for (i = 0; i < MCS_GROUP_RATES; i++) {
if (!(mg->supported & BIT(i))) if (!(mi->supported[group] & BIT(i)))
continue; continue;
index = MCS_GROUP_RATES * group + i; index = MCS_GROUP_RATES * group + i;
@ -636,7 +617,7 @@ minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi)
mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups);
mg = &mi->groups[mi->sample_group]; mg = &mi->groups[mi->sample_group];
if (!mg->supported) if (!mi->supported[mi->sample_group])
continue; continue;
if (++mg->index >= MCS_GROUP_RATES) { if (++mg->index >= MCS_GROUP_RATES) {
@ -657,7 +638,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
while (group > 0) { while (group > 0) {
group--; group--;
if (!mi->groups[group].supported) if (!mi->supported[group])
continue; continue;
if (minstrel_mcs_groups[group].streams > if (minstrel_mcs_groups[group].streams >
@ -994,7 +975,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
sample_idx = sample_table[mg->column][mg->index]; sample_idx = sample_table[mg->column][mg->index];
minstrel_set_next_sample_idx(mi); minstrel_set_next_sample_idx(mi);
if (!(mg->supported & BIT(sample_idx))) if (!(mi->supported[sample_group] & BIT(sample_idx)))
return -1; return -1;
mrs = &mg->rates[sample_idx]; mrs = &mg->rates[sample_idx];
@ -1048,22 +1029,6 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
return sample_idx; return sample_idx;
} }
static void
minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp,
struct minstrel_ht_sta *mi, bool val)
{
u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported;
if (!supported || !mi->cck_supported_short)
return;
if (supported & (mi->cck_supported_short << (val * 4)))
return;
supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4);
mi->groups[MINSTREL_CCK_GROUP].supported = supported;
}
static void static void
minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
struct ieee80211_tx_rate_control *txrc) struct ieee80211_tx_rate_control *txrc)
@ -1087,7 +1052,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
minstrel_aggr_check(sta, txrc->skb); minstrel_aggr_check(sta, txrc->skb);
info->flags |= mi->tx_flags; info->flags |= mi->tx_flags;
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
if (mp->fixed_rate_idx != -1) if (mp->fixed_rate_idx != -1)
@ -1154,7 +1118,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
mi->cck_supported_short |= BIT(i); mi->cck_supported_short |= BIT(i);
} }
mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported; mi->supported[MINSTREL_CCK_GROUP] = mi->cck_supported;
} }
static void static void
@ -1168,6 +1132,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
u16 sta_cap = sta->ht_cap.cap; u16 sta_cap = sta->ht_cap.cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
struct sta_info *sinfo = container_of(sta, struct sta_info, sta);
int use_vht; int use_vht;
int n_supported = 0; int n_supported = 0;
int ack_dur; int ack_dur;
@ -1224,7 +1189,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
u32 gflags = minstrel_mcs_groups[i].flags; u32 gflags = minstrel_mcs_groups[i].flags;
int bw, nss; int bw, nss;
mi->groups[i].supported = 0; mi->supported[i] = 0;
if (i == MINSTREL_CCK_GROUP) { if (i == MINSTREL_CCK_GROUP) {
minstrel_ht_update_cck(mp, mi, sband, sta); minstrel_ht_update_cck(mp, mi, sband, sta);
continue; continue;
@ -1256,8 +1221,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if (use_vht && minstrel_vht_only) if (use_vht && minstrel_vht_only)
continue; continue;
#endif #endif
mi->groups[i].supported = mcs->rx_mask[nss - 1]; mi->supported[i] = mcs->rx_mask[nss - 1];
if (mi->groups[i].supported) if (mi->supported[i])
n_supported++; n_supported++;
continue; continue;
} }
@ -1283,16 +1248,19 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
else else
bw = BW_20; bw = BW_20;
mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss, mi->supported[i] = minstrel_get_valid_vht_rates(bw, nss,
vht_cap->vht_mcs.tx_mcs_map); vht_cap->vht_mcs.tx_mcs_map);
if (mi->groups[i].supported) if (mi->supported[i])
n_supported++; n_supported++;
} }
if (!n_supported) if (!n_supported)
goto use_legacy; goto use_legacy;
if (test_sta_flag(sinfo, WLAN_STA_SHORT_PREAMBLE))
mi->cck_supported_short |= mi->cck_supported_short << 4;
/* create an initial rate table with the lowest supported rates */ /* create an initial rate table with the lowest supported rates */
minstrel_ht_update_stats(mp, mi); minstrel_ht_update_stats(mp, mi);
minstrel_ht_update_rates(mp, mi); minstrel_ht_update_rates(mp, mi);

View File

@ -52,9 +52,6 @@ struct minstrel_mcs_group_data {
u8 index; u8 index;
u8 column; u8 column;
/* bitfield of supported MCS rates of this group */
u16 supported;
/* sorted rate set within a MCS group*/ /* sorted rate set within a MCS group*/
u16 max_group_tp_rate[MAX_THR_RATES]; u16 max_group_tp_rate[MAX_THR_RATES];
u16 max_group_prob_rate; u16 max_group_prob_rate;
@ -101,6 +98,9 @@ struct minstrel_ht_sta {
u8 cck_supported; u8 cck_supported;
u8 cck_supported_short; u8 cck_supported_short;
/* Bitfield of supported MCS rates of all groups */
u16 supported[MINSTREL_GROUPS_NB];
/* MCS rate group info and statistics */ /* MCS rate group info and statistics */
struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
}; };

View File

@ -19,12 +19,12 @@ static char *
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
{ {
const struct mcs_group *mg; const struct mcs_group *mg;
unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; unsigned int j, tp_max, tp_avg, eprob, tx_time;
char htmode = '2'; char htmode = '2';
char gimode = 'L'; char gimode = 'L';
u32 gflags; u32 gflags;
if (!mi->groups[i].supported) if (!mi->supported[i])
return p; return p;
mg = &minstrel_mcs_groups[i]; mg = &minstrel_mcs_groups[i];
@ -41,8 +41,9 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
static const int bitrates[4] = { 10, 20, 55, 110 }; static const int bitrates[4] = { 10, 20, 55, 110 };
int idx = i * MCS_GROUP_RATES + j; int idx = i * MCS_GROUP_RATES + j;
unsigned int prob_ewmsd;
if (!(mi->groups[i].supported & BIT(j))) if (!(mi->supported[i] & BIT(j)))
continue; continue;
if (gflags & IEEE80211_TX_RC_MCS) { if (gflags & IEEE80211_TX_RC_MCS) {
@ -83,17 +84,16 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
prob_ewmsd = minstrel_get_ewmsd10(mrs);
p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
" %3u.%1u %3u %3u %-3u " " %3u %3u %-3u "
"%9llu %-9llu\n", "%9llu %-9llu\n",
tp_max / 10, tp_max % 10, tp_max / 10, tp_max % 10,
tp_avg / 10, tp_avg % 10, tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10, eprob / 10, eprob % 10,
mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob_ewmsd / 10, prob_ewmsd % 10,
prob / 10, prob % 10,
mrs->retry_count, mrs->retry_count,
mrs->last_success, mrs->last_success,
mrs->last_attempts, mrs->last_attempts,
@ -130,9 +130,9 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
p += sprintf(p, "\n"); p += sprintf(p, "\n");
p += sprintf(p, p += sprintf(p,
" best ____________rate__________ ________statistics________ ________last_______ ______sum-of________\n"); " best ____________rate__________ ________statistics________ _____last____ ______sum-of________\n");
p += sprintf(p, p += sprintf(p,
"mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [prob.|retry|suc|att] [#success | #attempts]\n"); "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob) sd(prob)] [retry|suc|att] [#success | #attempts]\n");
p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
for (i = 0; i < MINSTREL_CCK_GROUP; i++) for (i = 0; i < MINSTREL_CCK_GROUP; i++)
@ -165,12 +165,12 @@ static char *
minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
{ {
const struct mcs_group *mg; const struct mcs_group *mg;
unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; unsigned int j, tp_max, tp_avg, eprob, tx_time;
char htmode = '2'; char htmode = '2';
char gimode = 'L'; char gimode = 'L';
u32 gflags; u32 gflags;
if (!mi->groups[i].supported) if (!mi->supported[i])
return p; return p;
mg = &minstrel_mcs_groups[i]; mg = &minstrel_mcs_groups[i];
@ -187,8 +187,9 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
static const int bitrates[4] = { 10, 20, 55, 110 }; static const int bitrates[4] = { 10, 20, 55, 110 };
int idx = i * MCS_GROUP_RATES + j; int idx = i * MCS_GROUP_RATES + j;
unsigned int prob_ewmsd;
if (!(mi->groups[i].supported & BIT(j))) if (!(mi->supported[i] & BIT(j)))
continue; continue;
if (gflags & IEEE80211_TX_RC_MCS) { if (gflags & IEEE80211_TX_RC_MCS) {
@ -226,16 +227,15 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
prob_ewmsd = minstrel_get_ewmsd10(mrs);
p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
"%u,%llu,%llu,", "%u,%llu,%llu,",
tp_max / 10, tp_max % 10, tp_max / 10, tp_max % 10,
tp_avg / 10, tp_avg % 10, tp_avg / 10, tp_avg % 10,
eprob / 10, eprob % 10, eprob / 10, eprob % 10,
mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob_ewmsd / 10, prob_ewmsd % 10,
prob / 10, prob % 10,
mrs->retry_count, mrs->retry_count,
mrs->last_success, mrs->last_success,
mrs->last_attempts, mrs->last_attempts,

View File

@ -1908,7 +1908,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
unsigned int frag, seq; unsigned int frag, seq;
struct ieee80211_fragment_entry *entry; struct ieee80211_fragment_entry *entry;
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_rx_status *status;
hdr = (struct ieee80211_hdr *)rx->skb->data; hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control; fc = hdr->frame_control;
@ -2034,9 +2033,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
dev_kfree_skb(skb); dev_kfree_skb(skb);
} }
/* Complete frame has been reassembled - process it now */
status = IEEE80211_SKB_RXCB(rx->skb);
out: out:
ieee80211_led_rx(rx->local); ieee80211_led_rx(rx->local);
out_no_led: out_no_led:

View File

@ -1120,7 +1120,6 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
u32 rate_masks[NUM_NL80211_BANDS] = {}; u32 rate_masks[NUM_NL80211_BANDS] = {};
u8 bands_used = 0; u8 bands_used = 0;
u8 *ie; u8 *ie;
size_t len;
iebufsz = local->scan_ies_len + req->ie_len; iebufsz = local->scan_ies_len + req->ie_len;
@ -1145,10 +1144,9 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
ieee80211_prepare_scan_chandef(&chandef, req->scan_width); ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz, ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
&sched_scan_ies, req->ie, &sched_scan_ies, req->ie,
req->ie_len, bands_used, req->ie_len, bands_used, rate_masks, &chandef);
rate_masks, &chandef);
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
if (ret == 0) { if (ret == 0) {

View File

@ -513,23 +513,23 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
{ {
struct ieee80211_local *local = sta->local; struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
struct station_info *sinfo; struct station_info *sinfo = NULL;
int err = 0; int err = 0;
lockdep_assert_held(&local->sta_mtx); lockdep_assert_held(&local->sta_mtx);
sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
if (!sinfo) {
err = -ENOMEM;
goto out_err;
}
/* check if STA exists already */ /* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) { if (sta_info_get_bss(sdata, sta->sta.addr)) {
err = -EEXIST; err = -EEXIST;
goto out_err; goto out_err;
} }
sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
if (!sinfo) {
err = -ENOMEM;
goto out_err;
}
local->num_sta++; local->num_sta++;
local->sta_generation++; local->sta_generation++;
smp_mb(); smp_mb();
@ -2051,16 +2051,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct rate_control_ref *ref = NULL;
u32 thr = 0; u32 thr = 0;
int i, ac, cpu; int i, ac, cpu;
struct ieee80211_sta_rx_stats *last_rxstats; struct ieee80211_sta_rx_stats *last_rxstats;
last_rxstats = sta_get_last_rx_stats(sta); last_rxstats = sta_get_last_rx_stats(sta);
if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
ref = local->rate_ctrl;
sinfo->generation = sdata->local->sta_generation; sinfo->generation = sdata->local->sta_generation;
/* do before driver, so beacon filtering drivers have a /* do before driver, so beacon filtering drivers have a

View File

@ -541,6 +541,11 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
} else if (info->ack_frame_id) { } else if (info->ack_frame_id) {
ieee80211_report_ack_skb(local, info, acked, dropped); ieee80211_report_ack_skb(local, info, acked, dropped);
} }
if (!dropped && skb->destructor) {
skb->wifi_acked_valid = 1;
skb->wifi_acked = acked;
}
} }
/* /*
@ -633,10 +638,9 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
int retry_count; int retry_count;
int rates_idx;
bool acked, noack_success; bool acked, noack_success;
rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); ieee80211_tx_get_rates(hw, info, &retry_count);
sband = hw->wiphy->bands[info->band]; sband = hw->wiphy->bands[info->band];

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_vlan.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
@ -63,6 +64,10 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
u32 rate_flags = 0; u32 rate_flags = 0;
/* assume HW handles this */
if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
return 0;
rcu_read_lock(); rcu_read_lock();
chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf); chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
if (chanctx_conf) { if (chanctx_conf) {
@ -71,10 +76,6 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
} }
rcu_read_unlock(); rcu_read_unlock();
/* assume HW handles this */
if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
return 0;
/* uh huh? */ /* uh huh? */
if (WARN_ON_ONCE(tx->rate.idx < 0)) if (WARN_ON_ONCE(tx->rate.idx < 0))
return 0; return 0;
@ -3574,6 +3575,115 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
rcu_read_unlock(); rcu_read_unlock();
} }
static int ieee80211_change_da(struct sk_buff *skb, struct sta_info *sta)
{
struct ethhdr *eth;
int err;
err = skb_ensure_writable(skb, ETH_HLEN);
if (unlikely(err))
return err;
eth = (void *)skb->data;
ether_addr_copy(eth->h_dest, sta->sta.addr);
return 0;
}
static bool ieee80211_multicast_to_unicast(struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
const struct ethhdr *eth = (void *)skb->data;
const struct vlan_ethhdr *ethvlan = (void *)skb->data;
__be16 ethertype;
if (likely(!is_multicast_ether_addr(eth->h_dest)))
return false;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
if (sdata->u.vlan.sta)
return false;
if (sdata->wdev.use_4addr)
return false;
/* fall through */
case NL80211_IFTYPE_AP:
/* check runtime toggle for this bss */
if (!sdata->bss->multicast_to_unicast)
return false;
break;
default:
return false;
}
/* multicast to unicast conversion only for some payload */
ethertype = eth->h_proto;
if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN)
ethertype = ethvlan->h_vlan_encapsulated_proto;
switch (ethertype) {
case htons(ETH_P_ARP):
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
break;
default:
return false;
}
return true;
}
static void
ieee80211_convert_to_unicast(struct sk_buff *skb, struct net_device *dev,
struct sk_buff_head *queue)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
const struct ethhdr *eth = (struct ethhdr *)skb->data;
struct sta_info *sta, *first = NULL;
struct sk_buff *cloned_skb;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata)
/* AP-VLAN mismatch */
continue;
if (unlikely(ether_addr_equal(eth->h_source, sta->sta.addr)))
/* do not send back to source */
continue;
if (!first) {
first = sta;
continue;
}
cloned_skb = skb_clone(skb, GFP_ATOMIC);
if (!cloned_skb)
goto multicast;
if (unlikely(ieee80211_change_da(cloned_skb, sta))) {
dev_kfree_skb(cloned_skb);
goto multicast;
}
__skb_queue_tail(queue, cloned_skb);
}
if (likely(first)) {
if (unlikely(ieee80211_change_da(skb, first)))
goto multicast;
__skb_queue_tail(queue, skb);
} else {
/* no STA connected, drop */
kfree_skb(skb);
skb = NULL;
}
goto out;
multicast:
__skb_queue_purge(queue);
__skb_queue_tail(queue, skb);
out:
rcu_read_unlock();
}
/** /**
* ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
* @skb: packet to be sent * @skb: packet to be sent
@ -3584,7 +3694,17 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {
__ieee80211_subif_start_xmit(skb, dev, 0); if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) {
struct sk_buff_head queue;
__skb_queue_head_init(&queue);
ieee80211_convert_to_unicast(skb, dev, &queue);
while ((skb = __skb_dequeue(&queue)))
__ieee80211_subif_start_xmit(skb, dev, 0);
} else {
__ieee80211_subif_start_xmit(skb, dev, 0);
}
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
@ -4077,7 +4197,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
} }
if (ifmsh->sync_ops) if (ifmsh->sync_ops)
ifmsh->sync_ops->adjust_tbtt(sdata, beacon); ifmsh->sync_ops->adjust_tsf(sdata, beacon);
skb = dev_alloc_skb(local->tx_headroom + skb = dev_alloc_skb(local->tx_headroom +
beacon->head_len + beacon->head_len +

View File

@ -436,14 +436,10 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode, struct sta_info *sta, u8 opmode,
enum nl80211_band band) enum nl80211_band band)
{ {
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
enum ieee80211_sta_rx_bandwidth new_bw; enum ieee80211_sta_rx_bandwidth new_bw;
u32 changed = 0; u32 changed = 0;
u8 nss; u8 nss;
sband = local->hw.wiphy->bands[band];
/* ignore - no support for BF yet */ /* ignore - no support for BF yet */
if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
return 0; return 0;

View File

@ -293,7 +293,8 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */ /* remove ICV */
if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN)) if (!(status->flag & RX_FLAG_ICV_STRIPPED) &&
pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }

View File

@ -294,7 +294,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
/* Trim ICV */ /* Trim ICV */
skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN); if (!(status->flag & RX_FLAG_ICV_STRIPPED))
skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
/* Remove IV */ /* Remove IV */
memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen); memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);

View File

@ -176,6 +176,50 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
{ {
led_trigger_unregister(&rfkill->led_trigger); led_trigger_unregister(&rfkill->led_trigger);
} }
static struct led_trigger rfkill_any_led_trigger;
static struct work_struct rfkill_any_work;
static void rfkill_any_led_trigger_worker(struct work_struct *work)
{
enum led_brightness brightness = LED_OFF;
struct rfkill *rfkill;
mutex_lock(&rfkill_global_mutex);
list_for_each_entry(rfkill, &rfkill_list, node) {
if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
brightness = LED_FULL;
break;
}
}
mutex_unlock(&rfkill_global_mutex);
led_trigger_event(&rfkill_any_led_trigger, brightness);
}
static void rfkill_any_led_trigger_event(void)
{
schedule_work(&rfkill_any_work);
}
static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev)
{
rfkill_any_led_trigger_event();
}
static int rfkill_any_led_trigger_register(void)
{
INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker);
rfkill_any_led_trigger.name = "rfkill-any";
rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate;
return led_trigger_register(&rfkill_any_led_trigger);
}
static void rfkill_any_led_trigger_unregister(void)
{
led_trigger_unregister(&rfkill_any_led_trigger);
cancel_work_sync(&rfkill_any_work);
}
#else #else
static void rfkill_led_trigger_event(struct rfkill *rfkill) static void rfkill_led_trigger_event(struct rfkill *rfkill)
{ {
@ -189,6 +233,19 @@ static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill) static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
{ {
} }
static void rfkill_any_led_trigger_event(void)
{
}
static int rfkill_any_led_trigger_register(void)
{
return 0;
}
static void rfkill_any_led_trigger_unregister(void)
{
}
#endif /* CONFIG_RFKILL_LEDS */ #endif /* CONFIG_RFKILL_LEDS */
static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill, static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
@ -297,6 +354,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
spin_unlock_irqrestore(&rfkill->lock, flags); spin_unlock_irqrestore(&rfkill->lock, flags);
rfkill_led_trigger_event(rfkill); rfkill_led_trigger_event(rfkill);
rfkill_any_led_trigger_event();
if (prev != curr) if (prev != curr)
rfkill_event(rfkill); rfkill_event(rfkill);
@ -477,11 +535,9 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
spin_unlock_irqrestore(&rfkill->lock, flags); spin_unlock_irqrestore(&rfkill->lock, flags);
rfkill_led_trigger_event(rfkill); rfkill_led_trigger_event(rfkill);
rfkill_any_led_trigger_event();
if (!rfkill->registered) if (rfkill->registered && prev != blocked)
return ret;
if (prev != blocked)
schedule_work(&rfkill->uevent_work); schedule_work(&rfkill->uevent_work);
return ret; return ret;
@ -523,6 +579,7 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
schedule_work(&rfkill->uevent_work); schedule_work(&rfkill->uevent_work);
rfkill_led_trigger_event(rfkill); rfkill_led_trigger_event(rfkill);
rfkill_any_led_trigger_event();
return blocked; return blocked;
} }
@ -572,6 +629,7 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
schedule_work(&rfkill->uevent_work); schedule_work(&rfkill->uevent_work);
rfkill_led_trigger_event(rfkill); rfkill_led_trigger_event(rfkill);
rfkill_any_led_trigger_event();
} }
} }
EXPORT_SYMBOL(rfkill_set_states); EXPORT_SYMBOL(rfkill_set_states);
@ -988,6 +1046,7 @@ int __must_check rfkill_register(struct rfkill *rfkill)
#endif #endif
} }
rfkill_any_led_trigger_event();
rfkill_send_events(rfkill, RFKILL_OP_ADD); rfkill_send_events(rfkill, RFKILL_OP_ADD);
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
@ -1020,6 +1079,7 @@ void rfkill_unregister(struct rfkill *rfkill)
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
rfkill_send_events(rfkill, RFKILL_OP_DEL); rfkill_send_events(rfkill, RFKILL_OP_DEL);
list_del_init(&rfkill->node); list_del_init(&rfkill->node);
rfkill_any_led_trigger_event();
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
rfkill_led_trigger_unregister(rfkill); rfkill_led_trigger_unregister(rfkill);
@ -1266,24 +1326,33 @@ static int __init rfkill_init(void)
error = class_register(&rfkill_class); error = class_register(&rfkill_class);
if (error) if (error)
goto out; goto error_class;
error = misc_register(&rfkill_miscdev); error = misc_register(&rfkill_miscdev);
if (error) { if (error)
class_unregister(&rfkill_class); goto error_misc;
goto out;
} error = rfkill_any_led_trigger_register();
if (error)
goto error_led_trigger;
#ifdef CONFIG_RFKILL_INPUT #ifdef CONFIG_RFKILL_INPUT
error = rfkill_handler_init(); error = rfkill_handler_init();
if (error) { if (error)
misc_deregister(&rfkill_miscdev); goto error_input;
class_unregister(&rfkill_class);
goto out;
}
#endif #endif
out: return 0;
#ifdef CONFIG_RFKILL_INPUT
error_input:
rfkill_any_led_trigger_unregister();
#endif
error_led_trigger:
misc_deregister(&rfkill_miscdev);
error_misc:
class_unregister(&rfkill_class);
error_class:
return error; return error;
} }
subsys_initcall(rfkill_init); subsys_initcall(rfkill_init);
@ -1293,6 +1362,7 @@ static void __exit rfkill_exit(void)
#ifdef CONFIG_RFKILL_INPUT #ifdef CONFIG_RFKILL_INPUT
rfkill_handler_exit(); rfkill_handler_exit();
#endif #endif
rfkill_any_led_trigger_unregister();
misc_deregister(&rfkill_miscdev); misc_deregister(&rfkill_miscdev);
class_unregister(&rfkill_class); class_unregister(&rfkill_class);
} }

View File

@ -11,6 +11,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
cfg80211-$(CONFIG_OF) += of.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o

View File

@ -1142,6 +1142,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
dev->priv_flags |= IFF_DONT_BRIDGE; dev->priv_flags |= IFF_DONT_BRIDGE;
INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
@ -1230,6 +1232,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
kzfree(wdev->wext.keys); kzfree(wdev->wext.keys);
#endif #endif
flush_work(&wdev->disconnect_wk);
} }
/* /*
* synchronise (so that we won't find this netdev * synchronise (so that we won't find this netdev

View File

@ -228,6 +228,7 @@ struct cfg80211_event {
size_t resp_ie_len; size_t resp_ie_len;
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
int status; /* -1 = failed; 0..65535 = status code */ int status; /* -1 = failed; 0..65535 = status code */
enum nl80211_timeout_reason timeout_reason;
} cr; } cr;
struct { struct {
const u8 *req_ie; const u8 *req_ie;
@ -388,7 +389,8 @@ 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,
int status, bool wextev, int status, bool wextev,
struct cfg80211_bss *bss); struct cfg80211_bss *bss,
enum nl80211_timeout_reason timeout_reason);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap); size_t ie_len, u16 reason, bool from_ap);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev, int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
@ -400,6 +402,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
const u8 *resp_ie, size_t resp_ie_len); const u8 *resp_ie, size_t resp_ie_len);
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev); struct wireless_dev *wdev);
void cfg80211_autodisconnect_wk(struct work_struct *work);
/* SME implementation */ /* SME implementation */
void cfg80211_conn_work(struct work_struct *work); void cfg80211_conn_work(struct work_struct *work);
@ -430,6 +433,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
void cfg80211_process_wdev_events(struct wireless_dev *wdev); void cfg80211_process_wdev_events(struct wireless_dev *wdev);
bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz, u32 bw_khz);
/** /**
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
* @wiphy: the wiphy to validate against * @wiphy: the wiphy to validate against

View File

@ -48,7 +48,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
/* update current_bss etc., consumes the bss reference */ /* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code, status_code,
status_code == WLAN_STATUS_SUCCESS, bss); status_code == WLAN_STATUS_SUCCESS, bss,
NL80211_TIMEOUT_UNSPECIFIED);
} }
EXPORT_SYMBOL(cfg80211_rx_assoc_resp); EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
@ -345,6 +346,11 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
return 0; return 0;
if (ether_addr_equal(wdev->disconnect_bssid, bssid) ||
(wdev->current_bss &&
ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
wdev->conn_owner_nlportid = 0;
return rdev_deauth(rdev, dev, &req); return rdev_deauth(rdev, dev, &req);
} }
@ -657,8 +663,25 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
return err; return err;
} }
if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) {
return -EINVAL; /* Allow random TA to be used with Public Action frames if the
* driver has indicated support for this. Otherwise, only allow
* the local address to be used.
*/
if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
return -EINVAL;
if (!wdev->current_bss &&
!wiphy_ext_feature_isset(
&rdev->wiphy,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
return -EINVAL;
if (wdev->current_bss &&
!wiphy_ext_feature_isset(
&rdev->wiphy,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
return -EINVAL;
}
/* Transmit the Action frame as requested by user space */ /* Transmit the Action frame as requested by user space */
return rdev_mgmt_tx(rdev, wdev, params, cookie); return rdev_mgmt_tx(rdev, wdev, params, cookie);

View File

@ -405,6 +405,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN }, [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN },
[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, }, [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
[NL80211_ATTR_BSSID] = { .len = ETH_ALEN }, [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
[NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
@ -6775,13 +6780,10 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
/* /*
* If scan plans are not specified, * If scan plans are not specified,
* %NL80211_ATTR_SCHED_SCAN_INTERVAL must be specified. In this * %NL80211_ATTR_SCHED_SCAN_INTERVAL will be specified. In this
* case one scan plan will be set with the specified scan * case one scan plan will be set with the specified scan
* interval and infinite number of iterations. * interval and infinite number of iterations.
*/ */
if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return -EINVAL;
interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (!interval) if (!interval)
return -EINVAL; return -EINVAL;
@ -6953,6 +6955,12 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
if (!n_plans || n_plans > wiphy->max_sched_scan_plans) if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (!wiphy_ext_feature_isset(
wiphy, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) &&
(attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] ||
attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]))
return ERR_PTR(-EINVAL);
request = kzalloc(sizeof(*request) request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids + sizeof(*request->ssids) * n_ssids
+ sizeof(*request->match_sets) * n_match_sets + sizeof(*request->match_sets) * n_match_sets
@ -7159,6 +7167,26 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
request->delay = request->delay =
nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]); nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) {
request->relative_rssi = nla_get_s8(
attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]);
request->relative_rssi_set = true;
}
if (request->relative_rssi_set &&
attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) {
struct nl80211_bss_select_rssi_adjust *rssi_adjust;
rssi_adjust = nla_data(
attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]);
request->rssi_adjust.band = rssi_adjust->band;
request->rssi_adjust.delta = rssi_adjust->delta;
if (!is_band_valid(wiphy, request->rssi_adjust.band)) {
err = -EINVAL;
goto out_free;
}
}
err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs); err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs);
if (err) if (err)
goto out_free; goto out_free;
@ -8053,8 +8081,17 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err) { if (!err) {
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
ssid, ssid_len, &req); ssid, ssid_len, &req);
if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
dev->ieee80211_ptr->conn_owner_nlportid =
info->snd_portid;
memcpy(dev->ieee80211_ptr->disconnect_bssid,
bssid, ETH_ALEN);
}
wdev_unlock(dev->ieee80211_ptr); wdev_unlock(dev->ieee80211_ptr);
} }
@ -8773,11 +8810,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
} }
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = cfg80211_connect(rdev, dev, &connect, connkeys, err = cfg80211_connect(rdev, dev, &connect, connkeys,
connect.prev_bssid); connect.prev_bssid);
wdev_unlock(dev->ieee80211_ptr);
if (err) if (err)
kzfree(connkeys); kzfree(connkeys);
if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
if (connect.bssid)
memcpy(dev->ieee80211_ptr->disconnect_bssid,
connect.bssid, ETH_ALEN);
else
memset(dev->ieee80211_ptr->disconnect_bssid,
0, ETH_ALEN);
}
wdev_unlock(dev->ieee80211_ptr);
return err; return err;
} }
@ -9673,6 +9723,20 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay)) if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
return -ENOBUFS; return -ENOBUFS;
if (req->relative_rssi_set) {
struct nl80211_bss_select_rssi_adjust rssi_adjust;
if (nla_put_s8(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
req->relative_rssi))
return -ENOBUFS;
rssi_adjust.band = req->rssi_adjust.band;
rssi_adjust.delta = req->rssi_adjust.delta;
if (nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
sizeof(rssi_adjust), &rssi_adjust))
return -ENOBUFS;
}
freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
if (!freqs) if (!freqs)
return -ENOBUFS; return -ENOBUFS;
@ -11807,9 +11871,6 @@ static int nl80211_set_multicast_to_unicast(struct sk_buff *skb,
const struct nlattr *nla; const struct nlattr *nla;
bool enabled; bool enabled;
if (netif_running(dev))
return -EBUSY;
if (!rdev->ops->set_multicast_to_unicast) if (!rdev->ops->set_multicast_to_unicast)
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -12810,7 +12871,7 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
return -ENOBUFS; return -ENOBUFS;
} }
static int nl80211_send_scan_msg(struct sk_buff *msg, static int nl80211_prep_scan_msg(struct sk_buff *msg,
struct cfg80211_registered_device *rdev, struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, struct wireless_dev *wdev,
u32 portid, u32 seq, int flags, u32 portid, u32 seq, int flags,
@ -12841,7 +12902,7 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
} }
static int static int
nl80211_send_sched_scan_msg(struct sk_buff *msg, nl80211_prep_sched_scan_msg(struct sk_buff *msg,
struct cfg80211_registered_device *rdev, struct cfg80211_registered_device *rdev,
struct net_device *netdev, struct net_device *netdev,
u32 portid, u32 seq, int flags, u32 cmd) u32 portid, u32 seq, int flags, u32 cmd)
@ -12873,7 +12934,7 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
if (!msg) if (!msg)
return; return;
if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
NL80211_CMD_TRIGGER_SCAN) < 0) { NL80211_CMD_TRIGGER_SCAN) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return; return;
@ -12892,7 +12953,7 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
if (!msg) if (!msg)
return NULL; return NULL;
if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, if (nl80211_prep_scan_msg(msg, rdev, wdev, 0, 0, 0,
aborted ? NL80211_CMD_SCAN_ABORTED : aborted ? NL80211_CMD_SCAN_ABORTED :
NL80211_CMD_NEW_SCAN_RESULTS) < 0) { NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
@ -12902,8 +12963,9 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
return msg; return msg;
} }
void nl80211_send_scan_result(struct cfg80211_registered_device *rdev, /* send message created by nl80211_build_scan_msg() */
struct sk_buff *msg) void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
struct sk_buff *msg)
{ {
if (!msg) if (!msg)
return; return;
@ -12912,25 +12974,6 @@ void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
NL80211_MCGRP_SCAN, GFP_KERNEL); NL80211_MCGRP_SCAN, GFP_KERNEL);
} }
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
struct net_device *netdev)
{
struct sk_buff *msg;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_SCAN, GFP_KERNEL);
}
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 cmd) struct net_device *netdev, u32 cmd)
{ {
@ -12940,7 +12983,7 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
if (!msg) if (!msg)
return; return;
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { if (nl80211_prep_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return; return;
} }
@ -13042,7 +13085,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + len, gfp);
if (!msg) if (!msg)
return; return;
@ -13189,12 +13232,14 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid, struct net_device *netdev, 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,
int status, gfp_t gfp) int status,
enum nl80211_timeout_reason timeout_reason,
gfp_t gfp)
{ {
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
if (!msg) if (!msg)
return; return;
@ -13210,7 +13255,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
status) || status) ||
(status < 0 && nla_put_flag(msg, NL80211_ATTR_TIMED_OUT)) || (status < 0 &&
(nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) ||
(req_ie && (req_ie &&
nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
(resp_ie && (resp_ie &&
@ -13236,7 +13283,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
if (!msg) if (!msg)
return; return;
@ -13273,7 +13320,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); msg = nlmsg_new(100 + ie_len, GFP_KERNEL);
if (!msg) if (!msg)
return; return;
@ -13349,7 +13396,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
trace_cfg80211_notify_new_peer_candidate(dev, addr); trace_cfg80211_notify_new_peer_candidate(dev, addr);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + ie_len, gfp);
if (!msg) if (!msg)
return; return;
@ -13720,7 +13767,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct sk_buff *msg; struct sk_buff *msg;
void *hdr; void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + len, gfp);
if (!msg) if (!msg)
return -ENOMEM; return -ENOMEM;
@ -13764,7 +13811,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
trace_cfg80211_mgmt_tx_status(wdev, cookie, ack); trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(100 + len, gfp);
if (!msg) if (!msg)
return; return;
@ -14519,6 +14566,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
if (wdev->owner_nlportid == notify->portid) if (wdev->owner_nlportid == notify->portid)
schedule_destroy_work = true; schedule_destroy_work = true;
else if (wdev->conn_owner_nlportid == notify->portid)
schedule_work(&wdev->disconnect_wk);
} }
spin_lock_bh(&rdev->beacon_registrations_lock); spin_lock_bh(&rdev->beacon_registrations_lock);
@ -14573,7 +14622,7 @@ void cfg80211_ft_event(struct net_device *netdev,
if (!ft_event->target_ap) if (!ft_event->target_ap)
return; return;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
if (!msg) if (!msg)
return; return;

View File

@ -14,12 +14,10 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev); struct wireless_dev *wdev);
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, bool aborted); struct wireless_dev *wdev, bool aborted);
void nl80211_send_scan_result(struct cfg80211_registered_device *rdev, void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
struct sk_buff *msg); struct sk_buff *msg);
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 cmd); struct net_device *netdev, u32 cmd);
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
struct regulatory_request *request); struct regulatory_request *request);
@ -58,7 +56,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid, struct net_device *netdev, 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,
int status, gfp_t gfp); int status,
enum nl80211_timeout_reason timeout_reason,
gfp_t gfp);
void nl80211_send_roamed(struct cfg80211_registered_device *rdev, void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid, struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len, const u8 *req_ie, size_t req_ie_len,

138
net/wireless/of.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/of.h>
#include <net/cfg80211.h>
#include "core.h"
static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
struct ieee80211_freq_range *freq_limits,
unsigned int n_freq_limits,
struct ieee80211_channel *chan)
{
u32 bw = MHZ_TO_KHZ(20);
int i;
for (i = 0; i < n_freq_limits; i++) {
struct ieee80211_freq_range *limit = &freq_limits[i];
if (cfg80211_does_bw_fit_range(limit,
MHZ_TO_KHZ(chan->center_freq),
bw))
return true;
}
return false;
}
static void wiphy_freq_limits_apply(struct wiphy *wiphy,
struct ieee80211_freq_range *freq_limits,
unsigned int n_freq_limits)
{
enum nl80211_band band;
int i;
if (WARN_ON(!n_freq_limits))
return;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *chan = &sband->channels[i];
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
n_freq_limits,
chan)) {
pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
chan->center_freq);
chan->flags |= IEEE80211_CHAN_DISABLED;
}
}
}
}
void wiphy_read_of_freq_limits(struct wiphy *wiphy)
{
struct device *dev = wiphy_dev(wiphy);
struct device_node *np;
struct property *prop;
struct ieee80211_freq_range *freq_limits;
unsigned int n_freq_limits;
const __be32 *p;
int len, i;
int err = 0;
if (!dev)
return;
np = dev_of_node(dev);
if (!np)
return;
prop = of_find_property(np, "ieee80211-freq-limit", &len);
if (!prop)
return;
if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
dev_err(dev, "ieee80211-freq-limit wrong format");
return;
}
n_freq_limits = len / sizeof(u32) / 2;
freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL);
if (!freq_limits) {
err = -ENOMEM;
goto out_kfree;
}
p = NULL;
for (i = 0; i < n_freq_limits; i++) {
struct ieee80211_freq_range *limit = &freq_limits[i];
p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
if (!p) {
err = -EINVAL;
goto out_kfree;
}
p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
if (!p) {
err = -EINVAL;
goto out_kfree;
}
if (!limit->start_freq_khz ||
!limit->end_freq_khz ||
limit->start_freq_khz >= limit->end_freq_khz) {
err = -EINVAL;
goto out_kfree;
}
}
wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
out_kfree:
kfree(freq_limits);
if (err)
dev_err(dev, "Failed to get limits: %d\n", err);
}
EXPORT_SYMBOL(wiphy_read_of_freq_limits);

View File

@ -748,21 +748,6 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
return true; return true;
} }
static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz, u32 bw_khz)
{
u32 start_freq_khz, end_freq_khz;
start_freq_khz = center_freq_khz - (bw_khz/2);
end_freq_khz = center_freq_khz + (bw_khz/2);
if (start_freq_khz >= freq_range->start_freq_khz &&
end_freq_khz <= freq_range->end_freq_khz)
return true;
return false;
}
/** /**
* freq_in_rule_band - tells us if a frequency is in a frequency band * freq_in_rule_band - tells us if a frequency is in a frequency band
* @freq_range: frequency rule we want to query * @freq_range: frequency rule we want to query
@ -1070,7 +1055,7 @@ freq_reg_info_regd(u32 center_freq,
if (!band_rule_found) if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq); band_rule_found = freq_in_rule_band(fr, center_freq);
bw_fits = reg_does_bw_fit(fr, center_freq, bw); bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw);
if (band_rule_found && bw_fits) if (band_rule_found && bw_fits)
return rr; return rr;
@ -1138,11 +1123,13 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */ /* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq), if (!cfg80211_does_bw_fit_range(freq_range,
MHZ_TO_KHZ(10))) MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ; bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq), if (!cfg80211_does_bw_fit_range(freq_range,
MHZ_TO_KHZ(20))) MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ; bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10)) if (max_bandwidth_khz < MHZ_TO_KHZ(10))

View File

@ -227,7 +227,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
ASSERT_RTNL(); ASSERT_RTNL();
if (rdev->scan_msg) { if (rdev->scan_msg) {
nl80211_send_scan_result(rdev, rdev->scan_msg); nl80211_send_scan_msg(rdev, rdev->scan_msg);
rdev->scan_msg = NULL; rdev->scan_msg = NULL;
return; return;
} }
@ -273,7 +273,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
if (!send_message) if (!send_message)
rdev->scan_msg = msg; rdev->scan_msg = msg;
else else
nl80211_send_scan_result(rdev, msg); nl80211_send_scan_msg(rdev, msg);
} }
void __cfg80211_scan_done(struct work_struct *wk) void __cfg80211_scan_done(struct work_struct *wk)
@ -321,7 +321,8 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
spin_unlock_bh(&rdev->bss_lock); spin_unlock_bh(&rdev->bss_lock);
request->scan_start = jiffies; request->scan_start = jiffies;
} }
nl80211_send_sched_scan_results(rdev, request->dev); nl80211_send_sched_scan(rdev, request->dev,
NL80211_CMD_SCHED_SCAN_RESULTS);
} }
rtnl_unlock(); rtnl_unlock();
@ -1147,7 +1148,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
else else
rcu_assign_pointer(tmp.pub.beacon_ies, ies); rcu_assign_pointer(tmp.pub.beacon_ies, ies);
rcu_assign_pointer(tmp.pub.ies, ies); rcu_assign_pointer(tmp.pub.ies, ies);
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel; tmp.pub.channel = channel;
tmp.pub.scan_width = data->scan_width; tmp.pub.scan_width = data->scan_width;

View File

@ -34,10 +34,11 @@ struct cfg80211_conn {
CFG80211_CONN_SCAN_AGAIN, CFG80211_CONN_SCAN_AGAIN,
CFG80211_CONN_AUTHENTICATE_NEXT, CFG80211_CONN_AUTHENTICATE_NEXT,
CFG80211_CONN_AUTHENTICATING, CFG80211_CONN_AUTHENTICATING,
CFG80211_CONN_AUTH_FAILED, CFG80211_CONN_AUTH_FAILED_TIMEOUT,
CFG80211_CONN_ASSOCIATE_NEXT, CFG80211_CONN_ASSOCIATE_NEXT,
CFG80211_CONN_ASSOCIATING, CFG80211_CONN_ASSOCIATING,
CFG80211_CONN_ASSOC_FAILED, CFG80211_CONN_ASSOC_FAILED,
CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
CFG80211_CONN_DEAUTH, CFG80211_CONN_DEAUTH,
CFG80211_CONN_ABANDON, CFG80211_CONN_ABANDON,
CFG80211_CONN_CONNECTED, CFG80211_CONN_CONNECTED,
@ -140,7 +141,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
return err; return err;
} }
static int cfg80211_conn_do_work(struct wireless_dev *wdev) static int cfg80211_conn_do_work(struct wireless_dev *wdev,
enum nl80211_timeout_reason *treason)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_connect_params *params; struct cfg80211_connect_params *params;
@ -171,7 +173,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
NULL, 0, NULL, 0,
params->key, params->key_len, params->key, params->key_len,
params->key_idx, NULL, 0); params->key_idx, NULL, 0);
case CFG80211_CONN_AUTH_FAILED: case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
*treason = NL80211_TIMEOUT_AUTH;
return -ENOTCONN; return -ENOTCONN;
case CFG80211_CONN_ASSOCIATE_NEXT: case CFG80211_CONN_ASSOCIATE_NEXT:
if (WARN_ON(!rdev->ops->assoc)) if (WARN_ON(!rdev->ops->assoc))
@ -198,6 +201,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
WLAN_REASON_DEAUTH_LEAVING, WLAN_REASON_DEAUTH_LEAVING,
false); false);
return err; return err;
case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
*treason = NL80211_TIMEOUT_ASSOC;
/* fall through */
case CFG80211_CONN_ASSOC_FAILED: case CFG80211_CONN_ASSOC_FAILED:
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0, NULL, 0,
@ -223,6 +229,7 @@ void cfg80211_conn_work(struct work_struct *work)
container_of(work, struct cfg80211_registered_device, conn_work); container_of(work, struct cfg80211_registered_device, conn_work);
struct wireless_dev *wdev; struct wireless_dev *wdev;
u8 bssid_buf[ETH_ALEN], *bssid = NULL; u8 bssid_buf[ETH_ALEN], *bssid = NULL;
enum nl80211_timeout_reason treason;
rtnl_lock(); rtnl_lock();
@ -244,10 +251,12 @@ void cfg80211_conn_work(struct work_struct *work)
memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
bssid = bssid_buf; bssid = bssid_buf;
} }
if (cfg80211_conn_do_work(wdev)) { treason = NL80211_TIMEOUT_UNSPECIFIED;
if (cfg80211_conn_do_work(wdev, &treason)) {
__cfg80211_connect_result( __cfg80211_connect_result(
wdev->netdev, bssid, wdev->netdev, bssid,
NULL, 0, NULL, 0, -1, false, NULL); NULL, 0, NULL, 0, -1, false, NULL,
treason);
} }
wdev_unlock(wdev); wdev_unlock(wdev);
} }
@ -352,7 +361,8 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
} else if (status_code != WLAN_STATUS_SUCCESS) { } else if (status_code != WLAN_STATUS_SUCCESS) {
__cfg80211_connect_result(wdev->netdev, mgmt->bssid, __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
NULL, 0, NULL, 0, NULL, 0, NULL, 0,
status_code, false, NULL); status_code, false, NULL,
NL80211_TIMEOUT_UNSPECIFIED);
} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
schedule_work(&rdev->conn_work); schedule_work(&rdev->conn_work);
@ -400,7 +410,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
if (!wdev->conn) if (!wdev->conn)
return; return;
wdev->conn->state = CFG80211_CONN_AUTH_FAILED; wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
schedule_work(&rdev->conn_work); schedule_work(&rdev->conn_work);
} }
@ -422,7 +432,7 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
if (!wdev->conn) if (!wdev->conn)
return; return;
wdev->conn->state = CFG80211_CONN_ASSOC_FAILED; wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
schedule_work(&rdev->conn_work); schedule_work(&rdev->conn_work);
} }
@ -564,7 +574,9 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
/* we're good if we have a matching bss struct */ /* we're good if we have a matching bss struct */
if (bss) { if (bss) {
err = cfg80211_conn_do_work(wdev); enum nl80211_timeout_reason treason;
err = cfg80211_conn_do_work(wdev, &treason);
cfg80211_put_bss(wdev->wiphy, bss); cfg80211_put_bss(wdev->wiphy, bss);
} else { } else {
/* otherwise we'll need to scan for the AP first */ /* otherwise we'll need to scan for the AP first */
@ -661,7 +673,8 @@ 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,
int status, bool wextev, int status, bool wextev,
struct cfg80211_bss *bss) struct cfg80211_bss *bss,
enum nl80211_timeout_reason timeout_reason)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
const u8 *country_ie; const u8 *country_ie;
@ -680,7 +693,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
bssid, req_ie, req_ie_len, bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len, resp_ie, resp_ie_len,
status, GFP_KERNEL); status, timeout_reason, GFP_KERNEL);
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
if (wextev) { if (wextev) {
@ -727,6 +740,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
kzfree(wdev->connect_keys); kzfree(wdev->connect_keys);
wdev->connect_keys = NULL; wdev->connect_keys = NULL;
wdev->ssid_len = 0; wdev->ssid_len = 0;
wdev->conn_owner_nlportid = 0;
if (bss) { if (bss) {
cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_unhold_bss(bss_from_pub(bss));
cfg80211_put_bss(wdev->wiphy, bss); cfg80211_put_bss(wdev->wiphy, bss);
@ -770,7 +784,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
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,
size_t resp_ie_len, int status, gfp_t gfp) size_t resp_ie_len, int status, gfp_t gfp,
enum nl80211_timeout_reason timeout_reason)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@ -810,6 +825,7 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
cfg80211_hold_bss(bss_from_pub(bss)); cfg80211_hold_bss(bss_from_pub(bss));
ev->cr.bss = bss; ev->cr.bss = bss;
ev->cr.status = status; ev->cr.status = status;
ev->cr.timeout_reason = timeout_reason;
spin_lock_irqsave(&wdev->event_lock, flags); spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list); list_add_tail(&ev->list, &wdev->event_list);
@ -955,6 +971,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
wdev->current_bss = NULL; wdev->current_bss = NULL;
wdev->ssid_len = 0; wdev->ssid_len = 0;
wdev->conn_owner_nlportid = 0;
nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
@ -1098,6 +1115,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
kzfree(wdev->connect_keys); kzfree(wdev->connect_keys);
wdev->connect_keys = NULL; wdev->connect_keys = NULL;
wdev->conn_owner_nlportid = 0;
if (wdev->conn) if (wdev->conn)
err = cfg80211_sme_disconnect(wdev, reason); err = cfg80211_sme_disconnect(wdev, reason);
else if (!rdev->ops->disconnect) else if (!rdev->ops->disconnect)
@ -1107,3 +1126,32 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
return err; return err;
} }
/*
* Used to clean up after the connection / connection attempt owner socket
* disconnects
*/
void cfg80211_autodisconnect_wk(struct work_struct *work)
{
struct wireless_dev *wdev =
container_of(work, struct wireless_dev, disconnect_wk);
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
wdev_lock(wdev);
if (wdev->conn_owner_nlportid) {
/*
* Use disconnect_bssid if still connecting and ops->disconnect
* not implemented. Otherwise we can use cfg80211_disconnect.
*/
if (rdev->ops->disconnect || wdev->current_bss)
cfg80211_disconnect(rdev, wdev->netdev,
WLAN_REASON_DEAUTH_LEAVING, true);
else
cfg80211_mlme_deauth(rdev, wdev->netdev,
wdev->disconnect_bssid, NULL, 0,
WLAN_REASON_DEAUTH_LEAVING, false);
}
wdev_unlock(wdev);
}

View File

@ -39,9 +39,11 @@ SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
static ssize_t name_show(struct device *dev, static ssize_t name_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) { char *buf)
{
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
return sprintf(buf, "%s\n", dev_name(&wiphy->dev));
return sprintf(buf, "%s\n", wiphy_name(wiphy));
} }
static DEVICE_ATTR_RO(name); static DEVICE_ATTR_RO(name);

View File

@ -114,8 +114,7 @@ int ieee80211_frequency_to_channel(int freq)
} }
EXPORT_SYMBOL(ieee80211_frequency_to_channel); EXPORT_SYMBOL(ieee80211_frequency_to_channel);
struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, struct ieee80211_channel *ieee80211_get_channel(struct wiphy *wiphy, int freq)
int freq)
{ {
enum nl80211_band band; enum nl80211_band band;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
@ -135,14 +134,13 @@ struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
return NULL; return NULL;
} }
EXPORT_SYMBOL(__ieee80211_get_channel); EXPORT_SYMBOL(ieee80211_get_channel);
static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
enum nl80211_band band)
{ {
int i, want; int i, want;
switch (band) { switch (sband->band) {
case NL80211_BAND_5GHZ: case NL80211_BAND_5GHZ:
want = 3; want = 3;
for (i = 0; i < sband->n_bitrates; i++) { for (i = 0; i < sband->n_bitrates; i++) {
@ -192,6 +190,7 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e); WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e);
break; break;
case NUM_NL80211_BANDS: case NUM_NL80211_BANDS:
default:
WARN_ON(1); WARN_ON(1);
break; break;
} }
@ -203,7 +202,7 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy)
for (band = 0; band < NUM_NL80211_BANDS; band++) for (band = 0; band < NUM_NL80211_BANDS; band++)
if (wiphy->bands[band]) if (wiphy->bands[band])
set_mandatory_flags_band(wiphy->bands[band], band); set_mandatory_flags_band(wiphy->bands[band]);
} }
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher)
@ -952,7 +951,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len,
ev->cr.status, ev->cr.status,
ev->cr.status == WLAN_STATUS_SUCCESS, ev->cr.status == WLAN_STATUS_SUCCESS,
ev->cr.bss); ev->cr.bss, ev->cr.timeout_reason);
break; break;
case EVENT_ROAMED: case EVENT_ROAMED:
__cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
@ -1848,6 +1847,21 @@ void cfg80211_free_nan_func(struct cfg80211_nan_func *f)
} }
EXPORT_SYMBOL(cfg80211_free_nan_func); EXPORT_SYMBOL(cfg80211_free_nan_func);
bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz, u32 bw_khz)
{
u32 start_freq_khz, end_freq_khz;
start_freq_khz = center_freq_khz - (bw_khz / 2);
end_freq_khz = center_freq_khz + (bw_khz / 2);
if (start_freq_khz >= freq_range->start_freq_khz &&
end_freq_khz <= freq_range->end_freq_khz)
return true;
return false;
}
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
const unsigned char rfc1042_header[] __aligned(2) = const unsigned char rfc1042_header[] __aligned(2) =

View File

@ -1119,3 +1119,70 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
return ret; return ret;
} }
#endif #endif
char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
char *ends, struct iw_event *iwe, int event_len)
{
int lcp_len = iwe_stream_lcp_len(info);
event_len = iwe_stream_event_len_adjust(info, event_len);
/* Check if it's possible */
if (likely((stream + event_len) < ends)) {
iwe->len = event_len;
/* Beware of alignement issues on 64 bits */
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + lcp_len, &iwe->u,
event_len - lcp_len);
stream += event_len;
}
return stream;
}
EXPORT_SYMBOL(iwe_stream_add_event);
char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
char *ends, struct iw_event *iwe, char *extra)
{
int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
int point_len = iwe_stream_point_len(info);
int lcp_len = iwe_stream_lcp_len(info);
/* Check if it's possible */
if (likely((stream + event_len) < ends)) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
memcpy(stream + lcp_len,
((char *) &iwe->u) + IW_EV_POINT_OFF,
IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
if (iwe->u.data.length && extra)
memcpy(stream + point_len, extra, iwe->u.data.length);
stream += event_len;
}
return stream;
}
EXPORT_SYMBOL(iwe_stream_add_point);
char *iwe_stream_add_value(struct iw_request_info *info, char *event,
char *value, char *ends, struct iw_event *iwe,
int event_len)
{
int lcp_len = iwe_stream_lcp_len(info);
/* Don't duplicate LCP */
event_len -= IW_EV_LCP_LEN;
/* Check if it's possible */
if (likely((value + event_len) < ends)) {
/* Add new value */
memcpy(value, &iwe->u, event_len);
value += event_len;
/* Patch LCP */
iwe->len = value - event;
memcpy(event, (char *) iwe, lcp_len);
}
return value;
}
EXPORT_SYMBOL(iwe_stream_add_value);

View File

@ -105,30 +105,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
goto out; goto out;
} }
wdev->wext.connect.channel = chan; wdev->wext.connect.channel = chan;
/*
* SSID is not set, we just want to switch monitor channel,
* this is really just backward compatibility, if the SSID
* is set then we use the channel to select the BSS to use
* to connect to instead. If we were connected on another
* channel we disconnected above and reconnect below.
*/
if (chan && !wdev->wext.connect.ssid_len) {
struct cfg80211_chan_def chandef = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
.center_freq1 = freq,
};
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (chandef.chan)
err = cfg80211_set_monitor_channel(rdev, &chandef);
else
err = -EINVAL;
goto out;
}
err = cfg80211_mgd_wext_connect(rdev, wdev); err = cfg80211_mgd_wext_connect(rdev, wdev);
out: out:
wdev_unlock(wdev); wdev_unlock(wdev);