Lots of updates for net-next; along with the usual flurry

of small fixes, cleanups and internal features we have:
  * VHT support for TDLS and IBSS (conditional on drivers though)
  * first TX performance improvements (the biggest will come later)
  * many suspend/resume (race) fixes
  * name_assign_type support from Tom Gundersen
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCAAGBQJVGWlNAAoJEDBSmw7B7bqr5s8P/R9+Q6y4Ixice9dDOJYynl/d
 dMEUEfCBWUyDaQD1bNQIED2mc0sM5+Ax8OVIVx9fdrLGPxaISBqDJKE1USoTNZzm
 q+U3dM4Q45SfQSsgaY4FtxTlPWPtUKsNMXY/CxLR+IqVeA3+30rX+hv1f3SAqBj0
 68IwW/uUIHb71IZ+hz2Mwudt4JVW8KRg9VlZ0UY6EEvC4m5QD2YkwQQo/hJ2WF+/
 wAJbku02L/Vy4J7E6hNcKYWXokht4fVYphjl/1ZDd/+8L8SUv9mC88n1Jzxa428p
 1PmbtwzbpOrtTcC2BCPDA04IyfMc7k9DlLKw/h2KLPbHZXheD9eVmo/Am5vz+uH6
 926f+FK339SzoJnZ5wBBDiZ8W8TLYNc8ImxtcxjnrtGfr1CKiuh23P1CWyOlKJCO
 BYFJqkCOqWOHYnN0embaj7JqM/LmQI5ZoBZHZhD2KQXIXpTsjjIMPfJvip5D+tsV
 +iXIlQwdeK6rbjxdonBgn7n57XIeSVMAYeyDCbzIShfibjHbUZPn+RsZCtv8RWv8
 EaZu8PerU5ZDKwdX940+lWrtf/TMDJBYQpAIBRuiZK4DTNWCt3BrDlvb1FXGgA+X
 vQJnr32vjJ/pLDxNLHQlkKWC4I/CYtG47OgcJN9AQXrig1zApd+C29zy3aqch3ea
 wxV9dFfheTqZFjtZfSsH
 =O/cf
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-davem-2015-03-30' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg says:

====================
Lots of updates for net-next; along with the usual flurry
of small fixes, cleanups and internal features we have:
 * VHT support for TDLS and IBSS (conditional on drivers though)
 * first TX performance improvements (the biggest will come later)
 * many suspend/resume (race) fixes
 * name_assign_type support from Tom Gundersen
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2015-03-31 16:39:04 -04:00
commit 7b6249bba9
72 changed files with 1911 additions and 1192 deletions

View file

@ -1412,7 +1412,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
info->bssid, NULL, 0, 0, 0); info->bssid, NULL, 0, IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY);
if (bss) { if (bss) {
const struct cfg80211_bss_ies *ies; const struct cfg80211_bss_ies *ies;

View file

@ -686,20 +686,21 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
{ {
struct ath6kl *ar = vif->ar; struct ath6kl *ar = vif->ar;
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
u16 cap_mask, cap_val; u16 cap_val;
enum ieee80211_bss_type bss_type;
u8 *ie; u8 *ie;
if (nw_type & ADHOC_NETWORK) { if (nw_type & ADHOC_NETWORK) {
cap_mask = WLAN_CAPABILITY_IBSS;
cap_val = WLAN_CAPABILITY_IBSS; cap_val = WLAN_CAPABILITY_IBSS;
bss_type = IEEE80211_BSS_TYPE_IBSS;
} else { } else {
cap_mask = WLAN_CAPABILITY_ESS;
cap_val = WLAN_CAPABILITY_ESS; cap_val = WLAN_CAPABILITY_ESS;
bss_type = IEEE80211_BSS_TYPE_ESS;
} }
bss = cfg80211_get_bss(ar->wiphy, chan, bssid, bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
vif->ssid, vif->ssid_len, vif->ssid, vif->ssid_len,
cap_mask, cap_val); bss_type, IEEE80211_PRIVACY_ANY);
if (bss == NULL) { if (bss == NULL) {
/* /*
* Since cfg80211 may not yet know about the BSS, * Since cfg80211 may not yet know about the BSS,
@ -1495,6 +1496,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params) struct vif_params *params)
@ -1513,7 +1515,7 @@ static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
wdev = ath6kl_interface_add(ar, name, type, if_idx, nw_type); wdev = ath6kl_interface_add(ar, name, name_assign_type, type, if_idx, nw_type);
if (!wdev) if (!wdev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -3633,13 +3635,14 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
} }
struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u8 fw_vif_idx, u8 nw_type) u8 fw_vif_idx, u8 nw_type)
{ {
struct net_device *ndev; struct net_device *ndev;
struct ath6kl_vif *vif; struct ath6kl_vif *vif;
ndev = alloc_netdev(sizeof(*vif), name, NET_NAME_UNKNOWN, ether_setup); ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, ether_setup);
if (!ndev) if (!ndev)
return NULL; return NULL;

View file

@ -25,6 +25,7 @@ enum ath6kl_cfg_suspend_mode {
}; };
struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u8 fw_vif_idx, u8 nw_type); u8 fw_vif_idx, u8 nw_type);
void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq,

View file

@ -211,8 +211,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
rtnl_lock(); rtnl_lock();
/* Add an initial station interface */ /* Add an initial station interface */
wdev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, wdev = ath6kl_interface_add(ar, "wlan%d", NET_NAME_ENUM,
INFRA_NETWORK); NL80211_IFTYPE_STATION, 0, INFRA_NETWORK);
rtnl_unlock(); rtnl_unlock();

View file

@ -409,7 +409,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len, sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
if (!bss) { if (!bss) {
wil_err(wil, "Unable to find BSS\n"); wil_err(wil, "Unable to find BSS\n");
return -ENOENT; return -ENOENT;

View file

@ -625,6 +625,7 @@ static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params) struct vif_params *params)
@ -648,7 +649,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_P2P_DEVICE:
wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
if (!IS_ERR(wdev)) if (!IS_ERR(wdev))
brcmf_cfg80211_update_proto_addr_mode(wdev); brcmf_cfg80211_update_proto_addr_mode(wdev);
return wdev; return wdev;

View file

@ -2246,11 +2246,13 @@ static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p,
* *
* @wiphy: wiphy device of new interface. * @wiphy: wiphy device of new interface.
* @name: name of the new interface. * @name: name of the new interface.
* @name_assign_type: origin of the interface name
* @type: nl80211 interface type. * @type: nl80211 interface type.
* @flags: not used. * @flags: not used.
* @params: contains mac address for P2P device. * @params: contains mac address for P2P device.
*/ */
struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
struct vif_params *params) struct vif_params *params)
{ {
@ -2310,6 +2312,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
} }
strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
ifp->ndev->name_assign_type = name_assign_type;
err = brcmf_net_attach(ifp, true); err = brcmf_net_attach(ifp, true);
if (err) { if (err) {
brcmf_err("Registering netdevice failed\n"); brcmf_err("Registering netdevice failed\n");

View file

@ -149,6 +149,7 @@ struct brcmf_p2p_info {
s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg); s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg);
void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p);
struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
struct vif_params *params); struct vif_params *params);
int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev);

View file

@ -1240,8 +1240,8 @@ static void cw1200_do_join(struct cw1200_common *priv)
bssid = priv->vif->bss_conf.bssid; bssid = priv->vif->bss_conf.bssid;
bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
bssid, NULL, 0, 0, 0); IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
if (!bss && !conf->ibss_joined) { if (!bss && !conf->ibss_joined) {
wsm_unlock_tx(priv); wsm_unlock_tx(priv);

View file

@ -66,7 +66,7 @@ config IPW2100_DEBUG
config IPW2200 config IPW2200
tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
depends on PCI && CFG80211 depends on PCI && CFG80211
select CFG80211_WEXT select CFG80211_WEXT_EXPORT
select WIRELESS_EXT select WIRELESS_EXT
select WEXT_SPY select WEXT_SPY
select WEXT_PRIV select WEXT_PRIV

View file

@ -1129,20 +1129,23 @@ done:
IWL_DEBUG_MAC80211(priv, "leave\n"); IWL_DEBUG_MAC80211(priv, "leave\n");
} }
static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, static void iwlagn_mac_event_callback(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event) const struct ieee80211_event *event)
{ {
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
if (event->type != RSSI_EVENT)
return;
IWL_DEBUG_MAC80211(priv, "enter\n"); IWL_DEBUG_MAC80211(priv, "enter\n");
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
if (priv->lib->bt_params && if (priv->lib->bt_params &&
priv->lib->bt_params->advanced_bt_coexist) { priv->lib->bt_params->advanced_bt_coexist) {
if (rssi_event == RSSI_EVENT_LOW) if (event->u.rssi.data == RSSI_EVENT_LOW)
priv->bt_enable_pspoll = true; priv->bt_enable_pspoll = true;
else if (rssi_event == RSSI_EVENT_HIGH) else if (event->u.rssi.data == RSSI_EVENT_HIGH)
priv->bt_enable_pspoll = false; priv->bt_enable_pspoll = false;
iwlagn_send_advance_bt_config(priv); iwlagn_send_advance_bt_config(priv);
@ -1613,7 +1616,7 @@ const struct ieee80211_ops iwlagn_hw_ops = {
.channel_switch = iwlagn_mac_channel_switch, .channel_switch = iwlagn_mac_channel_switch,
.flush = iwlagn_mac_flush, .flush = iwlagn_mac_flush,
.tx_last_beacon = iwlagn_mac_tx_last_beacon, .tx_last_beacon = iwlagn_mac_tx_last_beacon,
.rssi_callback = iwlagn_mac_rssi_callback, .event_callback = iwlagn_mac_event_callback,
.set_tim = iwlagn_mac_set_tim, .set_tim = iwlagn_mac_set_tim,
}; };

View file

@ -1024,7 +1024,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
} }
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event) enum ieee80211_rssi_event_data rssi_event)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_bt_iterator_data data = { struct iwl_bt_iterator_data data = {

View file

@ -1069,7 +1069,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
} }
void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event) enum ieee80211_rssi_event_data rssi_event)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_bt_iterator_data data = { struct iwl_bt_iterator_data data = {

View file

@ -1251,7 +1251,7 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event); enum ieee80211_rssi_event_data);
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm);
u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
@ -1271,7 +1271,7 @@ int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event); enum ieee80211_rssi_event_data);
u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,

View file

@ -1356,8 +1356,8 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
/* Find the BSS we want using available scan results */ /* Find the BSS we want using available scan results */
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len, sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); IEEE80211_PRIVACY_ANY);
if (!bss) { if (!bss) {
wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", wiphy_err(wiphy, "assoc: bss %pM not in scan results\n",
sme->bssid); sme->bssid);
@ -2000,7 +2000,7 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
* bss list is populated already */ * bss list is populated already */
bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid,
params->ssid, params->ssid_len, params->ssid, params->ssid_len,
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
if (bss) { if (bss) {
ret = lbs_ibss_join_existing(priv, params, bss); ret = lbs_ibss_join_existing(priv, params, bss);

View file

@ -330,6 +330,83 @@ static const struct ieee80211_rate hwsim_rates[] = {
{ .bitrate = 540 } { .bitrate = 540 }
}; };
#define OUI_QCA 0x001374
#define QCA_NL80211_SUBCMD_TEST 1
enum qca_nl80211_vendor_subcmds {
QCA_WLAN_VENDOR_ATTR_TEST = 8,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_TEST
};
static const struct nla_policy
hwsim_vendor_test_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_MAX] = { .type = NLA_U32 },
};
static int mac80211_hwsim_vendor_cmd_test(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
int err;
u32 val;
err = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
hwsim_vendor_test_policy);
if (err)
return err;
if (!tb[QCA_WLAN_VENDOR_ATTR_TEST])
return -EINVAL;
val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TEST]);
wiphy_debug(wiphy, "%s: test=%u\n", __func__, val);
/* Send a vendor event as a test. Note that this would not normally be
* done within a command handler, but rather, based on some other
* trigger. For simplicity, this command is used to trigger the event
* here.
*
* event_idx = 0 (index in mac80211_hwsim_vendor_commands)
*/
skb = cfg80211_vendor_event_alloc(wiphy, wdev, 100, 0, GFP_KERNEL);
if (skb) {
/* skb_put() or nla_put() will fill up data within
* NL80211_ATTR_VENDOR_DATA.
*/
/* Add vendor data */
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 1);
/* Send the event - this will call nla_nest_end() */
cfg80211_vendor_event(skb, GFP_KERNEL);
}
/* Send a response to the command */
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 10);
if (!skb)
return -ENOMEM;
/* skb_put() or nla_put() will fill up data within
* NL80211_ATTR_VENDOR_DATA
*/
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 2);
return cfg80211_vendor_cmd_reply(skb);
}
static struct wiphy_vendor_command mac80211_hwsim_vendor_commands[] = {
{
.info = { .vendor_id = OUI_QCA,
.subcmd = QCA_NL80211_SUBCMD_TEST },
.flags = WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = mac80211_hwsim_vendor_cmd_test,
}
};
/* Advertise support vendor specific events */
static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
{ .vendor_id = OUI_QCA, .subcmd = 1 },
};
static const struct ieee80211_iface_limit hwsim_if_limits[] = { static const struct ieee80211_iface_limit hwsim_if_limits[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
{ .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) |
@ -906,8 +983,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
goto nla_put_failure; goto nla_put_failure;
} }
if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, ETH_ALEN, hdr->addr2))
ETH_ALEN, data->addresses[1].addr))
goto nla_put_failure; goto nla_put_failure;
/* We get the skb->data */ /* We get the skb->data */
@ -1522,21 +1598,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
vp->aid = info->aid; vp->aid = info->aid;
} }
if (changed & BSS_CHANGED_BEACON_INT) {
wiphy_debug(hw->wiphy, " BCNINT: %d\n", info->beacon_int);
data->beacon_int = info->beacon_int * 1024;
}
if (changed & BSS_CHANGED_BEACON_ENABLED) { if (changed & BSS_CHANGED_BEACON_ENABLED) {
wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon); wiphy_debug(hw->wiphy, " BCN EN: %d (BI=%u)\n",
info->enable_beacon, info->beacon_int);
vp->bcn_en = info->enable_beacon; vp->bcn_en = info->enable_beacon;
if (data->started && if (data->started &&
!hrtimer_is_queued(&data->beacon_timer.timer) && !hrtimer_is_queued(&data->beacon_timer.timer) &&
info->enable_beacon) { info->enable_beacon) {
u64 tsf, until_tbtt; u64 tsf, until_tbtt;
u32 bcn_int; u32 bcn_int;
if (WARN_ON(!data->beacon_int)) data->beacon_int = info->beacon_int * 1024;
data->beacon_int = 1000 * 1024;
tsf = mac80211_hwsim_get_tsf(hw, vif); tsf = mac80211_hwsim_get_tsf(hw, vif);
bcn_int = data->beacon_int; bcn_int = data->beacon_int;
until_tbtt = bcn_int - do_div(tsf, bcn_int); until_tbtt = bcn_int - do_div(tsf, bcn_int);
@ -1550,8 +1621,10 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
mac80211_hwsim_bcn_en_iter, &count); mac80211_hwsim_bcn_en_iter, &count);
wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u", wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u",
count); count);
if (count == 0) if (count == 0) {
tasklet_hrtimer_cancel(&data->beacon_timer); tasklet_hrtimer_cancel(&data->beacon_timer);
data->beacon_int = 0;
}
} }
} }
@ -2420,6 +2493,12 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->max_rates = 4; hw->max_rates = 4;
hw->max_rate_tries = 11; hw->max_rate_tries = 11;
hw->wiphy->vendor_commands = mac80211_hwsim_vendor_commands;
hw->wiphy->n_vendor_commands =
ARRAY_SIZE(mac80211_hwsim_vendor_commands);
hw->wiphy->vendor_events = mac80211_hwsim_vendor_events;
hw->wiphy->n_vendor_events = ARRAY_SIZE(mac80211_hwsim_vendor_events);
if (param->reg_strict) if (param->reg_strict)
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
if (param->regd) { if (param->regd) {
@ -2611,7 +2690,7 @@ static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr)
spin_lock_bh(&hwsim_radio_lock); spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry(data, &hwsim_radios, list) { list_for_each_entry(data, &hwsim_radios, list) {
if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) { if (mac80211_hwsim_addr_match(data, addr)) {
_found = true; _found = true;
break; break;
} }

View file

@ -1954,13 +1954,13 @@ done:
if (mode == NL80211_IFTYPE_ADHOC) if (mode == NL80211_IFTYPE_ADHOC)
bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
bssid, ssid, ssid_len, bssid, ssid, ssid_len,
WLAN_CAPABILITY_IBSS, IEEE80211_BSS_TYPE_IBSS,
WLAN_CAPABILITY_IBSS); IEEE80211_PRIVACY_ANY);
else else
bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bss = cfg80211_get_bss(priv->wdev.wiphy, channel,
bssid, ssid, ssid_len, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, IEEE80211_BSS_TYPE_ESS,
WLAN_CAPABILITY_ESS); IEEE80211_PRIVACY_ANY);
if (!bss) { if (!bss) {
if (is_scanning_required) { if (is_scanning_required) {
@ -2398,10 +2398,11 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
} }
/* /*
* create a new virtual interface with the given name * create a new virtual interface with the given name and name assign type
*/ */
struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params) struct vif_params *params)
@ -2521,7 +2522,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
} }
dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name,
NET_NAME_UNKNOWN, ether_setup, name_assign_type, ether_setup,
IEEE80211_NUM_ACS, 1); IEEE80211_NUM_ACS, 1);
if (!dev) { if (!dev) {
wiphy_err(wiphy, "no memory available for netdevice\n"); wiphy_err(wiphy, "no memory available for netdevice\n");

View file

@ -468,7 +468,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
rtnl_lock(); rtnl_lock();
/* Create station interface by default */ /* Create station interface by default */
wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
NL80211_IFTYPE_STATION, NULL, NULL); NL80211_IFTYPE_STATION, NULL, NULL);
if (IS_ERR(wdev)) { if (IS_ERR(wdev)) {
dev_err(adapter->dev, "cannot create default STA interface\n"); dev_err(adapter->dev, "cannot create default STA interface\n");
@ -477,7 +477,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
} }
if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
NL80211_IFTYPE_AP, NULL, NULL); NL80211_IFTYPE_AP, NULL, NULL);
if (IS_ERR(wdev)) { if (IS_ERR(wdev)) {
dev_err(adapter->dev, "cannot create AP interface\n"); dev_err(adapter->dev, "cannot create AP interface\n");
@ -487,7 +487,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
} }
if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
NL80211_IFTYPE_P2P_CLIENT, NULL, NL80211_IFTYPE_P2P_CLIENT, NULL,
NULL); NULL);
if (IS_ERR(wdev)) { if (IS_ERR(wdev)) {

View file

@ -1322,6 +1322,7 @@ u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type);
struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params); struct vif_params *params);

View file

@ -2,7 +2,7 @@ config HERMES
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
depends on (PPC_PMAC || PCI || PCMCIA) depends on (PPC_PMAC || PCI || PCMCIA)
depends on CFG80211 depends on CFG80211
select CFG80211_WEXT select CFG80211_WEXT_EXPORT
select WIRELESS_EXT select WIRELESS_EXT
select WEXT_SPY select WEXT_SPY
select WEXT_PRIV select WEXT_PRIV

View file

@ -77,7 +77,7 @@ static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
wl1271_debug(DEBUG_EVENT, wl1271_debug(DEBUG_EVENT,
"SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)", "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)",
freq, sync_channel, sync_band); freq, sync_channel, sync_band);
skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20, skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL, 20,
WLCORE_VENDOR_EVENT_SC_SYNC, WLCORE_VENDOR_EVENT_SC_SYNC,
GFP_KERNEL); GFP_KERNEL);
@ -98,7 +98,7 @@ static int wlcore_smart_config_decode_event(struct wl1271 *wl,
wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID"); wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID");
wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len); wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len);
skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, NULL,
ssid_len + pwd_len + 20, ssid_len + pwd_len + 20,
WLCORE_VENDOR_EVENT_SC_DECODE, WLCORE_VENDOR_EVENT_SC_DECODE,
GFP_KERNEL); GFP_KERNEL);

View file

@ -2580,6 +2580,7 @@ static const struct net_device_ops rtw_cfg80211_monitor_if_ops = {
}; };
static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name, static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name,
unsigned char name_assign_type,
struct net_device **ndev) struct net_device **ndev)
{ {
int ret = 0; int ret = 0;
@ -2612,6 +2613,7 @@ static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name,
mon_ndev->type = ARPHRD_IEEE80211_RADIOTAP; mon_ndev->type = ARPHRD_IEEE80211_RADIOTAP;
strncpy(mon_ndev->name, name, IFNAMSIZ); strncpy(mon_ndev->name, name, IFNAMSIZ);
mon_ndev->name[IFNAMSIZ - 1] = 0; mon_ndev->name[IFNAMSIZ - 1] = 0;
mon_ndev->name_assign_type = name_assign_type;
mon_ndev->destructor = rtw_ndev_destructor; mon_ndev->destructor = rtw_ndev_destructor;
mon_ndev->netdev_ops = &rtw_cfg80211_monitor_if_ops; mon_ndev->netdev_ops = &rtw_cfg80211_monitor_if_ops;
@ -2654,6 +2656,7 @@ out:
static struct wireless_dev * static struct wireless_dev *
cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name, cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
struct vif_params *params) struct vif_params *params)
{ {
@ -2673,7 +2676,8 @@ cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name,
break; break;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
ret = ret =
rtw_cfg80211_add_monitor_if(padapter, (char *)name, &ndev); rtw_cfg80211_add_monitor_if(padapter, (char *)name,
name_assign_type, &ndev);
break; break;
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:

View file

@ -214,6 +214,39 @@ enum ieee80211_rate_flags {
IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6, IEEE80211_RATE_SUPPORTS_10MHZ = 1<<6,
}; };
/**
* enum ieee80211_bss_type - BSS type filter
*
* @IEEE80211_BSS_TYPE_ESS: Infrastructure BSS
* @IEEE80211_BSS_TYPE_PBSS: Personal BSS
* @IEEE80211_BSS_TYPE_IBSS: Independent BSS
* @IEEE80211_BSS_TYPE_MBSS: Mesh BSS
* @IEEE80211_BSS_TYPE_ANY: Wildcard value for matching any BSS type
*/
enum ieee80211_bss_type {
IEEE80211_BSS_TYPE_ESS,
IEEE80211_BSS_TYPE_PBSS,
IEEE80211_BSS_TYPE_IBSS,
IEEE80211_BSS_TYPE_MBSS,
IEEE80211_BSS_TYPE_ANY
};
/**
* enum ieee80211_privacy - BSS privacy filter
*
* @IEEE80211_PRIVACY_ON: privacy bit set
* @IEEE80211_PRIVACY_OFF: privacy bit clear
* @IEEE80211_PRIVACY_ANY: Wildcard value for matching any privacy setting
*/
enum ieee80211_privacy {
IEEE80211_PRIVACY_ON,
IEEE80211_PRIVACY_OFF,
IEEE80211_PRIVACY_ANY
};
#define IEEE80211_PRIVACY(x) \
((x) ? IEEE80211_PRIVACY_ON : IEEE80211_PRIVACY_OFF)
/** /**
* struct ieee80211_rate - bitrate definition * struct ieee80211_rate - bitrate definition
* *
@ -2423,6 +2456,7 @@ struct cfg80211_ops {
struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy, struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params); struct vif_params *params);
@ -4010,14 +4044,16 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, const u8 *bssid,
const u8 *ssid, size_t ssid_len, const u8 *ssid, size_t ssid_len,
u16 capa_mask, u16 capa_val); enum ieee80211_bss_type bss_type,
enum ieee80211_privacy);
static inline struct cfg80211_bss * static inline struct cfg80211_bss *
cfg80211_get_ibss(struct wiphy *wiphy, cfg80211_get_ibss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *ssid, size_t ssid_len) const u8 *ssid, size_t ssid_len)
{ {
return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len, return cfg80211_get_bss(wiphy, channel, NULL, ssid, ssid_len,
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); IEEE80211_BSS_TYPE_IBSS,
IEEE80211_PRIVACY_ANY);
} }
/** /**
@ -4258,6 +4294,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
int approxlen); int approxlen);
struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_commands cmd, enum nl80211_commands cmd,
enum nl80211_attrs attr, enum nl80211_attrs attr,
int vendor_event_idx, int vendor_event_idx,
@ -4312,6 +4349,7 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb);
/** /**
* cfg80211_vendor_event_alloc - allocate vendor-specific event skb * cfg80211_vendor_event_alloc - allocate vendor-specific event skb
* @wiphy: the wiphy * @wiphy: the wiphy
* @wdev: the wireless device
* @event_idx: index of the vendor event in the wiphy's vendor_events * @event_idx: index of the vendor event in the wiphy's vendor_events
* @approxlen: an upper bound of the length of the data that will * @approxlen: an upper bound of the length of the data that will
* be put into the skb * be put into the skb
@ -4320,16 +4358,20 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb);
* This function allocates and pre-fills an skb for an event on the * This function allocates and pre-fills an skb for an event on the
* vendor-specific multicast group. * vendor-specific multicast group.
* *
* If wdev != NULL, both the ifindex and identifier of the specified
* wireless device are added to the event message before the vendor data
* attribute.
*
* When done filling the skb, call cfg80211_vendor_event() with the * When done filling the skb, call cfg80211_vendor_event() with the
* skb to send the event. * skb to send the event.
* *
* Return: An allocated and pre-filled skb. %NULL if any errors happen. * Return: An allocated and pre-filled skb. %NULL if any errors happen.
*/ */
static inline struct sk_buff * static inline struct sk_buff *
cfg80211_vendor_event_alloc(struct wiphy *wiphy, int approxlen, cfg80211_vendor_event_alloc(struct wiphy *wiphy, struct wireless_dev *wdev,
int event_idx, gfp_t gfp) int approxlen, int event_idx, gfp_t gfp)
{ {
return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_VENDOR, return __cfg80211_alloc_event_skb(wiphy, wdev, NL80211_CMD_VENDOR,
NL80211_ATTR_VENDOR_DATA, NL80211_ATTR_VENDOR_DATA,
event_idx, approxlen, gfp); event_idx, approxlen, gfp);
} }
@ -4430,7 +4472,7 @@ static inline int cfg80211_testmode_reply(struct sk_buff *skb)
static inline struct sk_buff * static inline struct sk_buff *
cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp)
{ {
return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_TESTMODE, return __cfg80211_alloc_event_skb(wiphy, NULL, NL80211_CMD_TESTMODE,
NL80211_ATTR_TESTDATA, -1, NL80211_ATTR_TESTDATA, -1,
approxlen, gfp); approxlen, gfp);
} }
@ -4860,6 +4902,17 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev,
bool ieee80211_operating_class_to_band(u8 operating_class, bool ieee80211_operating_class_to_band(u8 operating_class,
enum ieee80211_band *band); enum ieee80211_band *band);
/**
* ieee80211_chandef_to_operating_class - convert chandef to operation class
*
* @chandef: the chandef to convert
* @op_class: a pointer to the resulting operating class
*
* Returns %true if the conversion was successful, %false otherwise.
*/
bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
u8 *op_class);
/* /*
* cfg80211_tdls_oper_request - request userspace to perform TDLS operation * cfg80211_tdls_oper_request - request userspace to perform TDLS operation
* @dev: the device on which the operation is requested * @dev: the device on which the operation is requested

View file

@ -519,6 +519,17 @@ iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends,
return stream; return stream;
} }
static inline char *
iwe_stream_add_event_check(struct iw_request_info *info, char *stream,
char *ends, struct iw_event *iwe, int event_len)
{
char *res = iwe_stream_add_event(info, stream, ends, iwe, event_len);
if (res == stream)
return ERR_PTR(-E2BIG);
return res;
}
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* /*
* Wrapper to add an short Wireless Event containing a pointer to a * Wrapper to add an short Wireless Event containing a pointer to a
@ -545,6 +556,17 @@ iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends,
return stream; return stream;
} }
static inline char *
iwe_stream_add_point_check(struct iw_request_info *info, char *stream,
char *ends, struct iw_event *iwe, char *extra)
{
char *res = iwe_stream_add_point(info, stream, ends, iwe, extra);
if (res == stream)
return ERR_PTR(-E2BIG);
return res;
}
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* /*
* Wrapper to add a value to a Wireless Event in a stream of events. * Wrapper to add a value to a Wireless Event in a stream of events.

View file

@ -301,16 +301,85 @@ enum ieee80211_bss_change {
#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4 #define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
/** /**
* enum ieee80211_rssi_event - RSSI threshold event * enum ieee80211_event_type - event to be notified to the low level driver
* An indicator for when RSSI goes below/above a certain threshold. * @RSSI_EVENT: AP's rssi crossed the a threshold set by the driver.
* @RSSI_EVENT_HIGH: AP's rssi crossed the high threshold set by the driver. * @MLME_EVENT: event related to MLME
* @RSSI_EVENT_LOW: AP's rssi crossed the low threshold set by the driver.
*/ */
enum ieee80211_rssi_event { enum ieee80211_event_type {
RSSI_EVENT,
MLME_EVENT,
};
/**
* enum ieee80211_rssi_event_data - relevant when event type is %RSSI_EVENT
* @RSSI_EVENT_HIGH: AP's rssi went below the threshold set by the driver.
* @RSSI_EVENT_LOW: AP's rssi went above the threshold set by the driver.
*/
enum ieee80211_rssi_event_data {
RSSI_EVENT_HIGH, RSSI_EVENT_HIGH,
RSSI_EVENT_LOW, RSSI_EVENT_LOW,
}; };
/**
* enum ieee80211_rssi_event - data attached to an %RSSI_EVENT
* @data: See &enum ieee80211_rssi_event_data
*/
struct ieee80211_rssi_event {
enum ieee80211_rssi_event_data data;
};
/**
* enum ieee80211_mlme_event_data - relevant when event type is %MLME_EVENT
* @AUTH_EVENT: the MLME operation is authentication
* @ASSOC_EVENT: the MLME operation is association
* @DEAUTH_RX_EVENT: deauth received..
* @DEAUTH_TX_EVENT: deauth sent.
*/
enum ieee80211_mlme_event_data {
AUTH_EVENT,
ASSOC_EVENT,
DEAUTH_RX_EVENT,
DEAUTH_TX_EVENT,
};
/**
* enum ieee80211_mlme_event_status - relevant when event type is %MLME_EVENT
* @MLME_SUCCESS: the MLME operation completed successfully.
* @MLME_DENIED: the MLME operation was denied by the peer.
* @MLME_TIMEOUT: the MLME operation timed out.
*/
enum ieee80211_mlme_event_status {
MLME_SUCCESS,
MLME_DENIED,
MLME_TIMEOUT,
};
/**
* enum ieee80211_mlme_event - data attached to an %MLME_EVENT
* @data: See &enum ieee80211_mlme_event_data
* @status: See &enum ieee80211_mlme_event_status
* @reason: the reason code if applicable
*/
struct ieee80211_mlme_event {
enum ieee80211_mlme_event_data data;
enum ieee80211_mlme_event_status status;
u16 reason;
};
/**
* struct ieee80211_event - event to be sent to the driver
* @type The event itself. See &enum ieee80211_event_type.
* @rssi: relevant if &type is %RSSI_EVENT
* @mlme: relevant if &type is %AUTH_EVENT
*/
struct ieee80211_event {
enum ieee80211_event_type type;
union {
struct ieee80211_rssi_event rssi;
struct ieee80211_mlme_event mlme;
} u;
};
/** /**
* struct ieee80211_bss_conf - holds the BSS's changing parameters * struct ieee80211_bss_conf - holds the BSS's changing parameters
* *
@ -337,12 +406,15 @@ enum ieee80211_rssi_event {
* HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can * HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
* only come from a beacon, but might not become valid until after * only come from a beacon, but might not become valid until after
* association when a beacon is received (which is notified with the * association when a beacon is received (which is notified with the
* %BSS_CHANGED_DTIM flag.) * %BSS_CHANGED_DTIM flag.). See also sync_dtim_count important notice.
* @sync_device_ts: the device timestamp corresponding to the sync_tsf, * @sync_device_ts: the device timestamp corresponding to the sync_tsf,
* the driver/device can use this to calculate synchronisation * the driver/device can use this to calculate synchronisation
* (see @sync_tsf) * (see @sync_tsf). See also sync_dtim_count important notice.
* @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY * @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY
* is requested, see @sync_tsf/@sync_device_ts. * is requested, see @sync_tsf/@sync_device_ts.
* IMPORTANT: These three sync_* parameters would possibly be out of sync
* by the time the driver will use them. The synchronized view is currently
* guaranteed only in certain callbacks.
* @beacon_int: beacon interval * @beacon_int: beacon interval
* @assoc_capability: capabilities taken from assoc resp * @assoc_capability: capabilities taken from assoc resp
* @basic_rates: bitmap of basic rates, each bit stands for an * @basic_rates: bitmap of basic rates, each bit stands for an
@ -1278,6 +1350,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
*/ */
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
/**
* ieee80211_vif_to_wdev - return a wdev struct from a vif
* @vif: the vif to get the wdev for
*
* This can be used by mac80211 drivers with direct cfg80211 APIs
* (like the vendor commands) that needs to get the wdev for a vif.
*
* Note that this function may return %NULL if the given wdev isn't
* associated with a vif that the driver knows about (e.g. monitor
* or AP_VLAN interfaces.)
*/
struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
/** /**
* enum ieee80211_key_flags - key flags * enum ieee80211_key_flags - key flags
* *
@ -1472,7 +1557,8 @@ struct ieee80211_sta_rates {
* @supp_rates: Bitmap of supported rates (per band) * @supp_rates: Bitmap of supported rates (per band)
* @ht_cap: HT capabilities of this STA; restricted to our own capabilities * @ht_cap: HT capabilities of this STA; restricted to our own capabilities
* @vht_cap: VHT capabilities of this STA; restricted to our own capabilities * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
* @wme: indicates whether the STA supports QoS/WME. * @wme: indicates whether the STA supports QoS/WME (if local devices does,
* otherwise always false)
* @drv_priv: data area for driver use, will always be aligned to * @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information. * sizeof(void *), size is determined in hw information.
* @uapsd_queues: bitmap of queues configured for uapsd. Only valid * @uapsd_queues: bitmap of queues configured for uapsd. Only valid
@ -1488,6 +1574,7 @@ struct ieee80211_sta_rates {
* @tdls: indicates whether the STA is a TDLS peer * @tdls: indicates whether the STA is a TDLS peer
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
* valid if the STA is a TDLS peer in the first place. * valid if the STA is a TDLS peer in the first place.
* @mfp: indicates whether the STA uses management frame protection or not.
*/ */
struct ieee80211_sta { struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS]; u32 supp_rates[IEEE80211_NUM_BANDS];
@ -1504,6 +1591,7 @@ struct ieee80211_sta {
struct ieee80211_sta_rates __rcu *rates; struct ieee80211_sta_rates __rcu *rates;
bool tdls; bool tdls;
bool tdls_initiator; bool tdls_initiator;
bool mfp;
/* must be last */ /* must be last */
u8 drv_priv[0] __aligned(sizeof(void *)); u8 drv_priv[0] __aligned(sizeof(void *));
@ -2844,8 +2932,9 @@ enum ieee80211_reconfig_type {
* @set_bitrate_mask: Set a mask of rates to be used for rate control selection * @set_bitrate_mask: Set a mask of rates to be used for rate control selection
* when transmitting a frame. Currently only legacy rates are handled. * when transmitting a frame. Currently only legacy rates are handled.
* The callback can sleep. * The callback can sleep.
* @rssi_callback: Notify driver when the average RSSI goes above/below * @event_callback: Notify driver about any event in mac80211. See
* thresholds that were registered previously. The callback can sleep. * &enum ieee80211_event_type for the different types.
* The callback can sleep.
* *
* @release_buffered_frames: Release buffered frames according to the given * @release_buffered_frames: Release buffered frames according to the given
* parameters. In the case where the driver buffers some frames for * parameters. In the case where the driver buffers some frames for
@ -3141,9 +3230,9 @@ struct ieee80211_ops {
bool (*tx_frames_pending)(struct ieee80211_hw *hw); bool (*tx_frames_pending)(struct ieee80211_hw *hw);
int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const struct cfg80211_bitrate_mask *mask); const struct cfg80211_bitrate_mask *mask);
void (*rssi_callback)(struct ieee80211_hw *hw, void (*event_callback)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event); const struct ieee80211_event *event);
void (*allow_buffered_frames)(struct ieee80211_hw *hw, void (*allow_buffered_frames)(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
@ -4343,12 +4432,32 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw);
* haven't been re-added to the driver yet. * haven't been re-added to the driver yet.
* @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all
* interfaces, even if they haven't been re-added to the driver yet. * interfaces, even if they haven't been re-added to the driver yet.
* @IEEE80211_IFACE_ITER_ACTIVE: Iterate only active interfaces (netdev is up).
*/ */
enum ieee80211_interface_iteration_flags { enum ieee80211_interface_iteration_flags {
IEEE80211_IFACE_ITER_NORMAL = 0, IEEE80211_IFACE_ITER_NORMAL = 0,
IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0), IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0),
IEEE80211_IFACE_ITER_ACTIVE = BIT(1),
}; };
/**
* ieee80211_iterate_interfaces - iterate interfaces
*
* This function iterates over the interfaces associated with a given
* hardware and calls the callback for them. This includes active as well as
* inactive interfaces. This function allows the iterator function to sleep.
* Will iterate over a new interface during add_interface().
*
* @hw: the hardware struct of which the interfaces should be iterated over
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
* @iterator: the iterator function to call
* @data: first argument of the iterator function
*/
void ieee80211_iterate_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data);
/** /**
* ieee80211_iterate_active_interfaces - iterate active interfaces * ieee80211_iterate_active_interfaces - iterate active interfaces
* *
@ -4364,11 +4473,16 @@ enum ieee80211_interface_iteration_flags {
* @iterator: the iterator function to call * @iterator: the iterator function to call
* @data: first argument of the iterator function * @data: first argument of the iterator function
*/ */
void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, static inline void
u32 iter_flags, ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data); void *data)
{
ieee80211_iterate_interfaces(hw,
iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
iterator, data);
}
/** /**
* ieee80211_iterate_active_interfaces_atomic - iterate active interfaces * ieee80211_iterate_active_interfaces_atomic - iterate active interfaces

View file

@ -25,6 +25,19 @@
* *
*/ */
/*
* This header file defines the userspace API to the wireless stack. Please
* be careful not to break things - i.e. don't move anything around or so
* unless you can demonstrate that it breaks neither API nor ABI.
*
* Additions to the API should be accompanied by actual implementations in
* an upstream driver, so that example implementations exist in case there
* are ever concerns about the precise semantics of the API or changes are
* needed, and to ensure that code for dead (no longer implemented) API
* can actually be identified and removed.
* Nonetheless, semantics should also be documented carefully in this file.
*/
#include <linux/types.h> #include <linux/types.h>
#define NL80211_GENL_NAME "nl80211" #define NL80211_GENL_NAME "nl80211"
@ -1684,6 +1697,10 @@ enum nl80211_commands {
* If set during scheduled scan start then the new scan req will be * If set during scheduled scan start then the new scan req will be
* owned by the netlink socket that created it and the scheduled scan will * owned by the netlink socket that created it and the scheduled scan will
* be stopped when the socket is closed. * be stopped when the socket is closed.
* If set during configuration of regulatory indoor operation then the
* regulatory indoor configuration would be owned by the netlink socket
* that configured the indoor setting, and the indoor operation would be
* cleared 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.
@ -1737,8 +1754,12 @@ enum nl80211_commands {
* should be contained in the result as the sum of the respective counters * should be contained in the result as the sum of the respective counters
* over all channels. * over all channels.
* *
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
* WoWLAN net-detect scan) is started, u32 in seconds. * scheduled scan (or a WoWLAN net-detect scan) is started, u32
* in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
* *
* @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
@ -2107,6 +2128,8 @@ enum nl80211_attrs {
NL80211_ATTR_SCHED_SCAN_DELAY, NL80211_ATTR_SCHED_SCAN_DELAY,
NL80211_ATTR_REG_INDOOR,
/* 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,
@ -3092,7 +3115,8 @@ enum nl80211_mesh_power_mode {
* *
* @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
* established peering with for longer than this time (in seconds), then * established peering with for longer than this time (in seconds), then
* remove it from the STA's list of peers. Default is 30 minutes. * remove it from the STA's list of peers. You may set this to 0 to disable
* the removal of the STA. Default is 30 minutes.
* *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/ */
@ -3694,6 +3718,8 @@ struct nl80211_pattern_support {
* @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
* the chip into a special state -- works best with chips that have * the chip into a special state -- works best with chips that have
* support for low-power operation already (flag) * support for low-power operation already (flag)
* Note that this mode is incompatible with all of the others, if
* any others are even supported by the device.
* @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
* is detected is implementation-specific (flag) * is detected is implementation-specific (flag)
* @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
@ -4327,11 +4353,13 @@ enum nl80211_feature_flags {
/** /**
* enum nl80211_ext_feature_index - bit index of extended features. * enum nl80211_ext_feature_index - bit index of extended features.
* @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
* *
* @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.
*/ */
enum nl80211_ext_feature_index { enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_VHT_IBSS,
/* add new features before the definition below */ /* add new features before the definition below */
NUM_NL80211_EXT_FEATURES, NUM_NL80211_EXT_FEATURES,

View file

@ -85,11 +85,15 @@ struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
return tfm; return tfm;
err = crypto_aead_setkey(tfm, key, key_len); err = crypto_aead_setkey(tfm, key, key_len);
if (!err) if (err)
err = crypto_aead_setauthsize(tfm, mic_len); goto free_aead;
if (!err) err = crypto_aead_setauthsize(tfm, mic_len);
return tfm; if (err)
goto free_aead;
return tfm;
free_aead:
crypto_free_aead(tfm); crypto_free_aead(tfm);
return ERR_PTR(err); return ERR_PTR(err);
} }

View file

@ -80,11 +80,15 @@ struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
return tfm; return tfm;
err = crypto_aead_setkey(tfm, key, key_len); err = crypto_aead_setkey(tfm, key, key_len);
if (!err) if (err)
err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); goto free_aead;
if (!err) err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
return tfm; if (err)
goto free_aead;
return tfm;
free_aead:
crypto_free_aead(tfm); crypto_free_aead(tfm);
return ERR_PTR(err); return ERR_PTR(err);
} }

View file

@ -69,10 +69,10 @@ struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
return tfm; return tfm;
err = crypto_aead_setkey(tfm, key, key_len); err = crypto_aead_setkey(tfm, key, key_len);
if (!err)
return tfm;
if (!err) if (!err)
err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN); err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN);
if (!err)
return tfm;
crypto_free_aead(tfm); crypto_free_aead(tfm);
return ERR_PTR(err); return ERR_PTR(err);

View file

@ -234,6 +234,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
int i, ret = -EOPNOTSUPP; int i, ret = -EOPNOTSUPP;
u16 status = WLAN_STATUS_REQUEST_DECLINED; u16 status = WLAN_STATUS_REQUEST_DECLINED;
if (!sta->sta.ht_cap.ht_supported) {
ht_dbg(sta->sdata,
"STA %pM erroneously requests BA session on tid %d w/o QoS\n",
sta->sta.addr, tid);
/* send a response anyway, it's an error case if we get here */
goto end_no_lock;
}
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
ht_dbg(sta->sdata, ht_dbg(sta->sdata,
"Suspend in progress - Denying ADDBA request (%pM tid %d)\n", "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",

View file

@ -509,11 +509,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
struct tid_ampdu_tx *tid_tx; struct tid_ampdu_tx *tid_tx;
int ret = 0; int ret = 0;
trace_api_start_tx_ba_session(pubsta, tid);
if (WARN(sta->reserved_tid == tid, if (WARN(sta->reserved_tid == tid,
"Requested to start BA session on reserved tid=%d", tid)) "Requested to start BA session on reserved tid=%d", tid))
return -EINVAL; return -EINVAL;
trace_api_start_tx_ba_session(pubsta, tid); if (!pubsta->ht_cap.ht_supported)
return -EINVAL;
if (WARN_ON_ONCE(!local->ops->ampdu_action)) if (WARN_ON_ONCE(!local->ops->ampdu_action))
return -EINVAL; return -EINVAL;
@ -793,6 +796,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct sta_info *sta; struct sta_info *sta;
struct tid_ampdu_tx *tid_tx; struct tid_ampdu_tx *tid_tx;
bool send_delba = false;
trace_api_stop_tx_ba_cb(sdata, ra, tid); trace_api_stop_tx_ba_cb(sdata, ra, tid);
@ -824,13 +828,17 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
} }
if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop) if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop)
ieee80211_send_delba(sta->sdata, ra, tid, send_delba = true;
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
ieee80211_remove_tid_tx(sta, tid); ieee80211_remove_tid_tx(sta, tid);
unlock_sta: unlock_sta:
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (send_delba)
ieee80211_send_delba(sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
mutex_unlock(&sta->ampdu_mlme.mtx); mutex_unlock(&sta->ampdu_mlme.mtx);
unlock: unlock:
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);

View file

@ -24,6 +24,7 @@
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, enum nl80211_iftype type,
u32 *flags, u32 *flags,
struct vif_params *params) struct vif_params *params)
@ -33,7 +34,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
int err; int err;
err = ieee80211_if_add(local, name, &wdev, type, params); err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
@ -977,6 +978,14 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
set & BIT(NL80211_STA_FLAG_ASSOCIATED) && set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
!test_sta_flag(sta, WLAN_STA_ASSOC)) { !test_sta_flag(sta, WLAN_STA_ASSOC)) {
/*
* When peer becomes associated, init rate control as
* well. Some drivers require rate control initialized
* before drv_sta_state() is called.
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
rate_control_rate_init(sta);
ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (ret) if (ret)
return ret; return ret;
@ -1050,6 +1059,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
} }
} }
if (mask & BIT(NL80211_STA_FLAG_WME) &&
local->hw.queues >= IEEE80211_NUM_ACS)
sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
/* auth flags will be set later for TDLS stations */ /* auth flags will be set later for TDLS stations */
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
ret = sta_apply_auth_flags(local, sta, mask, set); ret = sta_apply_auth_flags(local, sta, mask, set);
@ -1064,10 +1077,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
} }
if (mask & BIT(NL80211_STA_FLAG_WME))
sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
if (mask & BIT(NL80211_STA_FLAG_MFP)) { if (mask & BIT(NL80211_STA_FLAG_MFP)) {
sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
if (set & BIT(NL80211_STA_FLAG_MFP)) if (set & BIT(NL80211_STA_FLAG_MFP))
set_sta_flag(sta, WLAN_STA_MFP); set_sta_flag(sta, WLAN_STA_MFP);
else else
@ -1377,11 +1388,6 @@ static int ieee80211_change_station(struct wiphy *wiphy,
if (err) if (err)
goto out_err; goto out_err;
/* When peer becomes authorized, init rate control as well */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
test_sta_flag(sta, WLAN_STA_AUTHORIZED))
rate_control_rate_init(sta);
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
if ((sdata->vif.type == NL80211_IFTYPE_AP || if ((sdata->vif.type == NL80211_IFTYPE_AP ||
@ -2273,7 +2279,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
{ {
struct sta_info *sta; struct sta_info *sta;
enum ieee80211_smps_mode old_req; enum ieee80211_smps_mode old_req;
int i;
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
return -EINVAL; return -EINVAL;
@ -2297,52 +2302,44 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
} }
ht_dbg(sdata, ht_dbg(sdata,
"SMSP %d requested in AP mode, sending Action frame to %d stations\n", "SMPS %d requested in AP mode, sending Action frame to %d stations\n",
smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
mutex_lock(&sdata->local->sta_mtx); mutex_lock(&sdata->local->sta_mtx);
for (i = 0; i < STA_HASH_SIZE; i++) { list_for_each_entry(sta, &sdata->local->sta_list, list) {
for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], /*
lockdep_is_held(&sdata->local->sta_mtx)); * Only stations associated to our AP and
sta; * associated VLANs
sta = rcu_dereference_protected(sta->hnext, */
lockdep_is_held(&sdata->local->sta_mtx))) { if (sta->sdata->bss != &sdata->u.ap)
/* continue;
* Only stations associated to our AP and
* associated VLANs
*/
if (sta->sdata->bss != &sdata->u.ap)
continue;
/* This station doesn't support MIMO - skip it */ /* This station doesn't support MIMO - skip it */
if (sta_info_tx_streams(sta) == 1) if (sta_info_tx_streams(sta) == 1)
continue; continue;
/* /*
* Don't wake up a STA just to send the action frame * Don't wake up a STA just to send the action frame
* unless we are getting more restrictive. * unless we are getting more restrictive.
*/ */
if (test_sta_flag(sta, WLAN_STA_PS_STA) && if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
!ieee80211_smps_is_restrictive(sta->known_smps_mode, !ieee80211_smps_is_restrictive(sta->known_smps_mode,
smps_mode)) { smps_mode)) {
ht_dbg(sdata, ht_dbg(sdata, "Won't send SMPS to sleeping STA %pM\n",
"Won't send SMPS to sleeping STA %pM\n", sta->sta.addr);
sta->sta.addr); continue;
continue;
}
/*
* If the STA is not authorized, wait until it gets
* authorized and the action frame will be sent then.
*/
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
ieee80211_send_smps_action(sdata, smps_mode,
sta->sta.addr,
sdata->vif.bss_conf.bssid);
} }
/*
* If the STA is not authorized, wait until it gets
* authorized and the action frame will be sent then.
*/
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
ieee80211_send_smps_action(sdata, smps_mode, sta->sta.addr,
sdata->vif.bss_conf.bssid);
} }
mutex_unlock(&sdata->local->sta_mtx); mutex_unlock(&sdata->local->sta_mtx);
@ -3581,7 +3578,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7); nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable(); local_bh_disable();
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, sta, skb);
local_bh_enable(); local_bh_enable();
rcu_read_unlock(); rcu_read_unlock();

View file

@ -18,172 +18,6 @@
#define DEBUGFS_FORMAT_BUFFER_SIZE 100 #define DEBUGFS_FORMAT_BUFFER_SIZE 100
#define TX_LATENCY_BIN_DELIMTER_C ','
#define TX_LATENCY_BIN_DELIMTER_S ","
#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n"
#define TX_LATENCY_DISABLED "disable\n"
/*
* Display if Tx latency statistics & bins are enabled/disabled
*/
static ssize_t sta_tx_latency_stat_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
struct ieee80211_tx_latency_bin_ranges *tx_latency;
char *buf;
int bufsz, i, ret;
int pos = 0;
rcu_read_lock();
tx_latency = rcu_dereference(local->tx_latency);
if (tx_latency && tx_latency->n_ranges) {
bufsz = tx_latency->n_ranges * 15;
buf = kzalloc(bufsz, GFP_ATOMIC);
if (!buf)
goto err;
for (i = 0; i < tx_latency->n_ranges; i++)
pos += scnprintf(buf + pos, bufsz - pos, "%d,",
tx_latency->ranges[i]);
pos += scnprintf(buf + pos, bufsz - pos, "\n");
} else if (tx_latency) {
bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1;
buf = kzalloc(bufsz, GFP_ATOMIC);
if (!buf)
goto err;
pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
TX_LATENCY_BINS_DISABLED);
} else {
bufsz = sizeof(TX_LATENCY_DISABLED) + 1;
buf = kzalloc(bufsz, GFP_ATOMIC);
if (!buf)
goto err;
pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
TX_LATENCY_DISABLED);
}
rcu_read_unlock();
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
kfree(buf);
return ret;
err:
rcu_read_unlock();
return -ENOMEM;
}
/*
* Receive input from user regarding Tx latency statistics
* The input should indicate if Tx latency statistics and bins are
* enabled/disabled.
* If bins are enabled input should indicate the amount of different bins and
* their ranges. Each bin will count how many Tx frames transmitted within the
* appropriate latency.
* Legal input is:
* a) "enable(bins disabled)" - to enable only general statistics
* b) "a,b,c,d,...z" - to enable general statistics and bins, where all are
* numbers and a < b < c < d.. < z
* c) "disable" - disable all statistics
* NOTE: must configure Tx latency statistics bins before stations connected.
*/
static ssize_t sta_tx_latency_stat_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_local *local = file->private_data;
char buf[128] = {};
char *bins = buf;
char *token;
int buf_size, i, alloc_size;
int prev_bin = 0;
int n_ranges = 0;
int ret = count;
struct ieee80211_tx_latency_bin_ranges *tx_latency;
if (sizeof(buf) <= count)
return -EINVAL;
buf_size = count;
if (copy_from_user(buf, userbuf, buf_size))
return -EFAULT;
mutex_lock(&local->sta_mtx);
/* cannot change config once we have stations */
if (local->num_sta)
goto unlock;
tx_latency =
rcu_dereference_protected(local->tx_latency,
lockdep_is_held(&local->sta_mtx));
/* disable Tx statistics */
if (!strcmp(buf, TX_LATENCY_DISABLED)) {
if (!tx_latency)
goto unlock;
RCU_INIT_POINTER(local->tx_latency, NULL);
synchronize_rcu();
kfree(tx_latency);
goto unlock;
}
/* Tx latency already enabled */
if (tx_latency)
goto unlock;
if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) {
/* check how many bins and between what ranges user requested */
token = buf;
while (*token != '\0') {
if (*token == TX_LATENCY_BIN_DELIMTER_C)
n_ranges++;
token++;
}
n_ranges++;
}
alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) +
n_ranges * sizeof(u32);
tx_latency = kzalloc(alloc_size, GFP_ATOMIC);
if (!tx_latency) {
ret = -ENOMEM;
goto unlock;
}
tx_latency->n_ranges = n_ranges;
for (i = 0; i < n_ranges; i++) { /* setting bin ranges */
token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S);
sscanf(token, "%d", &tx_latency->ranges[i]);
/* bins values should be in ascending order */
if (prev_bin >= tx_latency->ranges[i]) {
ret = -EINVAL;
kfree(tx_latency);
goto unlock;
}
prev_bin = tx_latency->ranges[i];
}
rcu_assign_pointer(local->tx_latency, tx_latency);
unlock:
mutex_unlock(&local->sta_mtx);
return ret;
}
static const struct file_operations stats_tx_latency_ops = {
.write = sta_tx_latency_stat_write,
.read = sta_tx_latency_stat_read,
.open = simple_open,
.llseek = generic_file_llseek,
};
int mac80211_format_buffer(char __user *userbuf, size_t count, int mac80211_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...) loff_t *ppos, char *fmt, ...)
{ {
@ -440,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop); DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued); DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
local->tx_handlers_drop_unencrypted);
DEBUGFS_STATS_ADD(tx_handlers_drop_fragment, DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
local->tx_handlers_drop_fragment); local->tx_handlers_drop_fragment);
DEBUGFS_STATS_ADD(tx_handlers_drop_wep, DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
@ -475,6 +307,4 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
DEBUGFS_DEVSTATS_ADD(tx_latency);
} }

View file

@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name(struct file *file, \
IEEE80211_IF_FILE_R(name) IEEE80211_IF_FILE_R(name)
/* common attributes */ /* common attributes */
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ],
HEX); HEX);
IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ],
@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
static void add_common_files(struct ieee80211_sub_if_data *sdata) static void add_common_files(struct ieee80211_sub_if_data *sdata)
{ {
DEBUGFS_ADD(drop_unencrypted);
DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);

View file

@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \
.llseek = generic_file_llseek, \ .llseek = generic_file_llseek, \
} }
#define STA_OPS_W(name) \
static const struct file_operations sta_ ##name## _ops = { \
.write = sta_##name##_write, \
.open = simple_open, \
.llseek = generic_file_llseek, \
}
#define STA_OPS_RW(name) \ #define STA_OPS_RW(name) \
static const struct file_operations sta_ ##name## _ops = { \ static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \ .read = sta_##name##_read, \
@ -398,131 +391,6 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
} }
STA_OPS(last_rx_rate); STA_OPS(last_rx_rate);
static int
sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency,
char *buf, int pos, int bufsz)
{
int i;
int range_count = tx_latency->n_ranges;
u32 *bin_ranges = tx_latency->ranges;
pos += scnprintf(buf + pos, bufsz - pos,
"Station\t\t\tTID\tMax\tAvg");
if (range_count) {
pos += scnprintf(buf + pos, bufsz - pos,
"\t<=%d", bin_ranges[0]);
for (i = 0; i < range_count - 1; i++)
pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d",
bin_ranges[i], bin_ranges[i+1]);
pos += scnprintf(buf + pos, bufsz - pos,
"\t%d<", bin_ranges[range_count - 1]);
}
pos += scnprintf(buf + pos, bufsz - pos, "\n");
return pos;
}
static int
sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range,
struct ieee80211_tx_latency_stat *tx_lat,
char *buf, int pos, int bufsz, int tid)
{
u32 avg = 0;
int j;
int bin_count = tx_lat->bin_count;
pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid);
/* make sure you don't divide in 0 */
if (tx_lat->counter)
avg = tx_lat->sum / tx_lat->counter;
pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d",
tx_lat->max, avg);
if (tx_lat_range->n_ranges && tx_lat->bins)
for (j = 0; j < bin_count; j++)
pos += scnprintf(buf + pos, bufsz - pos,
"\t%d", tx_lat->bins[j]);
pos += scnprintf(buf + pos, bufsz - pos, "\n");
return pos;
}
/*
* Output Tx latency statistics station && restart all statistics information
*/
static ssize_t sta_tx_latency_stat_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct ieee80211_local *local = sta->local;
struct ieee80211_tx_latency_bin_ranges *tx_latency;
char *buf;
int bufsz, ret, i;
int pos = 0;
bufsz = 20 * IEEE80211_NUM_TIDS *
sizeof(struct ieee80211_tx_latency_stat);
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rcu_read_lock();
tx_latency = rcu_dereference(local->tx_latency);
if (!sta->tx_lat) {
pos += scnprintf(buf + pos, bufsz - pos,
"Tx latency statistics are not enabled\n");
goto unlock;
}
pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz);
pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr);
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i],
buf, pos, bufsz, i);
unlock:
rcu_read_unlock();
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
kfree(buf);
return ret;
}
STA_OPS(tx_latency_stat);
static ssize_t sta_tx_latency_stat_reset_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
u32 *bins;
int bin_count;
struct sta_info *sta = file->private_data;
int i;
if (!sta->tx_lat)
return -EINVAL;
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
bins = sta->tx_lat[i].bins;
bin_count = sta->tx_lat[i].bin_count;
sta->tx_lat[i].max = 0;
sta->tx_lat[i].sum = 0;
sta->tx_lat[i].counter = 0;
if (bin_count)
memset(bins, 0, bin_count * sizeof(u32));
}
return count;
}
STA_OPS_W(tx_latency_stat_reset);
#define DEBUGFS_ADD(name) \ #define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \ debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops); sta->debugfs.dir, sta, &sta_ ##name## _ops);
@ -576,8 +444,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(last_ack_signal); DEBUGFS_ADD(last_ack_signal);
DEBUGFS_ADD(current_tx_rate); DEBUGFS_ADD(current_tx_rate);
DEBUGFS_ADD(last_rx_rate); DEBUGFS_ADD(last_rx_rate);
DEBUGFS_ADD(tx_latency_stat);
DEBUGFS_ADD(tx_latency_stat_reset);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);

View file

@ -941,13 +941,13 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline void drv_rssi_callback(struct ieee80211_local *local, static inline void drv_event_callback(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,
const enum ieee80211_rssi_event event) const struct ieee80211_event *event)
{ {
trace_drv_rssi_callback(local, sdata, event); trace_drv_event_callback(local, sdata, event);
if (local->ops->rssi_callback) if (local->ops->event_callback)
local->ops->rssi_callback(&local->hw, &sdata->vif, event); local->ops->event_callback(&local->hw, &sdata->vif, event);
trace_drv_return_void(local); trace_drv_return_void(local);
} }

View file

@ -252,8 +252,6 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
break; break;
} }
if (bw != sta->sta.bandwidth)
changed = true;
sta->sta.bandwidth = bw; sta->sta.bandwidth = bw;
sta->cur_max_bandwidth = sta->cur_max_bandwidth =

View file

@ -188,6 +188,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
*/ */
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
chandef, 0); chandef, 0);
/* add VHT capability and information IEs */
if (chandef->width != NL80211_CHAN_WIDTH_20 &&
chandef->width != NL80211_CHAN_WIDTH_40 &&
sband->vht_cap.vht_supported) {
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
sband->vht_cap.cap);
pos = ieee80211_ie_build_vht_oper(pos, &sband->vht_cap,
chandef);
}
} }
if (local->hw.queues >= IEEE80211_NUM_ACS) if (local->hw.queues >= IEEE80211_NUM_ACS)
@ -249,8 +259,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
if (presp) if (presp)
kfree_rcu(presp, rcu_head); kfree_rcu(presp, rcu_head);
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
/* make a copy of the chandef, it could be modified below. */ /* make a copy of the chandef, it could be modified below. */
chandef = *req_chandef; chandef = *req_chandef;
chan = chandef.chan; chan = chandef.chan;
@ -417,6 +425,11 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
NL80211_CHAN_WIDTH_20_NOHT); NL80211_CHAN_WIDTH_20_NOHT);
chandef.width = sdata->u.ibss.chandef.width; chandef.width = sdata->u.ibss.chandef.width;
break; break;
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_160:
chandef = sdata->u.ibss.chandef;
chandef.chan = cbss->channel;
break;
default: default:
/* fall back to 20 MHz for unsupported modes */ /* fall back to 20 MHz for unsupported modes */
cfg80211_chandef_create(&chandef, cbss->channel, cfg80211_chandef_create(&chandef, cbss->channel,
@ -470,22 +483,19 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct beacon_data *presp, *old_presp; struct beacon_data *presp, *old_presp;
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
const struct cfg80211_bss_ies *ies; const struct cfg80211_bss_ies *ies;
u16 capability; u16 capability = 0;
u64 tsf; u64 tsf;
int ret = 0; int ret = 0;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy) if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY; capability = WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid, ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS | ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS,
WLAN_CAPABILITY_PRIVACY, IEEE80211_PRIVACY(ifibss->privacy));
capability);
if (WARN_ON(!cbss)) { if (WARN_ON(!cbss)) {
ret = -EINVAL; ret = -EINVAL;
@ -525,23 +535,17 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
int err, changed = 0; int err, changed = 0;
u16 capability;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
/* update cfg80211 bss information with the new channel */ /* update cfg80211 bss information with the new channel */
if (!is_zero_ether_addr(ifibss->bssid)) { if (!is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
ifibss->chandef.chan, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid, ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS | ifibss->ssid_len,
WLAN_CAPABILITY_PRIVACY, IEEE80211_BSS_TYPE_IBSS,
capability); IEEE80211_PRIVACY(ifibss->privacy));
/* XXX: should not really modify cfg80211 data */ /* XXX: should not really modify cfg80211 data */
if (cbss) { if (cbss) {
cbss->channel = sdata->csa_chandef.chan; cbss->channel = sdata->csa_chandef.chan;
@ -682,19 +686,13 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
struct beacon_data *presp; struct beacon_data *presp;
struct sta_info *sta; struct sta_info *sta;
u16 capability;
if (!is_zero_ether_addr(ifibss->bssid)) { if (!is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid, ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS | ifibss->ssid_len,
WLAN_CAPABILITY_PRIVACY, IEEE80211_BSS_TYPE_IBSS,
capability); IEEE80211_PRIVACY(ifibss->privacy));
if (cbss) { if (cbss) {
cfg80211_unlink_bss(local->hw.wiphy, cbss); cfg80211_unlink_bss(local->hw.wiphy, cbss);
@ -980,6 +978,121 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0); mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
} }
static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
struct ieee802_11_elems *elems,
struct ieee80211_channel *channel)
{
struct sta_info *sta;
enum ieee80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
u32 supp_rates = 0;
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
return;
if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid))
return;
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
if (elems->supp_rates) {
supp_rates = ieee80211_sta_get_rates(sdata, elems,
band, NULL);
if (sta) {
u32 prev_rates;
prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */
scan_width = NL80211_BSS_CHAN_WIDTH_20;
if (rx_status->flag & RX_FLAG_5MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_5;
if (rx_status->flag & RX_FLAG_10MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_10;
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband, scan_width);
if (sta->sta.supp_rates[band] != prev_rates) {
ibss_dbg(sdata,
"updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
sta->sta.addr, prev_rates,
sta->sta.supp_rates[band]);
rates_updated = true;
}
} else {
rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates);
}
}
if (sta && elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS)
sta->sta.wme = true;
if (sta && elems->ht_operation && elems->ht_cap_elem &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
/* we both use HT */
struct ieee80211_ht_cap htcap_ie;
struct cfg80211_chan_def chandef;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
ieee80211_ht_oper_to_chandef(channel,
elems->ht_operation,
&chandef);
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
&htcap_ie,
sta);
if (elems->vht_operation && elems->vht_cap_elem &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20 &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_40) {
/* we both use VHT */
struct ieee80211_vht_cap cap_ie;
struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
ieee80211_vht_oper_to_chandef(channel,
elems->vht_operation,
&chandef);
memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
&cap_ie, sta);
if (memcmp(&cap, &sta->sta.vht_cap, sizeof(cap)))
rates_updated |= true;
}
if (bw != sta->sta.bandwidth)
rates_updated |= true;
if (!cfg80211_chandef_compatible(&sdata->u.ibss.chandef,
&chandef))
WARN_ON_ONCE(1);
}
if (sta && rates_updated) {
u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
u8 rx_nss = sta->sta.rx_nss;
/* Force rx_nss recalculation */
sta->sta.rx_nss = 0;
rate_control_rate_init(sta);
if (sta->sta.rx_nss != rx_nss)
changed |= IEEE80211_RC_NSS_CHANGED;
drv_sta_rc_update(local, sdata, &sta->sta, changed);
}
rcu_read_unlock();
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status, struct ieee80211_rx_status *rx_status,
@ -988,101 +1101,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
struct sta_info *sta;
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
u64 beacon_timestamp, rx_timestamp; u64 beacon_timestamp, rx_timestamp;
u32 supp_rates = 0; u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band; enum ieee80211_band band = rx_status->band;
enum nl80211_bss_scan_width scan_width;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel) if (!channel)
return; return;
if (sdata->vif.type == NL80211_IFTYPE_ADHOC && ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel);
ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) {
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
if (elems->supp_rates) {
supp_rates = ieee80211_sta_get_rates(sdata, elems,
band, NULL);
if (sta) {
u32 prev_rates;
prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */
scan_width = NL80211_BSS_CHAN_WIDTH_20;
if (rx_status->flag & RX_FLAG_5MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_5;
if (rx_status->flag & RX_FLAG_10MHZ)
scan_width = NL80211_BSS_CHAN_WIDTH_10;
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(sband,
scan_width);
if (sta->sta.supp_rates[band] != prev_rates) {
ibss_dbg(sdata,
"updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
sta->sta.addr, prev_rates,
sta->sta.supp_rates[band]);
rates_updated = true;
}
} else {
rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates);
}
}
if (sta && elems->wmm_info)
sta->sta.wme = true;
if (sta && elems->ht_operation && elems->ht_cap_elem &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 &&
sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) {
/* we both use HT */
struct ieee80211_ht_cap htcap_ie;
struct cfg80211_chan_def chandef;
ieee80211_ht_oper_to_chandef(channel,
elems->ht_operation,
&chandef);
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
/*
* fall back to HT20 if we don't use or use
* the other extension channel
*/
if (chandef.center_freq1 !=
sdata->u.ibss.chandef.center_freq1)
htcap_ie.cap_info &=
cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
sdata, sband, &htcap_ie, sta);
}
if (sta && rates_updated) {
u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
u8 rx_nss = sta->sta.rx_nss;
/* Force rx_nss recalculation */
sta->sta.rx_nss = 0;
rate_control_rate_init(sta);
if (sta->sta.rx_nss != rx_nss)
changed |= IEEE80211_RC_NSS_CHANGED;
drv_sta_rc_update(local, sdata, &sta->sta, changed);
}
rcu_read_unlock();
}
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel); channel);
@ -1273,7 +1301,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
NULL, scan_width); NULL, 0, scan_width);
} }
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@ -1304,14 +1332,82 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
if (ifibss->privacy) if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY; capability |= WLAN_CAPABILITY_PRIVACY;
else
sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
&ifibss->chandef, ifibss->basic_rates, &ifibss->chandef, ifibss->basic_rates,
capability, 0, true); capability, 0, true);
} }
static unsigned ibss_setup_channels(struct wiphy *wiphy,
struct ieee80211_channel **channels,
unsigned int channels_max,
u32 center_freq, u32 width)
{
struct ieee80211_channel *chan = NULL;
unsigned int n_chan = 0;
u32 start_freq, end_freq, freq;
if (width <= 20) {
start_freq = center_freq;
end_freq = center_freq;
} else {
start_freq = center_freq - width / 2 + 10;
end_freq = center_freq + width / 2 - 10;
}
for (freq = start_freq; freq <= end_freq; freq += 20) {
chan = ieee80211_get_channel(wiphy, freq);
if (!chan)
continue;
if (n_chan >= channels_max)
return n_chan;
channels[n_chan] = chan;
n_chan++;
}
return n_chan;
}
static unsigned int
ieee80211_ibss_setup_scan_channels(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
struct ieee80211_channel **channels,
unsigned int channels_max)
{
unsigned int n_chan = 0;
u32 width, cf1, cf2 = 0;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_40:
width = 40;
break;
case NL80211_CHAN_WIDTH_80P80:
cf2 = chandef->center_freq2;
/* fall through */
case NL80211_CHAN_WIDTH_80:
width = 80;
break;
case NL80211_CHAN_WIDTH_160:
width = 160;
break;
default:
width = 20;
break;
}
cf1 = chandef->center_freq1;
n_chan = ibss_setup_channels(wiphy, channels, channels_max, cf1, width);
if (cf2)
n_chan += ibss_setup_channels(wiphy, &channels[n_chan],
channels_max - n_chan, cf2,
width);
return n_chan;
}
/* /*
* This function is called with state == IEEE80211_IBSS_MLME_SEARCH * This function is called with state == IEEE80211_IBSS_MLME_SEARCH
*/ */
@ -1325,7 +1421,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
const u8 *bssid = NULL; const u8 *bssid = NULL;
enum nl80211_bss_scan_width scan_width; enum nl80211_bss_scan_width scan_width;
int active_ibss; int active_ibss;
u16 capability;
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -1335,9 +1430,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
if (active_ibss) if (active_ibss)
return; return;
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
if (ifibss->fixed_bssid) if (ifibss->fixed_bssid)
bssid = ifibss->bssid; bssid = ifibss->bssid;
if (ifibss->fixed_channel) if (ifibss->fixed_channel)
@ -1346,8 +1438,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
bssid = ifibss->bssid; bssid = ifibss->bssid;
cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
ifibss->ssid, ifibss->ssid_len, ifibss->ssid, ifibss->ssid_len,
WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, IEEE80211_BSS_TYPE_IBSS,
capability); IEEE80211_PRIVACY(ifibss->privacy));
if (cbss) { if (cbss) {
struct ieee80211_bss *bss; struct ieee80211_bss *bss;
@ -1381,11 +1473,18 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
/* Selected IBSS not found in current scan results - try to scan */ /* Selected IBSS not found in current scan results - try to scan */
if (time_after(jiffies, ifibss->last_scan_completed + if (time_after(jiffies, ifibss->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) { IEEE80211_SCAN_INTERVAL)) {
struct ieee80211_channel *channels[8];
unsigned int num;
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
&ifibss->chandef,
channels,
ARRAY_SIZE(channels));
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ieee80211_request_ibss_scan(sdata, ifibss->ssid,
ifibss->ssid_len, chan, ifibss->ssid_len, channels, num,
scan_width); scan_width);
} else { } else {
int interval = IEEE80211_SCAN_INTERVAL; int interval = IEEE80211_SCAN_INTERVAL;

View file

@ -830,8 +830,6 @@ struct ieee80211_sub_if_data {
unsigned long state; unsigned long state;
int drop_unencrypted;
char name[IFNAMSIZ]; char name[IFNAMSIZ];
/* Fragment table for host-based reassembly */ /* Fragment table for host-based reassembly */
@ -1042,24 +1040,6 @@ struct tpt_led_trigger {
}; };
#endif #endif
/*
* struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges
*
* Measuring Tx latency statistics. Counts how many Tx frames transmitted in a
* certain latency range (in Milliseconds). Each station that uses these
* ranges will have bins to count the amount of frames received in that range.
* The user can configure the ranges via debugfs.
* If ranges is NULL then Tx latency statistics bins are disabled for all
* stations.
*
* @n_ranges: number of ranges that are taken in account
* @ranges: the ranges that the user requested or NULL if disabled.
*/
struct ieee80211_tx_latency_bin_ranges {
int n_ranges;
u32 ranges[];
};
/** /**
* mac80211 scan flags - currently active scan mode * mac80211 scan flags - currently active scan mode
* *
@ -1211,12 +1191,6 @@ struct ieee80211_local {
struct timer_list sta_cleanup; struct timer_list sta_cleanup;
int sta_generation; int sta_generation;
/*
* Tx latency statistics parameters for all stations.
* Can enable via debugfs (NULL when disabled).
*/
struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency;
struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet; struct tasklet_struct tx_pending_tasklet;
@ -1298,7 +1272,6 @@ struct ieee80211_local {
/* TX/RX handler statistics */ /* TX/RX handler statistics */
unsigned int tx_handlers_drop; unsigned int tx_handlers_drop;
unsigned int tx_handlers_queued; unsigned int tx_handlers_queued;
unsigned int tx_handlers_drop_unencrypted;
unsigned int tx_handlers_drop_fragment; unsigned int tx_handlers_drop_fragment;
unsigned int tx_handlers_drop_wep; unsigned int tx_handlers_drop_wep;
unsigned int tx_handlers_drop_not_assoc; unsigned int tx_handlers_drop_not_assoc;
@ -1568,7 +1541,8 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_scan_work(struct work_struct *work); void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len, const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan, struct ieee80211_channel **channels,
unsigned int n_channels,
enum nl80211_bss_scan_width scan_width); enum nl80211_bss_scan_width scan_width);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req); struct cfg80211_scan_request *req);
@ -1617,6 +1591,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
int ieee80211_iface_init(void); int ieee80211_iface_init(void);
void ieee80211_iface_exit(void); void ieee80211_iface_exit(void);
int ieee80211_if_add(struct ieee80211_local *local, const char *name, int ieee80211_if_add(struct ieee80211_local *local, const char *name,
unsigned char name_assign_type,
struct wireless_dev **new_wdev, enum nl80211_iftype type, struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params); struct vif_params *params);
int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
@ -1784,7 +1759,8 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp); gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify); bool bss_notify);
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid, struct sk_buff *skb, int tid,
@ -1979,6 +1955,8 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 prot_mode); u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap); u32 cap);
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband, const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates); const u8 *srates, int srates_len, u32 *rates);
@ -1994,6 +1972,9 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef); struct cfg80211_chan_def *chandef);
void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_vht_operation *oper,
struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check int __must_check

View file

@ -1508,7 +1508,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
} }
/* reset some values that shouldn't be kept across type changes */ /* reset some values that shouldn't be kept across type changes */
sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION) if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false; sdata->u.mgd.use_4addr = false;
@ -1649,6 +1648,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
} }
int ieee80211_if_add(struct ieee80211_local *local, const char *name, int ieee80211_if_add(struct ieee80211_local *local, const char *name,
unsigned char name_assign_type,
struct wireless_dev **new_wdev, enum nl80211_iftype type, struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params) struct vif_params *params)
{ {
@ -1677,7 +1677,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
txqs = IEEE80211_NUM_ACS; txqs = IEEE80211_NUM_ACS;
ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
name, NET_NAME_UNKNOWN, name, name_assign_type,
ieee80211_if_setup, txqs, 1); ieee80211_if_setup, txqs, 1);
if (!ndev) if (!ndev)
return -ENOMEM; return -ENOMEM;

View file

@ -492,6 +492,7 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
for (j = 0; j < len; j++) for (j = 0; j < len; j++)
key->u.gen.rx_pn[i][j] = key->u.gen.rx_pn[i][j] =
seq[len - j - 1]; seq[len - j - 1];
key->flags |= KEY_FLAG_CIPHER_SCHEME;
} }
} }
memcpy(key->conf.key, key_data, key_len); memcpy(key->conf.key, key_data, key_len);

View file

@ -30,10 +30,12 @@ struct sta_info;
* @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present
* in the hardware for TX crypto hardware acceleration. * in the hardware for TX crypto hardware acceleration.
* @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped. * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.
* @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme
*/ */
enum ieee80211_internal_key_flags { enum ieee80211_internal_key_flags {
KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0),
KEY_FLAG_TAINTED = BIT(1), KEY_FLAG_TAINTED = BIT(1),
KEY_FLAG_CIPHER_SCHEME = BIT(2),
}; };
enum ieee80211_internal_tkip_state { enum ieee80211_internal_tkip_state {

View file

@ -1057,7 +1057,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* add one default STA interface if supported */ /* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
!(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) { !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
result = ieee80211_if_add(local, "wlan%d", NULL, result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
NL80211_IFTYPE_STATION, NULL); NL80211_IFTYPE_STATION, NULL);
if (result) if (result)
wiphy_warn(local->hw.wiphy, wiphy_warn(local->hw.wiphy,
@ -1201,8 +1201,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
ieee80211_free_ack_frame, NULL); ieee80211_free_ack_frame, NULL);
idr_destroy(&local->ack_status_frames); idr_destroy(&local->ack_status_frames);
kfree(rcu_access_pointer(local->tx_latency));
sta_info_stop(local); sta_info_stop(local);
wiphy_free(local->hw.wiphy); wiphy_free(local->hw.wiphy);

View file

@ -574,7 +574,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 changed; u32 changed;
ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); if (ifmsh->mshcfg.plink_timeout > 0)
ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
mesh_path_expire(sdata); mesh_path_expire(sdata);
changed = mesh_accept_plinks_update(sdata); changed = mesh_accept_plinks_update(sdata);

View file

@ -17,7 +17,7 @@
#define PLINK_GET_PLID(p) (p + 4) #define PLINK_GET_PLID(p) (p + 4)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000)) jiffies + msecs_to_jiffies(t)))
enum plink_event { enum plink_event {
PLINK_UNDEFINED, PLINK_UNDEFINED,
@ -382,6 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band = ieee80211_get_sdata_band(sdata); enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0, changed = 0; u32 rates, basic_rates = 0, changed = 0;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
@ -401,6 +402,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
elems->ht_cap_elem, sta)) elems->ht_cap_elem, sta))
changed |= IEEE80211_RC_BW_CHANGED; changed |= IEEE80211_RC_BW_CHANGED;
if (bw != sta->sta.bandwidth)
changed |= IEEE80211_RC_BW_CHANGED;
/* HT peer is operating 20MHz-only */ /* HT peer is operating 20MHz-only */
if (elems->ht_operation && if (elems->ht_operation &&
!(elems->ht_operation->ht_param & !(elems->ht_operation->ht_param &
@ -621,9 +625,9 @@ static void mesh_plink_timer(unsigned long data)
sta->llid, sta->plid, reason); sta->llid, sta->plid, reason);
} }
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
{ {
sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
sta->plink_timer.data = (unsigned long) sta; sta->plink_timer.data = (unsigned long) sta;
sta->plink_timer.function = mesh_plink_timer; sta->plink_timer.function = mesh_plink_timer;
sta->plink_timeout = timeout; sta->plink_timeout = timeout;

View file

@ -1168,11 +1168,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (!conf) { if (!conf) {
sdata_info(sdata, sdata_info(sdata,
"no channel context assigned to vif?, disconnecting\n"); "no channel context assigned to vif?, disconnecting\n");
ieee80211_queue_work(&local->hw, goto drop_connection;
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
mutex_unlock(&local->mtx);
return;
} }
chanctx = container_of(conf, struct ieee80211_chanctx, conf); chanctx = container_of(conf, struct ieee80211_chanctx, conf);
@ -1181,11 +1177,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
sdata_info(sdata, sdata_info(sdata,
"driver doesn't support chan-switch with channel contexts\n"); "driver doesn't support chan-switch with channel contexts\n");
ieee80211_queue_work(&local->hw, goto drop_connection;
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
mutex_unlock(&local->mtx);
return;
} }
ch_switch.timestamp = timestamp; ch_switch.timestamp = timestamp;
@ -1197,11 +1189,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (drv_pre_channel_switch(sdata, &ch_switch)) { if (drv_pre_channel_switch(sdata, &ch_switch)) {
sdata_info(sdata, sdata_info(sdata,
"preparing for channel switch failed, disconnecting\n"); "preparing for channel switch failed, disconnecting\n");
ieee80211_queue_work(&local->hw, goto drop_connection;
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
mutex_unlock(&local->mtx);
return;
} }
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@ -1210,11 +1198,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, sdata_info(sdata,
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n", "failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
res); res);
ieee80211_queue_work(&local->hw, goto drop_connection;
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
mutex_unlock(&local->mtx);
return;
} }
mutex_unlock(&local->chanctx_mtx); mutex_unlock(&local->chanctx_mtx);
@ -1244,6 +1228,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
mod_timer(&ifmgd->chswitch_timer, mod_timer(&ifmgd->chswitch_timer,
TU_TO_EXP_TIME((csa_ie.count - 1) * TU_TO_EXP_TIME((csa_ie.count - 1) *
cbss->beacon_interval)); cbss->beacon_interval));
return;
drop_connection:
ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
mutex_unlock(&local->mtx);
} }
static bool static bool
@ -1633,9 +1622,6 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{ {
struct ieee80211_local *local = (void *) data; struct ieee80211_local *local = (void *) data;
if (local->quiescing || local->suspended)
return;
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
} }
@ -2260,7 +2246,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
else else
ssid_len = ssid[1]; ssid_len = ssid[1];
ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
ssid + 2, ssid_len, NULL, ssid + 2, ssid_len, NULL,
0, (u32) -1, true, 0, 0, (u32) -1, true, 0,
ifmgd->associated->channel, false); ifmgd->associated->channel, false);
@ -2372,6 +2358,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
} }
EXPORT_SYMBOL(ieee80211_ap_probereq_get); EXPORT_SYMBOL(ieee80211_ap_probereq_get);
static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
const u8 *buf, size_t len, bool tx,
u16 reason)
{
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
.u.mlme.reason = reason,
};
if (tx)
cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
else
cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
drv_event_callback(sdata->local, sdata, &event);
}
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
@ -2397,8 +2401,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
} }
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
IEEE80211_DEAUTH_FRAME_LEN); WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
sdata_unlock(sdata); sdata_unlock(sdata);
} }
@ -2522,6 +2527,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
u16 auth_alg, auth_transaction, status_code; u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = AUTH_EVENT,
};
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -2554,6 +2563,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
mgmt->sa, status_code); mgmt->sa, status_code);
ieee80211_destroy_auth_data(sdata, false); ieee80211_destroy_auth_data(sdata, false);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
return; return;
} }
@ -2576,6 +2588,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
return; return;
} }
event.u.mlme.status = MLME_SUCCESS;
drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "authenticated\n"); sdata_info(sdata, "authenticated\n");
ifmgd->auth_data->done = true; ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
@ -2694,7 +2708,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL); ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
} }
@ -2720,7 +2734,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL); ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
} }
static void ieee80211_get_rates(struct ieee80211_supported_band *sband, static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@ -2982,10 +2996,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta); rate_control_rate_init(sta);
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
set_sta_flag(sta, WLAN_STA_MFP); set_sta_flag(sta, WLAN_STA_MFP);
sta->sta.mfp = true;
} else {
sta->sta.mfp = false;
}
sta->sta.wme = elems.wmm_param; sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@ -3055,6 +3073,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u8 *pos; u8 *pos;
bool reassoc; bool reassoc;
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = ASSOC_EVENT,
};
sdata_assert_lock(sdata); sdata_assert_lock(sdata);
@ -3106,6 +3128,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, "%pM denied association (code=%d)\n", sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code); mgmt->sa, status_code);
ieee80211_destroy_assoc_data(sdata, false); ieee80211_destroy_assoc_data(sdata, false);
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
} else { } else {
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */ /* oops -- internal error -- send timeout for now */
@ -3113,6 +3138,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
cfg80211_assoc_timeout(sdata->dev, bss); cfg80211_assoc_timeout(sdata->dev, bss);
return; return;
} }
event.u.mlme.status = MLME_SUCCESS;
drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "associated\n"); sdata_info(sdata, "associated\n");
/* /*
@ -3315,6 +3342,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
int sig = ifmgd->ave_beacon_signal; int sig = ifmgd->ave_beacon_signal;
int last_sig = ifmgd->last_ave_beacon_signal; int last_sig = ifmgd->last_ave_beacon_signal;
struct ieee80211_event event = {
.type = RSSI_EVENT,
};
/* /*
* if signal crosses either of the boundaries, invoke callback * if signal crosses either of the boundaries, invoke callback
@ -3323,12 +3353,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sig > ifmgd->rssi_max_thold && if (sig > ifmgd->rssi_max_thold &&
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig; ifmgd->last_ave_beacon_signal = sig;
drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); event.u.rssi.data = RSSI_EVENT_HIGH;
drv_event_callback(local, sdata, &event);
} else if (sig < ifmgd->rssi_min_thold && } else if (sig < ifmgd->rssi_min_thold &&
(last_sig >= ifmgd->rssi_max_thold || (last_sig >= ifmgd->rssi_max_thold ||
last_sig == 0)) { last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig; ifmgd->last_ave_beacon_signal = sig;
drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); event.u.rssi.data = RSSI_EVENT_LOW;
drv_event_callback(local, sdata, &event);
} }
} }
@ -3433,6 +3465,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ifmgd->csa_waiting_bcn) if (ifmgd->csa_waiting_bcn)
ieee80211_chswitch_post_beacon(sdata); ieee80211_chswitch_post_beacon(sdata);
/*
* Update beacon timing and dtim count on every beacon appearance. This
* will allow the driver to use the most updated values. Do it before
* comparing this one with last received beacon.
* IMPORTANT: These parameters would possibly be out of sync by the time
* the driver will use them. The synchronized view is currently
* guaranteed only in certain callbacks.
*/
if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
sdata->vif.bss_conf.sync_tsf =
le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp;
if (elems.tim)
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
}
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return; return;
ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc = ncrc;
@ -3460,18 +3512,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
else else
bss_conf->dtim_period = 1; bss_conf->dtim_period = 1;
if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
sdata->vif.bss_conf.sync_tsf =
le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts =
rx_status->device_timestamp;
if (elems.tim)
sdata->vif.bss_conf.sync_dtim_count =
elems.tim->dtim_count;
else
sdata->vif.bss_conf.sync_dtim_count = 0;
}
changed |= BSS_CHANGED_BEACON_INFO; changed |= BSS_CHANGED_BEACON_INFO;
ifmgd->have_beacon = true; ifmgd->have_beacon = true;
@ -3502,8 +3542,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DEAUTH_LEAVING, WLAN_REASON_DEAUTH_LEAVING,
true, deauth_buf); true, deauth_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf, ieee80211_report_disconnect(sdata, deauth_buf,
sizeof(deauth_buf)); sizeof(deauth_buf), true,
WLAN_REASON_DEAUTH_LEAVING);
return; return;
} }
@ -3621,8 +3662,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
tx, frame_buf); tx, frame_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
IEEE80211_DEAUTH_FRAME_LEN); reason);
} }
static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@ -3816,12 +3857,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
ieee80211_destroy_auth_data(sdata, false); ieee80211_destroy_auth_data(sdata, false);
} else if (ieee80211_probe_auth(sdata)) { } else if (ieee80211_probe_auth(sdata)) {
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = AUTH_EVENT,
.u.mlme.status = MLME_TIMEOUT,
};
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
ieee80211_destroy_auth_data(sdata, false); ieee80211_destroy_auth_data(sdata, false);
cfg80211_auth_timeout(sdata->dev, bssid); cfg80211_auth_timeout(sdata->dev, bssid);
drv_event_callback(sdata->local, sdata, &event);
} }
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
run_again(sdata, ifmgd->auth_data->timeout); run_again(sdata, ifmgd->auth_data->timeout);
@ -3831,9 +3878,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) || if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
ieee80211_do_assoc(sdata)) { ieee80211_do_assoc(sdata)) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss; struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = ASSOC_EVENT,
.u.mlme.status = MLME_TIMEOUT,
};
ieee80211_destroy_assoc_data(sdata, false); ieee80211_destroy_assoc_data(sdata, false);
cfg80211_assoc_timeout(sdata->dev, bss); cfg80211_assoc_timeout(sdata->dev, bss);
drv_event_callback(sdata->local, sdata, &event);
} }
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout); run_again(sdata, ifmgd->assoc_data->timeout);
@ -3905,12 +3958,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
{ {
struct ieee80211_sub_if_data *sdata = struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data; (struct ieee80211_sub_if_data *) data;
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (local->quiescing)
return;
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return; return;
@ -3926,9 +3975,6 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
if (local->quiescing)
return;
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return; return;
@ -3991,6 +4037,34 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
IEEE80211_DEAUTH_FRAME_LEN); IEEE80211_DEAUTH_FRAME_LEN);
} }
/* This is a bit of a hack - we should find a better and more generic
* solution to this. Normally when suspending, cfg80211 will in fact
* deauthenticate. However, it doesn't (and cannot) stop an ongoing
* auth (not so important) or assoc (this is the problem) process.
*
* As a consequence, it can happen that we are in the process of both
* associating and suspending, and receive an association response
* after cfg80211 has checked if it needs to disconnect, but before
* we actually set the flag to drop incoming frames. This will then
* cause the workqueue flush to process the association response in
* the suspend, resulting in a successful association just before it
* tries to remove the interface from the driver, which now though
* has a channel context assigned ... this results in issues.
*
* To work around this (for now) simply deauth here again if we're
* now connected.
*/
if (ifmgd->associated && !sdata->local->wowlan) {
u8 bssid[ETH_ALEN];
struct cfg80211_deauth_request req = {
.reason_code = WLAN_REASON_DEAUTH_LEAVING,
.bssid = bssid,
};
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
ieee80211_mgd_deauth(sdata, &req);
}
sdata_unlock(sdata); sdata_unlock(sdata);
} }
@ -4379,6 +4453,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
} else } else
WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid)); WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
/* Cancel scan to ensure that nothing interferes with connection */
if (local->scanning)
ieee80211_scan_cancel(local);
return 0; return 0;
} }
@ -4467,8 +4545,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED, WLAN_REASON_UNSPECIFIED,
false, frame_buf); false, frame_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf)); sizeof(frame_buf), true,
WLAN_REASON_UNSPECIFIED);
} }
sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@ -4568,8 +4647,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED, WLAN_REASON_UNSPECIFIED,
false, frame_buf); false, frame_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf)); sizeof(frame_buf), true,
WLAN_REASON_UNSPECIFIED);
} }
if (ifmgd->auth_data && !ifmgd->auth_data->done) { if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@ -4859,8 +4939,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->reason_code, tx, req->reason_code, tx,
frame_buf); frame_buf);
ieee80211_destroy_auth_data(sdata, false); ieee80211_destroy_auth_data(sdata, false);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf,
IEEE80211_DEAUTH_FRAME_LEN); sizeof(frame_buf), true,
req->reason_code);
return 0; return 0;
} }
@ -4874,8 +4955,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf); req->reason_code, tx, frame_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf,
IEEE80211_DEAUTH_FRAME_LEN); sizeof(frame_buf), true,
req->reason_code);
return 0; return 0;
} }
@ -4907,8 +4989,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
req->reason_code, !req->local_state_change, req->reason_code, !req->local_state_change,
frame_buf); frame_buf);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
IEEE80211_DEAUTH_FRAME_LEN); req->reason_code);
return 0; return 0;
} }

View file

@ -59,9 +59,26 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
cancel_work_sync(&local->dynamic_ps_enable_work); cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer); del_timer_sync(&local->dynamic_ps_timer);
local->wowlan = wowlan && local->open_count; local->wowlan = wowlan;
if (local->wowlan) { if (local->wowlan) {
int err = drv_suspend(local, wowlan); int err;
/* Drivers don't expect to suspend while some operations like
* authenticating or associating are in progress. It doesn't
* make sense anyway to accept that, since the authentication
* or association would never finish since the driver can't do
* that on its own.
* Thus, clean up in-progress auth/assoc first.
*/
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
continue;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
continue;
ieee80211_mgd_quiesce(sdata);
}
err = drv_suspend(local, wowlan);
if (err < 0) { if (err < 0) {
local->quiescing = false; local->quiescing = false;
local->wowlan = false; local->wowlan = false;
@ -80,6 +97,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return err; return err;
} else if (err > 0) { } else if (err > 0) {
WARN_ON(err != 1); WARN_ON(err != 1);
/* cfg80211 will call back into mac80211 to disconnect
* all interfaces, allow that to proceed properly
*/
ieee80211_wake_queues_by_reason(hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
false);
return err; return err;
} else { } else {
goto suspend; goto suspend;

View file

@ -17,10 +17,11 @@
#include "rc80211_minstrel.h" #include "rc80211_minstrel.h"
#include "rc80211_minstrel_ht.h" #include "rc80211_minstrel_ht.h"
#define AVG_AMPDU_SIZE 16
#define AVG_PKT_SIZE 1200 #define AVG_PKT_SIZE 1200
/* Number of bits for an average sized packet */ /* Number of bits for an average sized packet */
#define MCS_NBITS (AVG_PKT_SIZE << 3) #define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
/* Number of symbols for a packet with (bps) bits per symbol */ /* Number of symbols for a packet with (bps) bits per symbol */
#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) #define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
@ -33,7 +34,8 @@
) )
/* Transmit duration for the raw data part of an average sized packet */ /* Transmit duration for the raw data part of an average sized packet */
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) #define MCS_DURATION(streams, sgi, bps) \
(MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE)
#define BW_20 0 #define BW_20 0
#define BW_40 1 #define BW_40 1

View file

@ -1912,8 +1912,7 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
/* Drop unencrypted frames if key is set. */ /* Drop unencrypted frames if key is set. */
if (unlikely(!ieee80211_has_protected(fc) && if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_nullfunc(fc) && !ieee80211_is_nullfunc(fc) &&
ieee80211_is_data(fc) && ieee80211_is_data(fc) && rx->key))
(rx->key || rx->sdata->drop_unencrypted)))
return -EACCES; return -EACCES;
return 0; return 0;
@ -2043,6 +2042,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
struct sta_info *dsta; struct sta_info *dsta;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
skb = rx->skb; skb = rx->skb;
xmit_skb = NULL; xmit_skb = NULL;
@ -2173,8 +2175,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
dev_kfree_skb(rx->skb); dev_kfree_skb(rx->skb);
continue; continue;
} }
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
ieee80211_deliver_skb(rx); ieee80211_deliver_skb(rx);
} }
@ -2400,9 +2400,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
rx->skb->dev = dev; rx->skb->dev = dev;
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
!is_multicast_ether_addr( !is_multicast_ether_addr(
((struct ethhdr *)rx->skb->data)->h_dest) && ((struct ethhdr *)rx->skb->data)->h_dest) &&
@ -3128,6 +3125,12 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
goto rxh_next; \ goto rxh_next; \
} while (0); } while (0);
/* Lock here to avoid hitting all of the data used in the RX
* path (e.g. key data, station data, ...) concurrently when
* a frame is released from the reorder buffer due to timeout
* from the timer, potentially concurrently with RX from the
* driver.
*/
spin_lock_bh(&rx->local->rx_path_lock); spin_lock_bh(&rx->local->rx_path_lock);
while ((skb = __skb_dequeue(frames))) { while ((skb = __skb_dequeue(frames))) {

View file

@ -928,11 +928,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len, const u8 *ssid, u8 ssid_len,
struct ieee80211_channel *chan, struct ieee80211_channel **channels,
unsigned int n_channels,
enum nl80211_bss_scan_width scan_width) enum nl80211_bss_scan_width scan_width)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
int ret = -EBUSY; int ret = -EBUSY, i, n_ch = 0;
enum ieee80211_band band; enum ieee80211_band band;
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
@ -942,9 +943,8 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
goto unlock; goto unlock;
/* fill internal scan request */ /* fill internal scan request */
if (!chan) { if (!channels) {
int i, max_n; int max_n;
int n_ch = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band]) if (!local->hw.wiphy->bands[band])
@ -969,12 +969,19 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
local->int_scan_req->n_channels = n_ch; local->int_scan_req->n_channels = n_ch;
} else { } else {
if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR | for (i = 0; i < n_channels; i++) {
IEEE80211_CHAN_DISABLED))) if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED))
continue;
local->int_scan_req->channels[n_ch] = channels[i];
n_ch++;
}
if (WARN_ON_ONCE(n_ch == 0))
goto unlock; goto unlock;
local->int_scan_req->channels[0] = chan; local->int_scan_req->n_channels = n_ch;
local->int_scan_req->n_channels = 1;
} }
local->int_scan_req->ssids = &local->scan_ssid; local->int_scan_req->ssids = &local->scan_ssid;

View file

@ -229,17 +229,9 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
*/ */
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{ {
int i;
if (sta->rate_ctrl) if (sta->rate_ctrl)
rate_control_free_sta(sta); rate_control_free_sta(sta);
if (sta->tx_lat) {
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
kfree(sta->tx_lat[i].bins);
kfree(sta->tx_lat);
}
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
kfree(rcu_dereference_raw(sta->sta.rates)); kfree(rcu_dereference_raw(sta->sta.rates));
@ -295,42 +287,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct sta_info *sta; struct sta_info *sta;
struct timespec uptime; struct timespec uptime;
struct ieee80211_tx_latency_bin_ranges *tx_latency;
int i; int i;
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
if (!sta) if (!sta)
return NULL; return NULL;
rcu_read_lock();
tx_latency = rcu_dereference(local->tx_latency);
/* init stations Tx latency statistics && TID bins */
if (tx_latency) {
sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
sizeof(struct ieee80211_tx_latency_stat),
GFP_ATOMIC);
if (!sta->tx_lat) {
rcu_read_unlock();
goto free;
}
if (tx_latency->n_ranges) {
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/* size of bins is size of the ranges +1 */
sta->tx_lat[i].bin_count =
tx_latency->n_ranges + 1;
sta->tx_lat[i].bins =
kcalloc(sta->tx_lat[i].bin_count,
sizeof(u32), GFP_ATOMIC);
if (!sta->tx_lat[i].bins) {
rcu_read_unlock();
goto free;
}
}
}
}
rcu_read_unlock();
spin_lock_init(&sta->lock); spin_lock_init(&sta->lock);
spin_lock_init(&sta->ps_lock); spin_lock_init(&sta->ps_lock);
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
@ -359,8 +321,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
ewma_init(&sta->chain_signal_avg[i], 1024, 8); ewma_init(&sta->chain_signal_avg[i], 1024, 8);
if (sta_prepare_rate_control(local, sta, gfp)) if (sta_prepare_rate_control(local, sta, gfp)) {
goto free; kfree(sta);
return NULL;
}
for (i = 0; i < IEEE80211_NUM_TIDS; i++) { for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/* /*
@ -405,16 +369,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
} }
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta;
free: return sta;
if (sta->tx_lat) {
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
kfree(sta->tx_lat[i].bins);
kfree(sta->tx_lat);
}
kfree(sta);
return NULL;
} }
static int sta_info_insert_check(struct sta_info *sta) static int sta_info_insert_check(struct sta_info *sta)
@ -1275,7 +1231,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
} }
info->band = chanctx_conf->def.chan->band; info->band = chanctx_conf->def.chan->band;
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, sta, skb);
rcu_read_unlock(); rcu_read_unlock();
} }

View file

@ -234,25 +234,6 @@ struct sta_ampdu_mlme {
u8 dialog_token_allocator; u8 dialog_token_allocator;
}; };
/*
* struct ieee80211_tx_latency_stat - Tx latency statistics
*
* Measures TX latency and jitter for a station per TID.
*
* @max: worst case latency
* @sum: sum of all latencies
* @counter: amount of Tx frames sent from interface
* @bins: each bin counts how many frames transmitted within a certain
* latency range. when disabled it is NULL.
* @bin_count: amount of bins.
*/
struct ieee80211_tx_latency_stat {
u32 max;
u32 sum;
u32 counter;
u32 *bins;
u32 bin_count;
};
/* Value to indicate no TID reservation */ /* Value to indicate no TID reservation */
#define IEEE80211_TID_UNRESERVED 0xff #define IEEE80211_TID_UNRESERVED 0xff
@ -314,7 +295,6 @@ struct ieee80211_tx_latency_stat {
* @tid_seq: per-TID sequence numbers for sending to this STA * @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers * @timer_to_tid: identity mapping to ID timers
* @tx_lat: Tx latency statistics
* @llid: Local link ID * @llid: Local link ID
* @plid: Peer link ID * @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state * @reason: Cancel reason on PLINK_HOLDING state
@ -435,8 +415,6 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme; struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[IEEE80211_NUM_TIDS]; u8 timer_to_tid[IEEE80211_NUM_TIDS];
struct ieee80211_tx_latency_stat *tx_lat;
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
/* /*
* Mesh peer link attributes * Mesh peer link attributes

View file

@ -12,7 +12,6 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/time.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
@ -514,73 +513,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
} }
} }
/*
* Measure Tx frame completion and removal time for Tx latency statistics
* calculation. A single Tx frame latency should be measured from when it
* is entering the Kernel until we receive Tx complete confirmation indication
* and remove the skb.
*/
static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
struct sk_buff *skb,
struct sta_info *sta,
struct ieee80211_hdr *hdr)
{
u32 msrmnt;
u16 tid;
u8 *qc;
int i, bin_range_count;
u32 *bin_ranges;
__le16 fc;
struct ieee80211_tx_latency_stat *tx_lat;
struct ieee80211_tx_latency_bin_ranges *tx_latency;
ktime_t skb_arv = skb->tstamp;
tx_latency = rcu_dereference(local->tx_latency);
/* assert Tx latency stats are enabled & frame arrived when enabled */
if (!tx_latency || !ktime_to_ns(skb_arv))
return;
fc = hdr->frame_control;
if (!ieee80211_is_data(fc)) /* make sure it is a data frame */
return;
/* get frame tid */
if (ieee80211_is_data_qos(hdr->frame_control)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
} else {
tid = 0;
}
tx_lat = &sta->tx_lat[tid];
/* Calculate the latency */
msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
if (tx_lat->max < msrmnt) /* update stats */
tx_lat->max = msrmnt;
tx_lat->counter++;
tx_lat->sum += msrmnt;
if (!tx_lat->bins) /* bins not activated */
return;
/* count how many Tx frames transmitted with the appropriate latency */
bin_range_count = tx_latency->n_ranges;
bin_ranges = tx_latency->ranges;
for (i = 0; i < bin_range_count; i++) {
if (msrmnt <= bin_ranges[i]) {
tx_lat->bins[i]++;
break;
}
}
if (i == bin_range_count) /* msrmnt is bigger than the biggest range */
tx_lat->bins[i]++;
}
/* /*
* Use a static threshold for now, best value to be determined * Use a static threshold for now, best value to be determined
* by testing ... * by testing ...
@ -853,12 +785,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (acked) if (acked)
sta->last_ack_signal = info->status.ack_signal; sta->last_ack_signal = info->status.ack_signal;
/*
* Measure frame removal for tx latency
* statistics calculation
*/
ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);
} }
rcu_read_unlock(); rcu_read_unlock();

View file

@ -136,6 +136,24 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
*pos = 2 * subband_cnt; *pos = 2 * subband_cnt;
} }
static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
u8 *pos;
u8 op_class;
if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
&op_class))
return;
pos = skb_put(skb, 4);
*pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
*pos++ = 2; /* len */
*pos++ = op_class;
*pos++ = op_class; /* give current operating class as alternate too */
}
static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
{ {
u8 *pos = (void *)skb_put(skb, 3); u8 *pos = (void *)skb_put(skb, 3);
@ -193,6 +211,17 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
} }
static void
ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 *pos = (void *)skb_put(skb, 4);
*pos++ = WLAN_EID_AID;
*pos++ = 2; /* len */
put_unaligned_le16(ifmgd->aid, pos);
}
/* translate numbering in the WMM parameter IE to the mac80211 notation */ /* translate numbering in the WMM parameter IE to the mac80211 notation */
static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
{ {
@ -271,21 +300,11 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct sta_info *sta = NULL; struct sta_info *sta = NULL;
size_t offset = 0, noffset; size_t offset = 0, noffset;
u8 *pos; u8 *pos;
rcu_read_lock();
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock();
return;
}
}
ieee80211_add_srates_ie(sdata, skb, false, band); ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_supp_channels(sdata, skb); ieee80211_tdls_add_supp_channels(sdata, skb);
@ -338,6 +357,19 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset; offset = noffset;
} }
rcu_read_lock();
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock();
return;
}
}
ieee80211_tdls_add_oper_classes(sdata, skb);
/* /*
* with TDLS we can switch channels, and HT-caps are not necessarily * with TDLS we can switch channels, and HT-caps are not necessarily
* the same on all bands. The specification limits the setup to a * the same on all bands. The specification limits the setup to a
@ -346,7 +378,9 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) { if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
ht_cap.ht_supported) {
ieee80211_apply_htcap_overrides(sdata, &ht_cap); ieee80211_apply_htcap_overrides(sdata, &ht_cap);
/* disable SMPS in TDLS initiator */ /* disable SMPS in TDLS initiator */
@ -368,12 +402,63 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
} }
rcu_read_unlock();
if (ht_cap.ht_supported && if (ht_cap.ht_supported &&
(ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
ieee80211_tdls_add_bss_coex_ie(skb); ieee80211_tdls_add_bss_coex_ie(skb);
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* add any custom IEs that go before VHT capabilities */
if (extra_ies_len) {
static const u8 before_vht_cap[] = {
WLAN_EID_SUPP_RATES,
WLAN_EID_COUNTRY,
WLAN_EID_EXT_SUPP_RATES,
WLAN_EID_SUPPORTED_CHANNELS,
WLAN_EID_RSN,
WLAN_EID_EXT_CAPABILITY,
WLAN_EID_QOS_CAPA,
WLAN_EID_FAST_BSS_TRANSITION,
WLAN_EID_TIMEOUT_INTERVAL,
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
WLAN_EID_MULTI_BAND,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_vht_cap,
ARRAY_SIZE(before_vht_cap),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
/* build the VHT-cap similarly to the HT-cap */
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
vht_cap.vht_supported) {
ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
/* the AID is present only when VHT is implemented */
if (action_code == WLAN_TDLS_SETUP_REQUEST)
ieee80211_tdls_add_aid(sdata, skb);
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) {
/* the peer caps are already intersected with our own */
memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap));
/* the AID is present only when VHT is implemented */
ieee80211_tdls_add_aid(sdata, skb);
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
}
rcu_read_unlock();
/* add any remaining IEs */ /* add any remaining IEs */
if (extra_ies_len) { if (extra_ies_len) {
noffset = extra_ies_len; noffset = extra_ies_len;
@ -381,7 +466,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
memcpy(pos, extra_ies + offset, noffset - offset); memcpy(pos, extra_ies + offset, noffset - offset);
} }
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
} }
static void static void
@ -394,6 +478,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset; size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta; struct sta_info *sta, *ap_sta;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u8 *pos; u8 *pos;
rcu_read_lock(); rcu_read_lock();
@ -453,6 +538,21 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
} }
} }
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* only include VHT-operation if not on the 2.4GHz band */
if (band != IEEE80211_BAND_2GHZ && !ap_sta->sta.vht_cap.vht_supported &&
sta->sta.vht_cap.vht_supported) {
struct ieee80211_chanctx_conf *chanctx_conf =
rcu_dereference(sdata->vif.chanctx_conf);
if (!WARN_ON(!chanctx_conf)) {
pos = skb_put(skb, 2 +
sizeof(struct ieee80211_vht_operation));
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
&chanctx_conf->def);
}
}
rcu_read_unlock(); rcu_read_unlock();
/* add any remaining IEs */ /* add any remaining IEs */
@ -461,8 +561,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, noffset - offset); pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset); memcpy(pos, extra_ies + offset, noffset - offset);
} }
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
} }
static void static void
@ -708,8 +806,12 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
26 + /* max(WMM-info, WMM-param) */ 26 + /* max(WMM-info, WMM-param) */
2 + max(sizeof(struct ieee80211_ht_cap), 2 + max(sizeof(struct ieee80211_ht_cap),
sizeof(struct ieee80211_ht_operation)) + sizeof(struct ieee80211_ht_operation)) +
2 + max(sizeof(struct ieee80211_vht_cap),
sizeof(struct ieee80211_vht_operation)) +
50 + /* supported channels */ 50 + /* supported channels */
3 + /* 40/20 BSS coex */ 3 + /* 40/20 BSS coex */
4 + /* AID */
4 + /* oper classes */
extra_ies_len + extra_ies_len +
sizeof(struct ieee80211_tdls_lnkie)); sizeof(struct ieee80211_tdls_lnkie));
if (!skb) if (!skb)
@ -907,7 +1009,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
ret = -EBUSY; ret = -EBUSY;
goto exit; goto out_unlock;
} }
/* /*
@ -922,27 +1024,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
if (!sta_info_get(sdata, peer)) { if (!sta_info_get(sdata, peer)) {
rcu_read_unlock(); rcu_read_unlock();
ret = -ENOLINK; ret = -ENOLINK;
goto exit; goto out_unlock;
} }
rcu_read_unlock(); rcu_read_unlock();
} }
ieee80211_flush_queues(local, sdata, false); ieee80211_flush_queues(local, sdata, false);
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
mutex_unlock(&local->mtx);
/* we cannot take the mutex while preparing the setup packet */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code, dialog_token, status_code,
peer_capability, initiator, peer_capability, initiator,
extra_ies, extra_ies_len, 0, extra_ies, extra_ies_len, 0,
NULL); NULL);
if (ret < 0) if (ret < 0) {
goto exit; mutex_lock(&local->mtx);
eth_zero_addr(sdata->u.mgd.tdls_peer);
mutex_unlock(&local->mtx);
return ret;
}
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
ieee80211_queue_delayed_work(&sdata->local->hw, ieee80211_queue_delayed_work(&sdata->local->hw,
&sdata->u.mgd.tdls_peer_del_work, &sdata->u.mgd.tdls_peer_del_work,
TDLS_PEER_SETUP_TIMEOUT); TDLS_PEER_SETUP_TIMEOUT);
return 0;
exit: out_unlock:
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
return ret; return ret;
} }

View file

@ -1256,28 +1256,28 @@ TRACE_EVENT(drv_set_rekey_data,
LOCAL_PR_ARG, VIF_PR_ARG) LOCAL_PR_ARG, VIF_PR_ARG)
); );
TRACE_EVENT(drv_rssi_callback, TRACE_EVENT(drv_event_callback,
TP_PROTO(struct ieee80211_local *local, TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,
enum ieee80211_rssi_event rssi_event), const struct ieee80211_event *_event),
TP_ARGS(local, sdata, rssi_event), TP_ARGS(local, sdata, _event),
TP_STRUCT__entry( TP_STRUCT__entry(
LOCAL_ENTRY LOCAL_ENTRY
VIF_ENTRY VIF_ENTRY
__field(u32, rssi_event) __field(u32, type)
), ),
TP_fast_assign( TP_fast_assign(
LOCAL_ASSIGN; LOCAL_ASSIGN;
VIF_ASSIGN; VIF_ASSIGN;
__entry->rssi_event = rssi_event; __entry->type = _event->type;
), ),
TP_printk( TP_printk(
LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", LOCAL_PR_FMT VIF_PR_FMT " event:%d",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event LOCAL_PR_ARG, VIF_PR_ARG, __entry->type
) )
); );

View file

@ -20,7 +20,6 @@
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/time.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h> #include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
@ -595,23 +594,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
else if (!is_multicast_ether_addr(hdr->addr1) && else if (!is_multicast_ether_addr(hdr->addr1) &&
(key = rcu_dereference(tx->sdata->default_unicast_key))) (key = rcu_dereference(tx->sdata->default_unicast_key)))
tx->key = key; tx->key = key;
else if (info->flags & IEEE80211_TX_CTL_INJECTED) else
tx->key = NULL; tx->key = NULL;
else if (!tx->sdata->drop_unencrypted)
tx->key = NULL;
else if (tx->skb->protocol == tx->sdata->control_port_protocol)
tx->key = NULL;
else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
!(ieee80211_is_action(hdr->frame_control) &&
tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
tx->key = NULL;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
!ieee80211_is_robust_mgmt_frame(tx->skb))
tx->key = NULL;
else {
I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
return TX_DROP;
}
if (tx->key) { if (tx->key) {
bool skip_hw = false; bool skip_hw = false;
@ -1137,11 +1121,13 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
/* /*
* initialises @tx * initialises @tx
* pass %NULL for the station if unknown, a valid pointer if known
* or an ERR_PTR() if the station is known not to exist
*/ */
static ieee80211_tx_result static ieee80211_tx_result
ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_data *tx, struct ieee80211_tx_data *tx,
struct sk_buff *skb) struct sta_info *sta, struct sk_buff *skb)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
@ -1164,17 +1150,22 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
hdr = (struct ieee80211_hdr *) skb->data; hdr = (struct ieee80211_hdr *) skb->data;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { if (likely(sta)) {
tx->sta = rcu_dereference(sdata->u.vlan.sta); if (!IS_ERR(sta))
if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) tx->sta = sta;
return TX_DROP; } else {
} else if (info->flags & (IEEE80211_TX_CTL_INJECTED | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
IEEE80211_TX_INTFL_NL80211_FRAME_TX) || tx->sta = rcu_dereference(sdata->u.vlan.sta);
tx->sdata->control_port_protocol == tx->skb->protocol) { if (!tx->sta && sdata->wdev.use_4addr)
tx->sta = sta_info_get_bss(sdata, hdr->addr1); return TX_DROP;
} else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
IEEE80211_TX_CTL_INJECTED) ||
tx->sdata->control_port_protocol == tx->skb->protocol) {
tx->sta = sta_info_get_bss(sdata, hdr->addr1);
}
if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
tx->sta = sta_info_get(sdata, hdr->addr1);
} }
if (!tx->sta)
tx->sta = sta_info_get(sdata, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
!ieee80211_is_qos_nullfunc(hdr->frame_control) && !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
@ -1422,8 +1413,9 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_data tx; struct ieee80211_tx_data tx;
struct sk_buff *skb2;
if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
return false; return false;
info->band = band; info->band = band;
@ -1440,6 +1432,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
*sta = NULL; *sta = NULL;
} }
/* this function isn't suitable for fragmented data frames */
skb2 = __skb_dequeue(&tx.skbs);
if (WARN_ON(skb2 != skb || !skb_queue_empty(&tx.skbs))) {
ieee80211_free_txskb(hw, skb2);
ieee80211_purge_tx_queue(hw, &tx.skbs);
return false;
}
return true; return true;
} }
EXPORT_SYMBOL(ieee80211_tx_prepare_skb); EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
@ -1448,7 +1448,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
* Returns false if the frame couldn't be transmitted but was queued instead. * Returns false if the frame couldn't be transmitted but was queued instead.
*/ */
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool txpending) struct sta_info *sta, struct sk_buff *skb,
bool txpending)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx; struct ieee80211_tx_data tx;
@ -1464,7 +1465,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
/* initialises tx */ /* initialises tx */
led_len = skb->len; led_len = skb->len;
res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
if (unlikely(res_prepare == TX_DROP)) { if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb); ieee80211_free_txskb(&local->hw, skb);
@ -1520,7 +1521,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0; return 0;
} }
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sk_buff *skb)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@ -1555,7 +1557,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
} }
ieee80211_set_qos_hdr(sdata, skb); ieee80211_set_qos_hdr(sdata, skb);
ieee80211_tx(sdata, skb, false); ieee80211_tx(sdata, sta, skb, false);
} }
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@ -1776,7 +1778,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
goto fail_rcu; goto fail_rcu;
info->band = chandef->chan->band; info->band = chandef->chan->band;
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock(); rcu_read_unlock();
return NETDEV_TX_OK; return NETDEV_TX_OK;
@ -1788,21 +1790,89 @@ fail:
return NETDEV_TX_OK; /* meaning, we dealt with the skb */ return NETDEV_TX_OK; /* meaning, we dealt with the skb */
} }
/* static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
* Measure Tx frame arrival time for Tx latency statistics calculation
* A single Tx frame latency should be measured from when it is entering the
* Kernel until we receive Tx complete confirmation indication and the skb is
* freed.
*/
static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
struct sk_buff *skb)
{ {
struct ieee80211_tx_latency_bin_ranges *tx_latency; u16 ethertype = (skb->data[12] << 8) | skb->data[13];
tx_latency = rcu_dereference(local->tx_latency); return ethertype == ETH_P_TDLS &&
if (!tx_latency) skb->len > 14 &&
return; skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
skb->tstamp = ktime_get(); }
static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct sta_info **sta_out)
{
struct sta_info *sta;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta);
if (sta) {
*sta_out = sta;
return 0;
} else if (sdata->wdev.use_4addr) {
return -ENOLINK;
}
/* fall through */
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_ADHOC:
if (is_multicast_ether_addr(skb->data)) {
*sta_out = ERR_PTR(-ENOENT);
return 0;
}
sta = sta_info_get_bss(sdata, skb->data);
break;
case NL80211_IFTYPE_WDS:
sta = sta_info_get(sdata, sdata->u.wds.remote_addr);
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
/* determined much later */
*sta_out = NULL;
return 0;
#endif
case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
sta = sta_info_get(sdata, skb->data);
if (sta) {
bool tdls_peer, tdls_auth;
tdls_peer = test_sta_flag(sta,
WLAN_STA_TDLS_PEER);
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
if (tdls_peer && tdls_auth) {
*sta_out = sta;
return 0;
}
/*
* TDLS link during setup - throw out frames to
* peer. Allow TDLS-setup frames to unauthorized
* peers for the special case of a link teardown
* after a TDLS sta is removed due to being
* unreachable.
*/
if (tdls_peer && !tdls_auth &&
!ieee80211_is_tdls_setup(skb))
return -EINVAL;
}
}
sta = sta_info_get(sdata, sdata->u.mgd.bssid);
if (!sta)
return -ENOLINK;
break;
default:
return -EINVAL;
}
*sta_out = sta ?: ERR_PTR(-ENOENT);
return 0;
} }
/** /**
@ -1824,7 +1894,8 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
* Returns: the (possibly reallocated) skb or an ERR_PTR() code * Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/ */
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u32 info_flags) struct sk_buff *skb, u32 info_flags,
struct sta_info *sta)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
@ -1837,9 +1908,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
const u8 *encaps_data; const u8 *encaps_data;
int encaps_len, skip_header_bytes; int encaps_len, skip_header_bytes;
int nh_pos, h_pos; int nh_pos, h_pos;
struct sta_info *sta = NULL; bool wme_sta = false, authorized = false;
bool wme_sta = false, authorized = false, tdls_auth = false; bool tdls_peer;
bool tdls_peer = false, tdls_setup_frame = false;
bool multicast; bool multicast;
u16 info_id = 0; u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
@ -1847,6 +1917,9 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band; enum ieee80211_band band;
int ret; int ret;
if (IS_ERR(sta))
sta = NULL;
/* convert Ethernet header to proper 802.11 header (based on /* convert Ethernet header to proper 802.11 header (based on
* operation mode) */ * operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13]; ethertype = (skb->data[12] << 8) | skb->data[13];
@ -1854,8 +1927,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta); if (sdata->wdev.use_4addr) {
if (sta) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */ /* RA TA DA SA */
memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
@ -1874,7 +1946,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free; goto free;
} }
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
if (sta) if (sdata->wdev.use_4addr)
break; break;
/* fall through */ /* fall through */
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
@ -1978,38 +2050,10 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
break; break;
#endif #endif
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { /* we already did checks when looking up the RA STA */
sta = sta_info_get(sdata, skb->data); tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
if (sta) {
authorized = test_sta_flag(sta,
WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme;
tdls_peer = test_sta_flag(sta,
WLAN_STA_TDLS_PEER);
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
}
if (tdls_peer) if (tdls_peer) {
tdls_setup_frame =
ethertype == ETH_P_TDLS &&
skb->len > 14 &&
skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
}
/*
* TDLS link during setup - throw out frames to peer. We allow
* TDLS-setup frames to unauthorized peers for the special case
* of a link teardown after a TDLS sta is removed due to being
* unreachable.
*/
if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
ret = -EINVAL;
goto free;
}
/* send direct packets to authorized TDLS peers */
if (tdls_peer && tdls_auth) {
/* DA SA BSSID */ /* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@ -2071,26 +2115,19 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free; goto free;
} }
/*
* There's no need to try to look up the destination
* if it is a multicast address (which can only happen
* in AP mode)
*/
multicast = is_multicast_ether_addr(hdr.addr1); multicast = is_multicast_ether_addr(hdr.addr1);
if (!multicast) {
sta = sta_info_get(sdata, hdr.addr1); /* sta is always NULL for mesh */
if (sta) { if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = sta->sta.wme; wme_sta = sta->sta.wme;
} } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
/* For mesh, the use of the QoS header is mandatory */
wme_sta = true;
} }
/* For mesh, the use of the QoS header is mandatory */ /* receiver does QoS (which also means we do) use it */
if (ieee80211_vif_is_mesh(&sdata->vif)) if (wme_sta) {
wme_sta = true;
/* receiver and we are QoS enabled, use a QoS type frame */
if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2; hdrlen += 2;
} }
@ -2260,7 +2297,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
u32 info_flags) u32 info_flags)
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct sta_info *sta;
if (unlikely(skb->len < ETH_HLEN)) { if (unlikely(skb->len < ETH_HLEN)) {
kfree_skb(skb); kfree_skb(skb);
@ -2269,10 +2306,12 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
rcu_read_lock(); rcu_read_lock();
/* Measure frame arrival for Tx latency statistics calculation */ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
ieee80211_tx_latency_start_msrmnt(local, skb); kfree_skb(skb);
goto out;
}
skb = ieee80211_build_hdr(sdata, skb, info_flags); skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
if (IS_ERR(skb)) if (IS_ERR(skb))
goto out; goto out;
@ -2280,7 +2319,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
dev->stats.tx_bytes += skb->len; dev->stats.tx_bytes += skb->len;
dev->trans_start = jiffies; dev->trans_start = jiffies;
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, sta, skb);
out: out:
rcu_read_unlock(); rcu_read_unlock();
} }
@ -2308,10 +2347,17 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
.local = sdata->local, .local = sdata->local,
.sdata = sdata, .sdata = sdata,
}; };
struct sta_info *sta;
rcu_read_lock(); rcu_read_lock();
skb = ieee80211_build_hdr(sdata, skb, info_flags); if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
kfree_skb(skb);
skb = ERR_PTR(-EINVAL);
goto out;
}
skb = ieee80211_build_hdr(sdata, skb, info_flags, sta);
if (IS_ERR(skb)) if (IS_ERR(skb))
goto out; goto out;
@ -2369,7 +2415,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
return true; return true;
} }
info->band = chanctx_conf->def.chan->band; info->band = chanctx_conf->def.chan->band;
result = ieee80211_tx(sdata, skb, true); result = ieee80211_tx(sdata, NULL, skb, true);
} else { } else {
struct sk_buff_head skbs; struct sk_buff_head skbs;
@ -3107,7 +3153,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
if (sdata->vif.type == NL80211_IFTYPE_AP) if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb)) if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
break; break;
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} }
@ -3239,6 +3285,6 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
*/ */
local_bh_disable(); local_bh_disable();
IEEE80211_SKB_CB(skb)->band = band; IEEE80211_SKB_CB(skb)->band = band;
ieee80211_xmit(sdata, skb); ieee80211_xmit(sdata, NULL, skb);
local_bh_enable(); local_bh_enable();
} }

View file

@ -625,13 +625,14 @@ void ieee80211_wake_vif_queues(struct ieee80211_local *local,
reason, true); reason, true);
} }
static void __iterate_active_interfaces(struct ieee80211_local *local, static void __iterate_interfaces(struct ieee80211_local *local,
u32 iter_flags, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data) void *data)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
list_for_each_entry_rcu(sdata, &local->interfaces, list) { list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
@ -645,9 +646,9 @@ static void __iterate_active_interfaces(struct ieee80211_local *local,
break; break;
} }
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
continue; continue;
if (ieee80211_sdata_running(sdata)) if (ieee80211_sdata_running(sdata) || !active_only)
iterator(data, sdata->vif.addr, iterator(data, sdata->vif.addr,
&sdata->vif); &sdata->vif);
} }
@ -656,12 +657,12 @@ static void __iterate_active_interfaces(struct ieee80211_local *local,
lockdep_is_held(&local->iflist_mtx) || lockdep_is_held(&local->iflist_mtx) ||
lockdep_rtnl_is_held()); lockdep_rtnl_is_held());
if (sdata && if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER)) sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif); iterator(data, sdata->vif.addr, &sdata->vif);
} }
void ieee80211_iterate_active_interfaces( void ieee80211_iterate_interfaces(
struct ieee80211_hw *hw, u32 iter_flags, struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
@ -670,10 +671,10 @@ void ieee80211_iterate_active_interfaces(
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
mutex_lock(&local->iflist_mtx); mutex_lock(&local->iflist_mtx);
__iterate_active_interfaces(local, iter_flags, iterator, data); __iterate_interfaces(local, iter_flags, iterator, data);
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
} }
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces);
void ieee80211_iterate_active_interfaces_atomic( void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_hw *hw, u32 iter_flags, struct ieee80211_hw *hw, u32 iter_flags,
@ -684,7 +685,8 @@ void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
rcu_read_lock(); rcu_read_lock();
__iterate_active_interfaces(local, iter_flags, iterator, data); __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
iterator, data);
rcu_read_unlock(); rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
@ -699,7 +701,8 @@ void ieee80211_iterate_active_interfaces_rtnl(
ASSERT_RTNL(); ASSERT_RTNL();
__iterate_active_interfaces(local, iter_flags, iterator, data); __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
iterator, data);
} }
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
@ -742,6 +745,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
} }
EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
if (!ieee80211_sdata_running(sdata) ||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
return NULL;
return &sdata->wdev;
}
EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev);
/* /*
* Nothing should have been stuffed into the workqueue during * Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. Since we can't check each caller * the suspend->resume cycle. Since we can't check each caller
@ -1811,8 +1826,25 @@ int ieee80211_reconfig(struct ieee80211_local *local)
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR && sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata)) ieee80211_sdata_running(sdata)) {
res = drv_add_interface(local, sdata); res = drv_add_interface(local, sdata);
if (WARN_ON(res))
break;
}
}
/* If adding any of the interfaces failed above, roll back and
* report failure.
*/
if (res) {
list_for_each_entry_continue_reverse(sdata, &local->interfaces,
list)
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata))
drv_remove_interface(local, sdata);
ieee80211_handle_reconfig_failure(local);
return res;
} }
/* add channel contexts */ /* add channel contexts */
@ -2344,6 +2376,41 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos + sizeof(struct ieee80211_ht_operation); return pos + sizeof(struct ieee80211_ht_operation);
} }
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef)
{
struct ieee80211_vht_operation *vht_oper;
*pos++ = WLAN_EID_VHT_OPERATION;
*pos++ = sizeof(struct ieee80211_vht_operation);
vht_oper = (struct ieee80211_vht_operation *)pos;
vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(
chandef->center_freq1);
if (chandef->center_freq2)
vht_oper->center_freq_seg2_idx =
ieee80211_frequency_to_channel(chandef->center_freq2);
switch (chandef->width) {
case NL80211_CHAN_WIDTH_160:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
break;
case NL80211_CHAN_WIDTH_80P80:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
break;
case NL80211_CHAN_WIDTH_80:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
break;
default:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
break;
}
/* don't require special VHT peer rates */
vht_oper->basic_mcs_set = cpu_to_le16(0xffff);
return pos + sizeof(struct ieee80211_vht_operation);
}
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
@ -2373,6 +2440,39 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
cfg80211_chandef_create(chandef, control_chan, channel_type); cfg80211_chandef_create(chandef, control_chan, channel_type);
} }
void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_vht_operation *oper,
struct cfg80211_chan_def *chandef)
{
if (!oper)
return;
chandef->chan = control_chan;
switch (oper->chan_width) {
case IEEE80211_VHT_CHANWIDTH_USE_HT:
break;
case IEEE80211_VHT_CHANWIDTH_80MHZ:
chandef->width = NL80211_CHAN_WIDTH_80;
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
chandef->width = NL80211_CHAN_WIDTH_160;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
chandef->width = NL80211_CHAN_WIDTH_80P80;
break;
default:
break;
}
chandef->center_freq1 =
ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
control_chan->band);
chandef->center_freq2 =
ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
control_chan->band);
}
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband, const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates) const u8 *srates, int srates_len, u32 *rates)

View file

@ -129,10 +129,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
if (!vht_cap_ie || !sband->vht_cap.vht_supported) if (!vht_cap_ie || !sband->vht_cap.vht_supported)
return; return;
/* don't support VHT for TDLS peers for now */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
return;
/* /*
* A VHT STA must support 40 MHz, but if we verify that here * A VHT STA must support 40 MHz, but if we verify that here
* then we break a few things - some APs (e.g. Netgear R6300v2 * then we break a few things - some APs (e.g. Netgear R6300v2

View file

@ -780,9 +780,8 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_key *key = tx->key; struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
int hdrlen; int hdrlen;
u8 *pos; u8 *pos, iv_len = key->conf.iv_len;
if (info->control.hw_key && if (info->control.hw_key &&
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
@ -790,14 +789,14 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
return TX_CONTINUE; return TX_CONTINUE;
} }
if (unlikely(skb_headroom(skb) < cs->hdr_len && if (unlikely(skb_headroom(skb) < iv_len &&
pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC)))
return TX_DROP; return TX_DROP;
hdrlen = ieee80211_hdrlen(hdr->frame_control); hdrlen = ieee80211_hdrlen(hdr->frame_control);
pos = skb_push(skb, cs->hdr_len); pos = skb_push(skb, iv_len);
memmove(pos, pos + cs->hdr_len, hdrlen); memmove(pos, pos + iv_len, hdrlen);
return TX_CONTINUE; return TX_CONTINUE;
} }
@ -1217,7 +1216,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
if (!info->control.hw_key) if (!info->control.hw_key)
return TX_DROP; return TX_DROP;
if (tx->key->sta->cipher_scheme) { if (tx->key->flags & KEY_FLAG_CIPHER_SCHEME) {
res = ieee80211_crypto_cs_encrypt(tx, skb); res = ieee80211_crypto_cs_encrypt(tx, skb);
if (res != TX_CONTINUE) if (res != TX_CONTINUE)
return res; return res;

View file

@ -178,10 +178,18 @@ config CFG80211_WEXT
bool "cfg80211 wireless extensions compatibility" bool "cfg80211 wireless extensions compatibility"
depends on CFG80211 depends on CFG80211
select WEXT_CORE select WEXT_CORE
default y if CFG80211_WEXT_EXPORT
help help
Enable this option if you need old userspace for wireless Enable this option if you need old userspace for wireless
extensions with cfg80211-based drivers. extensions with cfg80211-based drivers.
config CFG80211_WEXT_EXPORT
bool
depends on CFG80211
help
Drivers should select this option if they require cfg80211's
wext compatibility symbols to be exported.
config LIB80211 config LIB80211
tristate tristate
default n default n

View file

@ -30,7 +30,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
return; return;
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
if (WARN_ON(!bss)) if (WARN_ON(!bss))
return; return;

View file

@ -229,7 +229,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
return -EALREADY; return -EALREADY;
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY);
if (!req.bss) if (!req.bss)
return -ENOENT; return -ENOENT;
@ -296,7 +297,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
rdev->wiphy.vht_capa_mod_mask); rdev->wiphy.vht_capa_mod_mask);
req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY);
if (!req->bss) if (!req->bss)
return -ENOENT; return -ENOENT;

View file

@ -399,6 +399,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
@ -1098,8 +1099,6 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
return -ENOBUFS; return -ENOBUFS;
/* TODO: send wowlan net detect */
nla_nest_end(msg, nl_wowlan); nla_nest_end(msg, nl_wowlan);
return 0; return 0;
@ -2668,7 +2667,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
wdev = rdev_add_virtual_intf(rdev, wdev = rdev_add_virtual_intf(rdev,
nla_data(info->attrs[NL80211_ATTR_IFNAME]), nla_data(info->attrs[NL80211_ATTR_IFNAME]),
type, err ? NULL : &flags, &params); NET_NAME_USER, type, err ? NULL : &flags,
&params);
if (WARN_ON(!wdev)) { if (WARN_ON(!wdev)) {
nlmsg_free(msg); nlmsg_free(msg);
return -EPROTO; return -EPROTO;
@ -4968,7 +4968,10 @@ static int parse_reg_rule(struct nlattr *tb[],
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{ {
char *data = NULL; char *data = NULL;
bool is_indoor;
enum nl80211_user_reg_hint_type user_reg_hint_type; enum nl80211_user_reg_hint_type user_reg_hint_type;
u32 owner_nlportid;
/* /*
* You should only get this when cfg80211 hasn't yet initialized * You should only get this when cfg80211 hasn't yet initialized
@ -4994,7 +4997,15 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
return regulatory_hint_user(data, user_reg_hint_type); return regulatory_hint_user(data, user_reg_hint_type);
case NL80211_USER_REG_HINT_INDOOR: case NL80211_USER_REG_HINT_INDOOR:
return regulatory_hint_indoor_user(); if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
owner_nlportid = info->snd_portid;
is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
} else {
owner_nlportid = 0;
is_indoor = true;
}
return regulatory_hint_indoor(is_indoor, owner_nlportid);
default: default:
return -EINVAL; return -EINVAL;
} }
@ -5275,7 +5286,7 @@ do { \
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
0, 65535, mask, 0, 65535, mask,
NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff, FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff,
mask, NL80211_MESHCONF_PLINK_TIMEOUT, mask, NL80211_MESHCONF_PLINK_TIMEOUT,
nla_get_u32); nla_get_u32);
if (mask_out) if (mask_out)
@ -7275,8 +7286,18 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
break; break;
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_40: case NL80211_CHAN_WIDTH_40:
if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS) if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
break; return -EINVAL;
break;
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
return -EINVAL;
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_VHT_IBSS))
return -EINVAL;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -7389,8 +7410,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
static struct sk_buff * static struct sk_buff *
__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 portid, u32 seq, struct wireless_dev *wdev, int approxlen,
enum nl80211_commands cmd, u32 portid, u32 seq, enum nl80211_commands cmd,
enum nl80211_attrs attr, enum nl80211_attrs attr,
const struct nl80211_vendor_cmd_info *info, const struct nl80211_vendor_cmd_info *info,
gfp_t gfp) gfp_t gfp)
@ -7421,6 +7442,16 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
goto nla_put_failure; goto nla_put_failure;
} }
if (wdev) {
if (nla_put_u64(skb, NL80211_ATTR_WDEV,
wdev_id(wdev)))
goto nla_put_failure;
if (wdev->netdev &&
nla_put_u32(skb, NL80211_ATTR_IFINDEX,
wdev->netdev->ifindex))
goto nla_put_failure;
}
data = nla_nest_start(skb, attr); data = nla_nest_start(skb, attr);
((void **)skb->cb)[0] = rdev; ((void **)skb->cb)[0] = rdev;
@ -7435,6 +7466,7 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
} }
struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
struct wireless_dev *wdev,
enum nl80211_commands cmd, enum nl80211_commands cmd,
enum nl80211_attrs attr, enum nl80211_attrs attr,
int vendor_event_idx, int vendor_event_idx,
@ -7460,7 +7492,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
return NULL; return NULL;
} }
return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0,
cmd, attr, info, gfp); cmd, attr, info, gfp);
} }
EXPORT_SYMBOL(__cfg80211_alloc_event_skb); EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
@ -8808,6 +8840,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval)) if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
return -ENOBUFS; return -ENOBUFS;
if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
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;
@ -9094,6 +9129,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan; const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
int err, i; int err, i;
bool prev_enabled = rdev->wiphy.wowlan_config; bool prev_enabled = rdev->wiphy.wowlan_config;
bool regular = false;
if (!wowlan) if (!wowlan)
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -9121,12 +9157,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
return -EINVAL; return -EINVAL;
new_triggers.disconnect = true; new_triggers.disconnect = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
return -EINVAL; return -EINVAL;
new_triggers.magic_pkt = true; new_triggers.magic_pkt = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
@ -9136,24 +9174,28 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
return -EINVAL; return -EINVAL;
new_triggers.gtk_rekey_failure = true; new_triggers.gtk_rekey_failure = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
return -EINVAL; return -EINVAL;
new_triggers.eap_identity_req = true; new_triggers.eap_identity_req = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
return -EINVAL; return -EINVAL;
new_triggers.four_way_handshake = true; new_triggers.four_way_handshake = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
return -EINVAL; return -EINVAL;
new_triggers.rfkill_release = true; new_triggers.rfkill_release = true;
regular = true;
} }
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
@ -9162,6 +9204,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
int rem, pat_len, mask_len, pkt_offset; int rem, pat_len, mask_len, pkt_offset;
struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
regular = true;
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem) rem)
n_patterns++; n_patterns++;
@ -9223,6 +9267,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
} }
if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
regular = true;
err = nl80211_parse_wowlan_tcp( err = nl80211_parse_wowlan_tcp(
rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
&new_triggers); &new_triggers);
@ -9231,6 +9276,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
} }
if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
regular = true;
err = nl80211_parse_wowlan_nd( err = nl80211_parse_wowlan_nd(
rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT], rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
&new_triggers); &new_triggers);
@ -9238,6 +9284,17 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
goto error; goto error;
} }
/* The 'any' trigger means the device continues operating more or less
* as in its normal operation mode and wakes up the host on most of the
* normal interrupts (like packet RX, ...)
* It therefore makes little sense to combine with the more constrained
* wakeup trigger modes.
*/
if (new_triggers.any && regular) {
err = -EINVAL;
goto error;
}
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
if (!ntrig) { if (!ntrig) {
err = -ENOMEM; err = -ENOMEM;
@ -9906,7 +9963,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
if (WARN_ON(!rdev->cur_cmd_info)) if (WARN_ON(!rdev->cur_cmd_info))
return NULL; return NULL;
return __cfg80211_alloc_vendor_skb(rdev, approxlen, return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen,
rdev->cur_cmd_info->snd_portid, rdev->cur_cmd_info->snd_portid,
rdev->cur_cmd_info->snd_seq, rdev->cur_cmd_info->snd_seq,
cmd, attr, NULL, GFP_KERNEL); cmd, attr, NULL, GFP_KERNEL);
@ -12775,6 +12832,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
rcu_read_unlock(); rcu_read_unlock();
/*
* It is possible that the user space process that is controlling the
* indoor setting disappeared, so notify the regulatory core.
*/
regulatory_netlink_notify(notify->portid);
return NOTIFY_OK; return NOTIFY_OK;
} }

View file

@ -35,13 +35,14 @@ static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev,
static inline struct wireless_dev static inline struct wireless_dev
*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, *rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
unsigned char name_assign_type,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
struct vif_params *params) struct vif_params *params)
{ {
struct wireless_dev *ret; struct wireless_dev *ret;
trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags, ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type,
params); type, flags, params);
trace_rdev_return_wdev(&rdev->wiphy, ret); trace_rdev_return_wdev(&rdev->wiphy, ret);
return ret; return ret;
} }

View file

@ -82,17 +82,12 @@
* be intersected with the current one. * be intersected with the current one.
* @REG_REQ_ALREADY_SET: the regulatory request will not change the current * @REG_REQ_ALREADY_SET: the regulatory request will not change the current
* regulatory settings, and no further processing is required. * regulatory settings, and no further processing is required.
* @REG_REQ_USER_HINT_HANDLED: a non alpha2 user hint was handled and no
* further processing is required, i.e., not need to update last_request
* etc. This should be used for user hints that do not provide an alpha2
* but some other type of regulatory hint, i.e., indoor operation.
*/ */
enum reg_request_treatment { enum reg_request_treatment {
REG_REQ_OK, REG_REQ_OK,
REG_REQ_IGNORE, REG_REQ_IGNORE,
REG_REQ_INTERSECT, REG_REQ_INTERSECT,
REG_REQ_ALREADY_SET, REG_REQ_ALREADY_SET,
REG_REQ_USER_HINT_HANDLED,
}; };
static struct regulatory_request core_request_world = { static struct regulatory_request core_request_world = {
@ -133,9 +128,12 @@ static int reg_num_devs_support_basehint;
* State variable indicating if the platform on which the devices * State variable indicating if the platform on which the devices
* are attached is operating in an indoor environment. The state variable * are attached is operating in an indoor environment. The state variable
* is relevant for all registered devices. * is relevant for all registered devices.
* (protected by RTNL)
*/ */
static bool reg_is_indoor; static bool reg_is_indoor;
static spinlock_t reg_indoor_lock;
/* Used to track the userspace process controlling the indoor setting */
static u32 reg_is_indoor_portid;
static const struct ieee80211_regdomain *get_cfg80211_regdom(void) static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{ {
@ -554,6 +552,9 @@ reg_call_crda(struct regulatory_request *request)
{ {
if (call_crda(request->alpha2)) if (call_crda(request->alpha2))
return REG_REQ_IGNORE; return REG_REQ_IGNORE;
queue_delayed_work(system_power_efficient_wq,
&reg_timeout, msecs_to_jiffies(3142));
return REG_REQ_OK; return REG_REQ_OK;
} }
@ -1248,13 +1249,6 @@ static bool reg_request_cell_base(struct regulatory_request *request)
return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
} }
static bool reg_request_indoor(struct regulatory_request *request)
{
if (request->initiator != NL80211_REGDOM_SET_BY_USER)
return false;
return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR;
}
bool reg_last_request_cell_base(void) bool reg_last_request_cell_base(void)
{ {
return reg_request_cell_base(get_last_request()); return reg_request_cell_base(get_last_request());
@ -1800,8 +1794,7 @@ static void reg_set_request_processed(void)
need_more_processing = true; need_more_processing = true;
spin_unlock(&reg_requests_lock); spin_unlock(&reg_requests_lock);
if (lr->initiator == NL80211_REGDOM_SET_BY_USER) cancel_delayed_work(&reg_timeout);
cancel_delayed_work(&reg_timeout);
if (need_more_processing) if (need_more_processing)
schedule_work(&reg_work); schedule_work(&reg_work);
@ -1833,11 +1826,6 @@ __reg_process_hint_user(struct regulatory_request *user_request)
{ {
struct regulatory_request *lr = get_last_request(); struct regulatory_request *lr = get_last_request();
if (reg_request_indoor(user_request)) {
reg_is_indoor = true;
return REG_REQ_USER_HINT_HANDLED;
}
if (reg_request_cell_base(user_request)) if (reg_request_cell_base(user_request))
return reg_ignore_cell_hint(user_request); return reg_ignore_cell_hint(user_request);
@ -1885,8 +1873,7 @@ reg_process_hint_user(struct regulatory_request *user_request)
treatment = __reg_process_hint_user(user_request); treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE || if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET || treatment == REG_REQ_ALREADY_SET) {
treatment == REG_REQ_USER_HINT_HANDLED) {
reg_free_request(user_request); reg_free_request(user_request);
return treatment; return treatment;
} }
@ -1947,7 +1934,6 @@ reg_process_hint_driver(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
case REG_REQ_USER_HINT_HANDLED:
reg_free_request(driver_request); reg_free_request(driver_request);
return treatment; return treatment;
case REG_REQ_INTERSECT: case REG_REQ_INTERSECT:
@ -2047,7 +2033,6 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
case REG_REQ_OK: case REG_REQ_OK:
break; break;
case REG_REQ_IGNORE: case REG_REQ_IGNORE:
case REG_REQ_USER_HINT_HANDLED:
/* fall through */ /* fall through */
case REG_REQ_ALREADY_SET: case REG_REQ_ALREADY_SET:
reg_free_request(country_ie_request); reg_free_request(country_ie_request);
@ -2086,11 +2071,8 @@ static void reg_process_hint(struct regulatory_request *reg_request)
case NL80211_REGDOM_SET_BY_USER: case NL80211_REGDOM_SET_BY_USER:
treatment = reg_process_hint_user(reg_request); treatment = reg_process_hint_user(reg_request);
if (treatment == REG_REQ_IGNORE || if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET || treatment == REG_REQ_ALREADY_SET)
treatment == REG_REQ_USER_HINT_HANDLED)
return; return;
queue_delayed_work(system_power_efficient_wq,
&reg_timeout, msecs_to_jiffies(3142));
return; return;
case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_DRIVER:
if (!wiphy) if (!wiphy)
@ -2177,6 +2159,13 @@ static void reg_process_pending_hints(void)
} }
reg_process_hint(reg_request); reg_process_hint(reg_request);
lr = get_last_request();
spin_lock(&reg_requests_lock);
if (!list_empty(&reg_requests_list) && lr && lr->processed)
schedule_work(&reg_work);
spin_unlock(&reg_requests_lock);
} }
/* Processes beacon hints -- this has nothing to do with country IEs */ /* Processes beacon hints -- this has nothing to do with country IEs */
@ -2309,22 +2298,50 @@ int regulatory_hint_user(const char *alpha2,
return 0; return 0;
} }
int regulatory_hint_indoor_user(void) int regulatory_hint_indoor(bool is_indoor, u32 portid)
{ {
struct regulatory_request *request; spin_lock(&reg_indoor_lock);
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); /* It is possible that more than one user space process is trying to
if (!request) * configure the indoor setting. To handle such cases, clear the indoor
return -ENOMEM; * setting in case that some process does not think that the device
* is operating in an indoor environment. In addition, if a user space
* process indicates that it is controlling the indoor setting, save its
* portid, i.e., make it the owner.
*/
reg_is_indoor = is_indoor;
if (reg_is_indoor) {
if (!reg_is_indoor_portid)
reg_is_indoor_portid = portid;
} else {
reg_is_indoor_portid = 0;
}
request->wiphy_idx = WIPHY_IDX_INVALID; spin_unlock(&reg_indoor_lock);
request->initiator = NL80211_REGDOM_SET_BY_USER;
request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR; if (!is_indoor)
queue_regulatory_request(request); reg_check_channels();
return 0; return 0;
} }
void regulatory_netlink_notify(u32 portid)
{
spin_lock(&reg_indoor_lock);
if (reg_is_indoor_portid != portid) {
spin_unlock(&reg_indoor_lock);
return;
}
reg_is_indoor = false;
reg_is_indoor_portid = 0;
spin_unlock(&reg_indoor_lock);
reg_check_channels();
}
/* Driver hints */ /* Driver hints */
int regulatory_hint(struct wiphy *wiphy, const char *alpha2) int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
{ {
@ -2486,13 +2503,22 @@ static void restore_regulatory_settings(bool reset_user)
char alpha2[2]; char alpha2[2];
char world_alpha2[2]; char world_alpha2[2];
struct reg_beacon *reg_beacon, *btmp; struct reg_beacon *reg_beacon, *btmp;
struct regulatory_request *reg_request, *tmp;
LIST_HEAD(tmp_reg_req_list); LIST_HEAD(tmp_reg_req_list);
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
ASSERT_RTNL(); ASSERT_RTNL();
reg_is_indoor = false; /*
* Clear the indoor setting in case that it is not controlled by user
* space, as otherwise there is no guarantee that the device is still
* operating in an indoor environment.
*/
spin_lock(&reg_indoor_lock);
if (reg_is_indoor && !reg_is_indoor_portid) {
reg_is_indoor = false;
reg_check_channels();
}
spin_unlock(&reg_indoor_lock);
reset_regdomains(true, &world_regdom); reset_regdomains(true, &world_regdom);
restore_alpha2(alpha2, reset_user); restore_alpha2(alpha2, reset_user);
@ -2504,11 +2530,7 @@ static void restore_regulatory_settings(bool reset_user)
* settings. * settings.
*/ */
spin_lock(&reg_requests_lock); spin_lock(&reg_requests_lock);
list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) { list_splice_tail_init(&reg_requests_list, &tmp_reg_req_list);
if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER)
continue;
list_move_tail(&reg_request->list, &tmp_reg_req_list);
}
spin_unlock(&reg_requests_lock); spin_unlock(&reg_requests_lock);
/* Clear beacon hints */ /* Clear beacon hints */
@ -3089,6 +3111,7 @@ int __init regulatory_init(void)
spin_lock_init(&reg_requests_lock); spin_lock_init(&reg_requests_lock);
spin_lock_init(&reg_pending_beacons_lock); spin_lock_init(&reg_pending_beacons_lock);
spin_lock_init(&reg_indoor_lock);
reg_regdb_size_check(); reg_regdb_size_check();

View file

@ -25,7 +25,20 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
int regulatory_hint_user(const char *alpha2, int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type); enum nl80211_user_reg_hint_type user_reg_hint_type);
int regulatory_hint_indoor_user(void);
/**
* regulatory_hint_indoor - hint operation in indoor env. or not
* @is_indoor: if true indicates that user space thinks that the
* device is operating in an indoor environment.
* @portid: the netlink port ID on which the hint was given.
*/
int regulatory_hint_indoor(bool is_indoor, u32 portid);
/**
* regulatory_netlink_notify - notify on released netlink socket
* @portid: the netlink socket port ID
*/
void regulatory_netlink_notify(u32 portid);
void wiphy_regulatory_register(struct wiphy *wiphy); void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy); void wiphy_regulatory_deregister(struct wiphy *wiphy);

View file

@ -531,24 +531,78 @@ static int cmp_bss(struct cfg80211_bss *a,
} }
} }
static bool cfg80211_bss_type_match(u16 capability,
enum ieee80211_band band,
enum ieee80211_bss_type bss_type)
{
bool ret = true;
u16 mask, val;
if (bss_type == IEEE80211_BSS_TYPE_ANY)
return ret;
if (band == IEEE80211_BAND_60GHZ) {
mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
switch (bss_type) {
case IEEE80211_BSS_TYPE_ESS:
val = WLAN_CAPABILITY_DMG_TYPE_AP;
break;
case IEEE80211_BSS_TYPE_PBSS:
val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
break;
case IEEE80211_BSS_TYPE_IBSS:
val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
break;
default:
return false;
}
} else {
mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
switch (bss_type) {
case IEEE80211_BSS_TYPE_ESS:
val = WLAN_CAPABILITY_ESS;
break;
case IEEE80211_BSS_TYPE_IBSS:
val = WLAN_CAPABILITY_IBSS;
break;
case IEEE80211_BSS_TYPE_MBSS:
val = 0;
break;
default:
return false;
}
}
ret = ((capability & mask) == val);
return ret;
}
/* Returned bss is reference counted and must be cleaned up appropriately. */ /* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel, struct ieee80211_channel *channel,
const u8 *bssid, const u8 *bssid,
const u8 *ssid, size_t ssid_len, const u8 *ssid, size_t ssid_len,
u16 capa_mask, u16 capa_val) enum ieee80211_bss_type bss_type,
enum ieee80211_privacy privacy)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_internal_bss *bss, *res = NULL; struct cfg80211_internal_bss *bss, *res = NULL;
unsigned long now = jiffies; unsigned long now = jiffies;
int bss_privacy;
trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask, trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
capa_val); privacy);
spin_lock_bh(&rdev->bss_lock); spin_lock_bh(&rdev->bss_lock);
list_for_each_entry(bss, &rdev->bss_list, list) { list_for_each_entry(bss, &rdev->bss_list, list) {
if ((bss->pub.capability & capa_mask) != capa_val) if (!cfg80211_bss_type_match(bss->pub.capability,
bss->pub.channel->band, bss_type))
continue;
bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
(privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
continue; continue;
if (channel && bss->pub.channel != channel) if (channel && bss->pub.channel != channel)
continue; continue;
@ -896,6 +950,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
struct cfg80211_bss_ies *ies; struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_internal_bss tmp = {}, *res;
int bss_type;
bool signal_valid; bool signal_valid;
if (WARN_ON(!wiphy)) if (WARN_ON(!wiphy))
@ -950,8 +1005,15 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
if (!res) if (!res)
return NULL; return NULL;
if (res->pub.capability & WLAN_CAPABILITY_ESS) if (channel->band == IEEE80211_BAND_60GHZ) {
regulatory_hint_found_beacon(wiphy, channel, gfp); bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
} else {
if (res->pub.capability & WLAN_CAPABILITY_ESS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
}
trace_cfg80211_return_bss(&res->pub); trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
@ -973,6 +1035,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
bool signal_valid; bool signal_valid;
size_t ielen = len - offsetof(struct ieee80211_mgmt, size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable); u.probe_resp.variable);
int bss_type;
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable)); offsetof(struct ieee80211_mgmt, u.beacon.variable));
@ -1025,8 +1088,15 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
if (!res) if (!res)
return NULL; return NULL;
if (res->pub.capability & WLAN_CAPABILITY_ESS) if (channel->band == IEEE80211_BAND_60GHZ) {
regulatory_hint_found_beacon(wiphy, channel, gfp); bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
} else {
if (res->pub.capability & WLAN_CAPABILITY_ESS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
}
trace_cfg80211_return_bss(&res->pub); trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */ /* cfg80211_bss_update gives us a referenced result */
@ -1237,17 +1307,17 @@ int cfg80211_wext_siwscan(struct net_device *dev,
kfree(creq); kfree(creq);
return err; return err;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
static void ieee80211_scan_add_ies(struct iw_request_info *info, static char *ieee80211_scan_add_ies(struct iw_request_info *info,
const struct cfg80211_bss_ies *ies, const struct cfg80211_bss_ies *ies,
char **current_ev, char *end_buf) char *current_ev, char *end_buf)
{ {
const u8 *pos, *end, *next; const u8 *pos, *end, *next;
struct iw_event iwe; struct iw_event iwe;
if (!ies) if (!ies)
return; return current_ev;
/* /*
* If needed, fragment the IEs buffer (at IE boundaries) into short * If needed, fragment the IEs buffer (at IE boundaries) into short
@ -1264,10 +1334,11 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVGENIE; iwe.cmd = IWEVGENIE;
iwe.u.data.length = next - pos; iwe.u.data.length = next - pos;
*current_ev = iwe_stream_add_point(info, *current_ev, current_ev = iwe_stream_add_point_check(info, current_ev,
end_buf, &iwe, end_buf, &iwe,
(void *)pos); (void *)pos);
if (IS_ERR(current_ev))
return current_ev;
pos = next; pos = next;
} }
@ -1275,10 +1346,14 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVGENIE; iwe.cmd = IWEVGENIE;
iwe.u.data.length = end - pos; iwe.u.data.length = end - pos;
*current_ev = iwe_stream_add_point(info, *current_ev, current_ev = iwe_stream_add_point_check(info, current_ev,
end_buf, &iwe, end_buf, &iwe,
(void *)pos); (void *)pos);
if (IS_ERR(current_ev))
return current_ev;
} }
return current_ev;
} }
static char * static char *
@ -1289,7 +1364,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
const struct cfg80211_bss_ies *ies; const struct cfg80211_bss_ies *ies;
struct iw_event iwe; struct iw_event iwe;
const u8 *ie; const u8 *ie;
u8 *buf, *cfg, *p; u8 buf[50];
u8 *cfg, *p, *tmp;
int rem, i, sig; int rem, i, sig;
bool ismesh = false; bool ismesh = false;
@ -1297,22 +1373,28 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.cmd = SIOCGIWAP; iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
IW_EV_ADDR_LEN); IW_EV_ADDR_LEN);
if (IS_ERR(current_ev))
return current_ev;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWFREQ; iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
iwe.u.freq.e = 0; iwe.u.freq.e = 0;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
IW_EV_FREQ_LEN); IW_EV_FREQ_LEN);
if (IS_ERR(current_ev))
return current_ev;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWFREQ; iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = bss->pub.channel->center_freq; iwe.u.freq.m = bss->pub.channel->center_freq;
iwe.u.freq.e = 6; iwe.u.freq.e = 6;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
IW_EV_FREQ_LEN); IW_EV_FREQ_LEN);
if (IS_ERR(current_ev))
return current_ev;
if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
@ -1341,8 +1423,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
/* not reached */ /* not reached */
break; break;
} }
current_ev = iwe_stream_add_event(info, current_ev, end_buf, current_ev = iwe_stream_add_event_check(info, current_ev,
&iwe, IW_EV_QUAL_LEN); end_buf, &iwe,
IW_EV_QUAL_LEN);
if (IS_ERR(current_ev))
return current_ev;
} }
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
@ -1352,8 +1437,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
else else
iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0; iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
&iwe, ""); &iwe, "");
if (IS_ERR(current_ev))
return current_ev;
rcu_read_lock(); rcu_read_lock();
ies = rcu_dereference(bss->pub.ies); ies = rcu_dereference(bss->pub.ies);
@ -1371,66 +1458,91 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.cmd = SIOCGIWESSID; iwe.cmd = SIOCGIWESSID;
iwe.u.data.length = ie[1]; iwe.u.data.length = ie[1];
iwe.u.data.flags = 1; iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point_check(info,
&iwe, (u8 *)ie + 2); current_ev,
end_buf, &iwe,
(u8 *)ie + 2);
if (IS_ERR(current_ev))
goto unlock;
break; break;
case WLAN_EID_MESH_ID: case WLAN_EID_MESH_ID:
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = SIOCGIWESSID; iwe.cmd = SIOCGIWESSID;
iwe.u.data.length = ie[1]; iwe.u.data.length = ie[1];
iwe.u.data.flags = 1; iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf, current_ev = iwe_stream_add_point_check(info,
&iwe, (u8 *)ie + 2); current_ev,
end_buf, &iwe,
(u8 *)ie + 2);
if (IS_ERR(current_ev))
goto unlock;
break; break;
case WLAN_EID_MESH_CONFIG: case WLAN_EID_MESH_CONFIG:
ismesh = true; ismesh = true;
if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
break; break;
buf = kmalloc(50, GFP_ATOMIC);
if (!buf)
break;
cfg = (u8 *)ie + 2; cfg = (u8 *)ie + 2;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM; iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh Network Path Selection Protocol ID: " sprintf(buf, "Mesh Network Path Selection Protocol ID: "
"0x%02X", cfg[0]); "0x%02X", cfg[0]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Path Selection Metric ID: 0x%02X", sprintf(buf, "Path Selection Metric ID: 0x%02X",
cfg[1]); cfg[1]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Congestion Control Mode ID: 0x%02X", sprintf(buf, "Congestion Control Mode ID: 0x%02X",
cfg[2]); cfg[2]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Formation Info: 0x%02X", cfg[5]); sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
&iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
sprintf(buf, "Capabilities: 0x%02X", cfg[6]); sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info,
end_buf, current_ev,
&iwe, buf); end_buf,
kfree(buf); &iwe, buf);
if (IS_ERR(current_ev))
goto unlock;
break; break;
case WLAN_EID_SUPP_RATES: case WLAN_EID_SUPP_RATES:
case WLAN_EID_EXT_SUPP_RATES: case WLAN_EID_EXT_SUPP_RATES:
@ -1445,8 +1557,14 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
for (i = 0; i < ie[1]; i++) { for (i = 0; i < ie[1]; i++) {
iwe.u.bitrate.value = iwe.u.bitrate.value =
((ie[i + 2] & 0x7f) * 500000); ((ie[i + 2] & 0x7f) * 500000);
tmp = p;
p = iwe_stream_add_value(info, current_ev, p, p = iwe_stream_add_value(info, current_ev, p,
end_buf, &iwe, IW_EV_PARAM_LEN); end_buf, &iwe,
IW_EV_PARAM_LEN);
if (p == tmp) {
current_ev = ERR_PTR(-E2BIG);
goto unlock;
}
} }
current_ev = p; current_ev = p;
break; break;
@ -1465,31 +1583,35 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.mode = IW_MODE_MASTER; iwe.u.mode = IW_MODE_MASTER;
else else
iwe.u.mode = IW_MODE_ADHOC; iwe.u.mode = IW_MODE_ADHOC;
current_ev = iwe_stream_add_event(info, current_ev, end_buf, current_ev = iwe_stream_add_event_check(info, current_ev,
&iwe, IW_EV_UINT_LEN); end_buf, &iwe,
IW_EV_UINT_LEN);
if (IS_ERR(current_ev))
goto unlock;
} }
buf = kmalloc(31, GFP_ATOMIC); memset(&iwe, 0, sizeof(iwe));
if (buf) { iwe.cmd = IWEVCUSTOM;
memset(&iwe, 0, sizeof(iwe)); sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
iwe.cmd = IWEVCUSTOM; iwe.u.data.length = strlen(buf);
sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
iwe.u.data.length = strlen(buf); &iwe, buf);
current_ev = iwe_stream_add_point(info, current_ev, end_buf, if (IS_ERR(current_ev))
&iwe, buf); goto unlock;
memset(&iwe, 0, sizeof(iwe)); memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM; iwe.cmd = IWEVCUSTOM;
sprintf(buf, " Last beacon: %ums ago", sprintf(buf, " Last beacon: %ums ago",
elapsed_jiffies_msecs(bss->ts)); elapsed_jiffies_msecs(bss->ts));
iwe.u.data.length = strlen(buf); iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, current_ev = iwe_stream_add_point_check(info, current_ev,
end_buf, &iwe, buf); end_buf, &iwe, buf);
kfree(buf); if (IS_ERR(current_ev))
} goto unlock;
ieee80211_scan_add_ies(info, ies, &current_ev, end_buf); current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
unlock:
rcu_read_unlock(); rcu_read_unlock();
return current_ev; return current_ev;
} }
@ -1501,19 +1623,27 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
char *current_ev = buf; char *current_ev = buf;
char *end_buf = buf + len; char *end_buf = buf + len;
struct cfg80211_internal_bss *bss; struct cfg80211_internal_bss *bss;
int err = 0;
spin_lock_bh(&rdev->bss_lock); spin_lock_bh(&rdev->bss_lock);
cfg80211_bss_expire(rdev); cfg80211_bss_expire(rdev);
list_for_each_entry(bss, &rdev->bss_list, list) { list_for_each_entry(bss, &rdev->bss_list, list) {
if (buf + len - current_ev <= IW_EV_ADDR_LEN) { if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
spin_unlock_bh(&rdev->bss_lock); err = -E2BIG;
return -E2BIG; break;
} }
current_ev = ieee80211_bss(&rdev->wiphy, info, bss, current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
current_ev, end_buf); current_ev, end_buf);
if (IS_ERR(current_ev)) {
err = PTR_ERR(current_ev);
break;
}
} }
spin_unlock_bh(&rdev->bss_lock); spin_unlock_bh(&rdev->bss_lock);
if (err)
return err;
return current_ev - buf; return current_ev - buf;
} }
@ -1545,5 +1675,5 @@ int cfg80211_wext_giwscan(struct net_device *dev,
return res; return res;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
#endif #endif

View file

@ -257,19 +257,15 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
{ {
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
u16 capa = WLAN_CAPABILITY_ESS;
ASSERT_WDEV_LOCK(wdev); ASSERT_WDEV_LOCK(wdev);
if (wdev->conn->params.privacy)
capa |= WLAN_CAPABILITY_PRIVACY;
bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
wdev->conn->params.bssid, wdev->conn->params.bssid,
wdev->conn->params.ssid, wdev->conn->params.ssid,
wdev->conn->params.ssid_len, wdev->conn->params.ssid_len,
WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, IEEE80211_BSS_TYPE_ESS,
capa); IEEE80211_PRIVACY(wdev->conn->params.privacy));
if (!bss) if (!bss)
return NULL; return NULL;
@ -637,8 +633,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
wdev->ssid, wdev->ssid_len, wdev->ssid, wdev->ssid_len,
WLAN_CAPABILITY_ESS, IEEE80211_BSS_TYPE_ESS,
WLAN_CAPABILITY_ESS); IEEE80211_PRIVACY_ANY);
if (bss) if (bss)
cfg80211_hold_bss(bss_from_pub(bss)); cfg80211_hold_bss(bss_from_pub(bss));
} }
@ -795,8 +791,8 @@ void cfg80211_roamed(struct net_device *dev,
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
wdev->ssid_len, WLAN_CAPABILITY_ESS, wdev->ssid_len,
WLAN_CAPABILITY_ESS); IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
if (WARN_ON(!bss)) if (WARN_ON(!bss))
return; return;

View file

@ -628,6 +628,7 @@ DECLARE_EVENT_CLASS(station_add_change,
__field(u8, plink_state) __field(u8, plink_state)
__field(u8, uapsd_queues) __field(u8, uapsd_queues)
__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap)) __array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
__array(char, vlan, IFNAMSIZ)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
@ -645,16 +646,19 @@ DECLARE_EVENT_CLASS(station_add_change,
if (params->ht_capa) if (params->ht_capa)
memcpy(__entry->ht_capa, params->ht_capa, memcpy(__entry->ht_capa, params->ht_capa,
sizeof(struct ieee80211_ht_cap)); sizeof(struct ieee80211_ht_cap));
memset(__entry->vlan, 0, sizeof(__entry->vlan));
if (params->vlan)
memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
), ),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
", station flags mask: %u, station flags set: %u, " ", station flags mask: %u, station flags set: %u, "
"station modify mask: %u, listen interval: %d, aid: %u, " "station modify mask: %u, listen interval: %d, aid: %u, "
"plink action: %u, plink state: %u, uapsd queues: %u", "plink action: %u, plink state: %u, uapsd queues: %u, vlan:%s",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
__entry->sta_flags_mask, __entry->sta_flags_set, __entry->sta_flags_mask, __entry->sta_flags_set,
__entry->sta_modify_mask, __entry->listen_interval, __entry->sta_modify_mask, __entry->listen_interval,
__entry->aid, __entry->plink_action, __entry->plink_state, __entry->aid, __entry->plink_action, __entry->plink_state,
__entry->uapsd_queues) __entry->uapsd_queues, __entry->vlan)
); );
DEFINE_EVENT(station_add_change, rdev_add_station, DEFINE_EVENT(station_add_change, rdev_add_station,
@ -2637,28 +2641,30 @@ DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped,
TRACE_EVENT(cfg80211_get_bss, TRACE_EVENT(cfg80211_get_bss,
TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *bssid, const u8 *ssid, size_t ssid_len,
u16 capa_mask, u16 capa_val), enum ieee80211_bss_type bss_type,
TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val), enum ieee80211_privacy privacy),
TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, bss_type, privacy),
TP_STRUCT__entry( TP_STRUCT__entry(
WIPHY_ENTRY WIPHY_ENTRY
CHAN_ENTRY CHAN_ENTRY
MAC_ENTRY(bssid) MAC_ENTRY(bssid)
__dynamic_array(u8, ssid, ssid_len) __dynamic_array(u8, ssid, ssid_len)
__field(u16, capa_mask) __field(enum ieee80211_bss_type, bss_type)
__field(u16, capa_val) __field(enum ieee80211_privacy, privacy)
), ),
TP_fast_assign( TP_fast_assign(
WIPHY_ASSIGN; WIPHY_ASSIGN;
CHAN_ASSIGN(channel); CHAN_ASSIGN(channel);
MAC_ASSIGN(bssid, bssid); MAC_ASSIGN(bssid, bssid);
memcpy(__get_dynamic_array(ssid), ssid, ssid_len); memcpy(__get_dynamic_array(ssid), ssid, ssid_len);
__entry->capa_mask = capa_mask; __entry->bss_type = bss_type;
__entry->capa_val = capa_val; __entry->privacy = privacy;
), ),
TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, " TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT
"capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG, ", buf: %#.2x, bss_type: %d, privacy: %d",
MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0], WIPHY_PR_ARG, CHAN_PR_ARG, MAC_PR_ARG(bssid),
__entry->capa_mask, __entry->capa_val) ((u8 *)__get_dynamic_array(ssid))[0], __entry->bss_type,
__entry->privacy)
); );
TRACE_EVENT(cfg80211_inform_bss_width_frame, TRACE_EVENT(cfg80211_inform_bss_width_frame,

View file

@ -1296,6 +1296,7 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
switch (operating_class) { switch (operating_class) {
case 112: case 112:
case 115 ... 127: case 115 ... 127:
case 128 ... 130:
*band = IEEE80211_BAND_5GHZ; *band = IEEE80211_BAND_5GHZ;
return true; return true;
case 81: case 81:
@ -1313,6 +1314,135 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
} }
EXPORT_SYMBOL(ieee80211_operating_class_to_band); EXPORT_SYMBOL(ieee80211_operating_class_to_band);
bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
u8 *op_class)
{
u8 vht_opclass;
u16 freq = chandef->center_freq1;
if (freq >= 2412 && freq <= 2472) {
if (chandef->width > NL80211_CHAN_WIDTH_40)
return false;
/* 2.407 GHz, channels 1..13 */
if (chandef->width == NL80211_CHAN_WIDTH_40) {
if (freq > chandef->chan->center_freq)
*op_class = 83; /* HT40+ */
else
*op_class = 84; /* HT40- */
} else {
*op_class = 81;
}
return true;
}
if (freq == 2484) {
if (chandef->width > NL80211_CHAN_WIDTH_40)
return false;
*op_class = 82; /* channel 14 */
return true;
}
switch (chandef->width) {
case NL80211_CHAN_WIDTH_80:
vht_opclass = 128;
break;
case NL80211_CHAN_WIDTH_160:
vht_opclass = 129;
break;
case NL80211_CHAN_WIDTH_80P80:
vht_opclass = 130;
break;
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_5:
return false; /* unsupported for now */
default:
vht_opclass = 0;
break;
}
/* 5 GHz, channels 36..48 */
if (freq >= 5180 && freq <= 5240) {
if (vht_opclass) {
*op_class = vht_opclass;
} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
if (freq > chandef->chan->center_freq)
*op_class = 116;
else
*op_class = 117;
} else {
*op_class = 115;
}
return true;
}
/* 5 GHz, channels 52..64 */
if (freq >= 5260 && freq <= 5320) {
if (vht_opclass) {
*op_class = vht_opclass;
} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
if (freq > chandef->chan->center_freq)
*op_class = 119;
else
*op_class = 120;
} else {
*op_class = 118;
}
return true;
}
/* 5 GHz, channels 100..144 */
if (freq >= 5500 && freq <= 5720) {
if (vht_opclass) {
*op_class = vht_opclass;
} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
if (freq > chandef->chan->center_freq)
*op_class = 122;
else
*op_class = 123;
} else {
*op_class = 121;
}
return true;
}
/* 5 GHz, channels 149..169 */
if (freq >= 5745 && freq <= 5845) {
if (vht_opclass) {
*op_class = vht_opclass;
} else if (chandef->width == NL80211_CHAN_WIDTH_40) {
if (freq > chandef->chan->center_freq)
*op_class = 126;
else
*op_class = 127;
} else if (freq <= 5805) {
*op_class = 124;
} else {
*op_class = 125;
}
return true;
}
/* 56.16 GHz, channel 1..4 */
if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
if (chandef->width >= NL80211_CHAN_WIDTH_40)
return false;
*op_class = 180;
return true;
}
/* not supported yet */
return false;
}
EXPORT_SYMBOL(ieee80211_chandef_to_operating_class);
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int) u32 beacon_int)
{ {

View file

@ -63,7 +63,7 @@ int cfg80211_wext_giwname(struct net_device *dev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwname); EXPORT_WEXT_HANDLER(cfg80211_wext_giwname);
int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra) u32 *mode, char *extra)
@ -99,7 +99,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode);
int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
u32 *mode, char *extra) u32 *mode, char *extra)
@ -134,7 +134,7 @@ int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
} }
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode); EXPORT_WEXT_HANDLER(cfg80211_wext_giwmode);
int cfg80211_wext_giwrange(struct net_device *dev, int cfg80211_wext_giwrange(struct net_device *dev,
@ -248,7 +248,7 @@ int cfg80211_wext_giwrange(struct net_device *dev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); EXPORT_WEXT_HANDLER(cfg80211_wext_giwrange);
/** /**
@ -303,7 +303,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,
return err; return err;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts); EXPORT_WEXT_HANDLER(cfg80211_wext_siwrts);
int cfg80211_wext_giwrts(struct net_device *dev, int cfg80211_wext_giwrts(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
@ -317,7 +317,7 @@ int cfg80211_wext_giwrts(struct net_device *dev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts); EXPORT_WEXT_HANDLER(cfg80211_wext_giwrts);
int cfg80211_wext_siwfrag(struct net_device *dev, int cfg80211_wext_siwfrag(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
@ -343,7 +343,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
return err; return err;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag); EXPORT_WEXT_HANDLER(cfg80211_wext_siwfrag);
int cfg80211_wext_giwfrag(struct net_device *dev, int cfg80211_wext_giwfrag(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
@ -357,7 +357,7 @@ int cfg80211_wext_giwfrag(struct net_device *dev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag); EXPORT_WEXT_HANDLER(cfg80211_wext_giwfrag);
static int cfg80211_wext_siwretry(struct net_device *dev, static int cfg80211_wext_siwretry(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
@ -427,7 +427,7 @@ int cfg80211_wext_giwretry(struct net_device *dev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); EXPORT_WEXT_HANDLER(cfg80211_wext_giwretry);
static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool pairwise, struct net_device *dev, bool pairwise,

View file

@ -4,6 +4,12 @@
#include <net/iw_handler.h> #include <net/iw_handler.h>
#include <linux/wireless.h> #include <linux/wireless.h>
#ifdef CONFIG_CFG80211_WEXT_EXPORT
#define EXPORT_WEXT_HANDLER(h) EXPORT_SYMBOL_GPL(h)
#else
#define EXPORT_WEXT_HANDLER(h)
#endif /* CONFIG_CFG80211_WEXT_EXPORT */
int cfg80211_ibss_wext_siwfreq(struct net_device *dev, int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
struct iw_request_info *info, struct iw_request_info *info,
struct iw_freq *freq, char *extra); struct iw_freq *freq, char *extra);