diff options
author | David S. Miller <davem@davemloft.net> | 2018-12-19 08:36:18 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-12-19 08:36:18 -0800 |
commit | 5a862f86b8e86562fc8532160c5530a13e1e944b (patch) | |
tree | 0fa13538752825a01dc98c34dc175271c9341815 /net | |
parent | 33f18c96afdf4da20014f834874e2820ee880cde (diff) | |
parent | d359bbce0601c6a19203a4b813a7e3910fcba282 (diff) |
Merge tag 'mac80211-next-for-davem-2018-12-19' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says:
====================
This time we have too many changes to list, highlights:
* virt_wifi - wireless control simulation on top of
another network interface
* hwsim configurability to test capabilities similar
to real hardware
* various mesh improvements
* various radiotap vendor data fixes in mac80211
* finally the nl_set_extack_cookie_u64() we talked
about previously, used for
* peer measurement APIs, right now only with FTM
(flight time measurement) for location
* made nl80211 radio/interface announcements more complete
* various new HE (802.11ax) things:
updates, TWT support, ...
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
31 files changed, 1330 insertions, 148 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index f869e35d0974..be471fe95048 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -57,14 +57,13 @@ comment "Some wireless drivers require a rate control algorithm" depends on MAC80211 && MAC80211_HAS_RC=n config MAC80211_MESH - bool "Enable mac80211 mesh networking (pre-802.11s) support" + bool "Enable mac80211 mesh networking support" depends on MAC80211 ---help--- - This options enables support of Draft 802.11s mesh networking. - The implementation is based on Draft 2.08 of the Mesh Networking - amendment. However, no compliance with that draft is claimed or even - possible, as drafts leave a number of identifiers to be defined after - ratification. For more information visit http://o11s.org/. + Select this option to enable 802.11 mesh operation in mac80211 + drivers that support it. 802.11 mesh connects multiple stations + over (possibly multi-hop) wireless links to form a single logical + LAN. config MAC80211_LEDS bool "Enable LED triggers" diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 818aa0060349..de65fe3ed9cc 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -800,8 +800,8 @@ static int ieee80211_set_ftm_responder_params( u8 *pos; int len; - if ((!lci || !lci_len) && (!civicloc || !civicloc_len)) - return 1; + if (!lci_len && !civicloc_len) + return 0; bss_conf = &sdata->vif.bss_conf; old = bss_conf->ftmr_params; @@ -2028,6 +2028,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, nconf->dot11MeshAwakeWindowDuration; if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask)) conf->plink_timeout = nconf->plink_timeout; + if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask)) + conf->dot11MeshConnectedToMeshGate = + nconf->dot11MeshConnectedToMeshGate; ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON); return 0; } @@ -3850,6 +3853,26 @@ ieee80211_get_ftm_responder_stats(struct wiphy *wiphy, return drv_get_ftm_responder_stats(local, sdata, ftm_stats); } +static int +ieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev, + struct cfg80211_pmsr_request *request) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev); + + return drv_start_pmsr(local, sdata, request); +} + +static void +ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev, + struct cfg80211_pmsr_request *request) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev); + + return drv_abort_pmsr(local, sdata, request); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -3945,4 +3968,6 @@ const struct cfg80211_ops mac80211_config_ops = { .tx_control_port = ieee80211_tx_control_port, .get_txq_stats = ieee80211_get_txq_stats, .get_ftm_responder_stats = ieee80211_get_ftm_responder_stats, + .start_pmsr = ieee80211_start_pmsr, + .abort_pmsr = ieee80211_abort_pmsr, }; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index c813207bb123..cff0fb3578c9 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -641,6 +641,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC); IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC); +IEEE80211_IF_FILE(dot11MeshConnectedToMeshGate, + u.mesh.mshcfg.dot11MeshConnectedToMeshGate, DEC); #endif #define DEBUGFS_ADD_MODE(name, mode) \ @@ -762,6 +764,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); MESHPARAMS_ADD(power_mode); MESHPARAMS_ADD(dot11MeshAwakeWindowDuration); + MESHPARAMS_ADD(dot11MeshConnectedToMeshGate); #undef MESHPARAMS_ADD } #endif diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index af5185a836e5..b753194710ad 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -795,22 +795,22 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf, #define PRINT_NSS_SUPP(f, n) \ do { \ - int i; \ + int _i; \ u16 v = le16_to_cpu(nss->f); \ p += scnprintf(p, buf_sz + buf - p, n ": %#.4x\n", v); \ - for (i = 0; i < 8; i += 2) { \ - switch ((v >> i) & 0x3) { \ + for (_i = 0; _i < 8; _i += 2) { \ + switch ((v >> _i) & 0x3) { \ case 0: \ - PRINT(n "-%d-SUPPORT-0-7", i / 2); \ + PRINT(n "-%d-SUPPORT-0-7", _i / 2); \ break; \ case 1: \ - PRINT(n "-%d-SUPPORT-0-9", i / 2); \ + PRINT(n "-%d-SUPPORT-0-9", _i / 2); \ break; \ case 2: \ - PRINT(n "-%d-SUPPORT-0-11", i / 2); \ + PRINT(n "-%d-SUPPORT-0-11", _i / 2); \ break; \ case 3: \ - PRINT(n "-%d-NOT-SUPPORTED", i / 2); \ + PRINT(n "-%d-NOT-SUPPORTED", _i / 2); \ break; \ } \ } \ diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0b1747a2313d..3e0d5922a440 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1199,6 +1199,40 @@ drv_get_ftm_responder_stats(struct ieee80211_local *local, return ret; } +static inline int drv_start_pmsr(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_pmsr_request *request) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_start_pmsr(local, sdata); + + if (local->ops->start_pmsr) + ret = local->ops->start_pmsr(&local->hw, &sdata->vif, request); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline void drv_abort_pmsr(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_pmsr_request *request) +{ + trace_drv_abort_pmsr(local, sdata); + + might_sleep(); + if (!check_sdata_in_driver(sdata)) + return; + + if (local->ops->abort_pmsr) + local->ops->abort_pmsr(&local->hw, &sdata->vif, request); + trace_drv_return_void(local); +} + static inline int drv_start_nan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_conf *conf) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 10a05062e4a0..7dfb4e2f98b2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -500,6 +500,7 @@ struct ieee80211_if_managed { unsigned int uapsd_max_sp_len; int wmm_last_param_set; + int mu_edca_last_param_set; u8 use_4addr; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5f3c81e705c7..e9652e623a31 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1801,7 +1801,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, } ieee80211_assign_perm_addr(local, ndev->perm_addr, type); - if (params && is_valid_ether_addr(params->macaddr)) + if (is_valid_ether_addr(params->macaddr)) memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN); else memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); @@ -1870,11 +1870,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_setup_sdata(sdata, type); if (ndev) { - if (params) { - ndev->ieee80211_ptr->use_4addr = params->use_4addr; - if (type == NL80211_IFTYPE_STATION) - sdata->u.mgd.use_4addr = params->use_4addr; - } + ndev->ieee80211_ptr->use_4addr = params->use_4addr; + if (type == NL80211_IFTYPE_STATION) + sdata->u.mgd.use_4addr = params->use_4addr; ndev->features |= local->hw.netdev_features; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 83e71e6b2ebe..ada8e16d52d2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1221,8 +1221,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* add one default STA interface if supported */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && !ieee80211_hw_check(hw, NO_AUTO_VIF)) { + struct vif_params params = {0}; + result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL, - NL80211_IFTYPE_STATION, NULL); + NL80211_IFTYPE_STATION, ¶ms); if (result) wiphy_warn(local->hw.wiphy, "Failed to add default virtual iface\n"); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8bad414c52ad..c90452aa0c42 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -254,6 +254,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos, neighbors; u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); + bool is_connected_to_gate = ifmsh->num_gates > 0 || + ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol || + ifmsh->mshcfg.dot11MeshConnectedToMeshGate; if (skb_tailroom(skb) < 2 + meshconf_len) return -ENOMEM; @@ -278,7 +281,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, /* Mesh Formation Info - number of neighbors */ neighbors = atomic_read(&ifmsh->estab_plinks); neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS); - *pos++ = neighbors << 1; + *pos++ = (neighbors << 1) | is_connected_to_gate; /* Mesh capability */ *pos = 0x00; *pos |= ifmsh->mshcfg.dot11MeshForwarding ? @@ -1191,7 +1194,8 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (!sdata->u.mesh.user_mpm || sdata->u.mesh.mshcfg.rssi_threshold == 0 || sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal) - mesh_neighbour_update(sdata, mgmt->sa, &elems); + mesh_neighbour_update(sdata, mgmt->sa, &elems, + rx_status); } if (ifmsh->sync_ops) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 21526630bf65..cad6592c52a1 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -273,7 +273,8 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata); /* Mesh plinks */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr, struct ieee802_11_elems *ie); + u8 *hw_addr, struct ieee802_11_elems *ie, + struct ieee80211_rx_status *rx_status); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_timer(struct timer_list *t); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 5b5b0f95ffd1..33055c8ed37e 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -513,7 +513,8 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) static struct sta_info * mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, - struct ieee802_11_elems *elems) + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) { struct sta_info *sta = NULL; @@ -521,11 +522,17 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, if (sdata->u.mesh.user_mpm || sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { if (mesh_peer_accepts_plinks(elems) && - mesh_plink_availables(sdata)) + mesh_plink_availables(sdata)) { + int sig = 0; + + if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM)) + sig = rx_status->signal; + cfg80211_notify_new_peer_candidate(sdata->dev, addr, elems->ie_start, elems->total_len, - GFP_KERNEL); + sig, GFP_KERNEL); + } } else sta = __mesh_sta_info_alloc(sdata, addr); @@ -538,13 +545,15 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, * @sdata: local meshif * @addr: peer's address * @elems: IEs from beacon or mesh peering frame. + * @rx_status: rx status for the frame for signal reporting * * Return existing or newly allocated sta_info under RCU read lock. * (re)initialize with given IEs. */ static struct sta_info * mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, - u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) + u8 *addr, struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) __acquires(RCU) { struct sta_info *sta = NULL; @@ -555,7 +564,7 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, } else { rcu_read_unlock(); /* can't run atomic */ - sta = mesh_sta_info_alloc(sdata, addr, elems); + sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status); if (!sta) { rcu_read_lock(); return NULL; @@ -576,20 +585,25 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, * @sdata: local meshif * @addr: peer's address * @elems: IEs from beacon or mesh peering frame + * @rx_status: rx status for the frame for signal reporting * * Initiates peering if appropriate. */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, u8 *hw_addr, - struct ieee802_11_elems *elems) + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) { struct sta_info *sta; u32 changed = 0; - sta = mesh_sta_info_get(sdata, hw_addr, elems); + sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status); if (!sta) goto out; + sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form & + IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE; + if (mesh_peer_accepts_plinks(elems) && sta->mesh->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && @@ -1069,7 +1083,8 @@ out: static void mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - struct ieee802_11_elems *elems) + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) { struct sta_info *sta; @@ -1134,7 +1149,7 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, if (event == OPN_ACPT) { rcu_read_unlock(); /* allocate sta entry if necessary and update info */ - sta = mesh_sta_info_get(sdata, mgmt->sa, elems); + sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status); if (!sta) { mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); goto unlock_rcu; @@ -1200,5 +1215,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, return; } ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); - mesh_process_plink_frame(sdata, mgmt, &elems); + mesh_process_plink_frame(sdata, mgmt, &elems, rx_status); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bcf5ffc1567a..687821567287 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -916,6 +916,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) ieee80211_add_vht_ie(sdata, skb, sband, &assoc_data->ap_vht_cap); + /* + * If AP doesn't support HT, mark HE as disabled. + * If on the 5GHz band, make sure it supports VHT. + */ + if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || + (sband->band == NL80211_BAND_5GHZ && + ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) + ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) ieee80211_add_he_ie(sdata, skb, sband); @@ -1869,7 +1878,7 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t left; - int count, ac; + int count, mu_edca_count, ac; const u8 *pos; u8 uapsd_queues = 0; @@ -1889,9 +1898,16 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local, uapsd_queues = ifmgd->uapsd_queues; count = wmm_param[6] & 0x0f; - if (count == ifmgd->wmm_last_param_set) + /* -1 is the initial value of ifmgd->mu_edca_last_param_set. + * if mu_edca was preset before and now it disappeared tell + * the driver about it. + */ + mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1; + if (count == ifmgd->wmm_last_param_set && + mu_edca_count == ifmgd->mu_edca_last_param_set) return false; ifmgd->wmm_last_param_set = count; + ifmgd->mu_edca_last_param_set = mu_edca_count; pos = wmm_param + 8; left = wmm_param_len - 8; @@ -3062,6 +3078,19 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, } } +static bool ieee80211_twt_req_supported(const struct sta_info *sta, + const struct ieee802_11_elems *elems) +{ + if (elems->ext_capab_len < 10) + return false; + + if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT)) + return false; + + return sta->sta.he_cap.he_cap_elem.mac_cap_info[0] & + IEEE80211_HE_MAC_CAP0_TWT_RES; +} + static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss, struct ieee80211_mgmt *mgmt, size_t len) @@ -3215,16 +3244,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, goto out; } - /* - * If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark - * HE as disabled. If on the 5GHz band, make sure it supports VHT. - */ - if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || - (sband->band == NL80211_BAND_5GHZ && - ifmgd->flags & IEEE80211_STA_DISABLE_VHT) || - (!elems.he_cap && !elems.he_operation)) - ifmgd->flags |= IEEE80211_STA_DISABLE_HE; - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && (!elems.he_cap || !elems.he_operation)) { mutex_unlock(&sdata->local->sta_mtx); @@ -3251,8 +3270,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta); bss_conf->he_support = sta->sta.he_cap.has_he; + bss_conf->twt_requester = + ieee80211_twt_req_supported(sta, &elems); } else { bss_conf->he_support = false; + bss_conf->twt_requester = false; } if (bss_conf->he_support) { @@ -3337,6 +3359,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, * 4-bit value. */ ifmgd->wmm_last_param_set = -1; + ifmgd->mu_edca_last_param_set = -1; if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { ieee80211_set_wmm_default(sdata, false, false); @@ -4660,8 +4683,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, } } - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && - ieee80211_get_he_sta_cap(sband)) { + if (!ieee80211_get_he_sta_cap(sband)) + ifmgd->flags |= IEEE80211_STA_DISABLE_HE; + + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) { const struct cfg80211_bss_ies *ies; const u8 *he_oper_ie; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 428f7ad5f9b5..45aad3d3108c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -143,6 +143,9 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, /* allocate extra bitmaps */ if (status->chains) len += 4 * hweight8(status->chains); + /* vendor presence bitmap */ + if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) + len += 4; if (ieee80211_have_rx_timestamp(status)) { len = ALIGN(len, 8); @@ -207,8 +210,6 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { struct ieee80211_vendor_radiotap *rtap = (void *)skb->data; - /* vendor presence bitmap */ - len += 4; /* alignment for fixed 6-byte vendor data header */ len = ALIGN(len, 2); /* vendor data header */ @@ -753,6 +754,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, struct ieee80211_sub_if_data *monitor_sdata = rcu_dereference(local->monitor_sdata); bool only_monitor = false; + unsigned int min_head_len; if (status->flag & RX_FLAG_RADIOTAP_HE) rtap_space += sizeof(struct ieee80211_radiotap_he); @@ -760,12 +762,18 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (status->flag & RX_FLAG_RADIOTAP_HE_MU) rtap_space += sizeof(struct ieee80211_radiotap_he_mu); + if (status->flag & RX_FLAG_RADIOTAP_LSIG) + rtap_space += sizeof(struct ieee80211_radiotap_lsig); + if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { - struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; + struct ieee80211_vendor_radiotap *rtap = + (void *)(origskb->data + rtap_space); rtap_space += sizeof(*rtap) + rtap->len + rtap->pad; } + min_head_len = rtap_space; + /* * First, we may need to make a copy of the skb because * (1) we need to modify it for radiotap (if not present), and @@ -775,18 +783,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * the SKB because it has a bad FCS/PLCP checksum. */ - if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { - if (unlikely(origskb->len <= FCS_LEN)) { - /* driver bug */ - WARN_ON(1); - dev_kfree_skb(origskb); - return NULL; + if (!(status->flag & RX_FLAG_NO_PSDU)) { + if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { + if (unlikely(origskb->len <= FCS_LEN + rtap_space)) { + /* driver bug */ + WARN_ON(1); + dev_kfree_skb(origskb); + return NULL; + } + present_fcs_len = FCS_LEN; } - present_fcs_len = FCS_LEN; + + /* also consider the hdr->frame_control */ + min_head_len += 2; } - /* ensure hdr->frame_control and vendor radiotap data are in skb head */ - if (!pskb_may_pull(origskb, 2 + rtap_space)) { + /* ensure that the expected data elements are in skb head */ + if (!pskb_may_pull(origskb, min_head_len)) { dev_kfree_skb(origskb); return NULL; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5d2a11777718..95413413f98c 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -356,7 +356,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); - bool hw_scan = local->ops->hw_scan; + bool hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); bool was_scanning = local->scanning; struct cfg80211_scan_request *scan_req; struct ieee80211_sub_if_data *scan_sdata; @@ -606,6 +606,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; + bool hw_scan = local->ops->hw_scan; int rc; lockdep_assert_held(&local->mtx); @@ -620,7 +621,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, return 0; } - if (local->ops->hw_scan) { + again: + if (hw_scan) { u8 *ies; local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; @@ -679,7 +681,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, else memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN); - if (local->ops->hw_scan) { + if (hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && (req->channels[0] == local->_oper_chandef.chan)) { @@ -722,7 +724,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_idle(local); - if (local->ops->hw_scan) { + if (hw_scan) { WARN_ON(!ieee80211_prep_hw_scan(local)); rc = drv_hw_scan(local, sdata, local->hw_scan_req); } else { @@ -740,6 +742,18 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, RCU_INIT_POINTER(local->scan_sdata, NULL); } + if (hw_scan && rc == 1) { + /* + * we can't fall back to software for P2P-GO + * as it must update NoA etc. + */ + if (ieee80211_vif_type_p2p(&sdata->vif) == + NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + hw_scan = false; + goto again; + } + return rc; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index fb8c2252ac0e..c4a8f115ed33 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2253,11 +2253,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, } if (tidstats && !cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) { - for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { - struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; - - sta_set_tidstats(sta, tidstats, i); - } + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) + sta_set_tidstats(sta, &sinfo->pertid[i], i); } if (ieee80211_vif_is_mesh(&sdata->vif)) { @@ -2267,7 +2264,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, BIT_ULL(NL80211_STA_INFO_PLINK_STATE) | BIT_ULL(NL80211_STA_INFO_LOCAL_PM) | BIT_ULL(NL80211_STA_INFO_PEER_PM) | - BIT_ULL(NL80211_STA_INFO_NONPEER_PM); + BIT_ULL(NL80211_STA_INFO_NONPEER_PM) | + BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_GATE); sinfo->llid = sta->mesh->llid; sinfo->plid = sta->mesh->plid; @@ -2279,6 +2277,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sinfo->local_pm = sta->mesh->local_pm; sinfo->peer_pm = sta->mesh->peer_pm; sinfo->nonpeer_pm = sta->mesh->nonpeer_pm; + sinfo->connected_to_gate = sta->mesh->connected_to_gate; #endif } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9a04327d71d1..8eb29041be54 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -364,6 +364,7 @@ DECLARE_EWMA(mesh_fail_avg, 20, 8) * @nonpeer_pm: STA power save mode towards non-peer neighbors * @processed_beacon: set to true after peer rates and capabilities are * processed + * @connected_to_gate: true if mesh STA has a path to a mesh gate * @fail_avg: moving percentage of failed MSDUs */ struct mesh_sta { @@ -381,6 +382,7 @@ struct mesh_sta { u8 plink_retries; bool processed_beacon; + bool connected_to_gate; enum nl80211_plink_state plink_state; u32 plink_timeout; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 588c51a67c89..35ea0dcb55e6 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1052,10 +1052,10 @@ TRACE_EVENT(drv_ampdu_action, ); TRACE_EVENT(drv_get_survey, - TP_PROTO(struct ieee80211_local *local, int idx, + TP_PROTO(struct ieee80211_local *local, int _idx, struct survey_info *survey), - TP_ARGS(local, idx, survey), + TP_ARGS(local, _idx, survey), TP_STRUCT__entry( LOCAL_ENTRY @@ -1064,7 +1064,7 @@ TRACE_EVENT(drv_get_survey, TP_fast_assign( LOCAL_ASSIGN; - __entry->idx = idx; + __entry->idx = _idx; ), TP_printk( @@ -1882,6 +1882,18 @@ TRACE_EVENT(drv_del_nan_func, ) ); +DEFINE_EVENT(local_sdata_evt, drv_start_pmsr, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_sdata_evt, drv_abort_pmsr, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1f536ba573b4..f170d6c6629a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3218,6 +3218,9 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, if (!ieee80211_hw_check(&local->hw, TX_AMSDU)) return false; + if (skb_is_gso(skb)) + return false; + if (!txq) return false; @@ -3242,7 +3245,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, tin = &txqi->tin; flow = fq_flow_classify(fq, tin, skb, fq_flow_get_default_func); head = skb_peek_tail(&flow->queue); - if (!head) + if (!head || skb_is_gso(head)) goto out; orig_len = head->len; @@ -3583,7 +3586,7 @@ begin: skb_queue_splice_tail(&tx.skbs, &txqi->frags); } - if (skb && skb_has_frag_list(skb) && + if (skb_has_frag_list(skb) && !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) { if (skb_linearize(skb)) { ieee80211_free_txskb(&local->hw, skb); @@ -4579,7 +4582,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); if (qos) { - __le16 qos = cpu_to_le16(7); + __le16 qoshdr = cpu_to_le16(7); BUILD_BUG_ON((IEEE80211_STYPE_QOS_NULLFUNC | IEEE80211_STYPE_NULLFUNC) != @@ -4588,7 +4591,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, cpu_to_le16(IEEE80211_STYPE_QOS_NULLFUNC); skb->priority = 7; skb_set_queue_mapping(skb, IEEE80211_AC_VO); - skb_put_data(skb, &qos, sizeof(qos)); + skb_put_data(skb, &qoshdr, sizeof(qoshdr)); } memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index bec424316ea4..d0eb38b890aa 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -299,16 +299,16 @@ out: spin_unlock_bh(&fq->lock); } -void ieee80211_wake_txqs(unsigned long data) +static void +__releases(&local->queue_stop_reason_lock) +__acquires(&local->queue_stop_reason_lock) +_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags) { - struct ieee80211_local *local = (struct ieee80211_local *)data; struct ieee80211_sub_if_data *sdata; int n_acs = IEEE80211_NUM_ACS; - unsigned long flags; int i; rcu_read_lock(); - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->hw.queues < IEEE80211_NUM_ACS) n_acs = 1; @@ -317,7 +317,7 @@ void ieee80211_wake_txqs(unsigned long data) if (local->queue_stop_reasons[i]) continue; - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags); list_for_each_entry_rcu(sdata, &local->interfaces, list) { int ac; @@ -329,13 +329,22 @@ void ieee80211_wake_txqs(unsigned long data) __ieee80211_wake_txqs(sdata, ac); } } - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + spin_lock_irqsave(&local->queue_stop_reason_lock, *flags); } - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); rcu_read_unlock(); } +void ieee80211_wake_txqs(unsigned long data) +{ + struct ieee80211_local *local = (struct ieee80211_local *)data; + unsigned long flags; + + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + _ieee80211_wake_txqs(local, &flags); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +} + void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) { struct ieee80211_sub_if_data *sdata; @@ -371,7 +380,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason, - bool refcounted) + bool refcounted, + unsigned long *flags) { struct ieee80211_local *local = hw_to_local(hw); @@ -405,8 +415,19 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, } else tasklet_schedule(&local->tx_pending_tasklet); - if (local->ops->wake_tx_queue) - tasklet_schedule(&local->wake_txqs_tasklet); + /* + * Calling _ieee80211_wake_txqs here can be a problem because it may + * release queue_stop_reason_lock which has been taken by + * __ieee80211_wake_queue's caller. It is certainly not very nice to + * release someone's lock, but it is fine because all the callers of + * __ieee80211_wake_queue call it right before releasing the lock. + */ + if (local->ops->wake_tx_queue) { + if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) + tasklet_schedule(&local->wake_txqs_tasklet); + else + _ieee80211_wake_txqs(local, flags); + } } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -417,7 +438,7 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, unsigned long flags; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - __ieee80211_wake_queue(hw, queue, reason, refcounted); + __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -514,7 +535,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, false); __skb_queue_tail(&local->pending[queue], skb); __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, - false); + false, &flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -547,7 +568,7 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local, for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, - false); + false, &flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -605,7 +626,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); for_each_set_bit(i, &queues, hw->queues) - __ieee80211_wake_queue(hw, i, reason, refcounted); + __ieee80211_wake_queue(hw, i, reason, refcounted, &flags); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } @@ -1202,6 +1223,8 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA && elen >= (sizeof(*elems->mu_edca_param_set) + 1)) { elems->mu_edca_param_set = (void *)&pos[1]; + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); } else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) { elems->he_cap = (void *)&pos[1]; elems->he_cap_len = elen - 1; diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 0f8465852254..41a5cd4b5c0e 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -16,7 +16,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/gpio.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 1d84f91bbfb0..72a224ce8e0a 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o +cfg80211-y += pmsr.o cfg80211-$(CONFIG_OF) += of.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2db713d18f71..7dc1bbd0888f 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -6,6 +6,7 @@ * * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright 2018 Intel Corporation */ #include <linux/export.h> @@ -747,6 +748,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported) return false; + /* fall through */ case NL80211_CHAN_WIDTH_20_NOHT: prohibited_flags |= IEEE80211_CHAN_NO_20MHZ; width = 20; @@ -769,6 +771,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) return false; + /* fall through */ case NL80211_CHAN_WIDTH_80: if (!vht_cap->vht_supported) return false; diff --git a/net/wireless/core.c b/net/wireless/core.c index 5bd01058b9e6..623dfe5e211c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -4,6 +4,7 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -190,11 +191,25 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, return err; } + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (!wdev->netdev) + continue; + nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); + } + nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); + wiphy_net_set(&rdev->wiphy, net); err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); WARN_ON(err); + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (!wdev->netdev) + continue; + nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); + } + return 0; } @@ -664,6 +679,34 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; #endif + if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported)) + return -EINVAL; + + if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) { + if (WARN_ON(!wiphy->pmsr_capa->ftm.asap && + !wiphy->pmsr_capa->ftm.non_asap)) + return -EINVAL; + if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles || + !wiphy->pmsr_capa->ftm.bandwidths)) + return -EINVAL; + if (WARN_ON(wiphy->pmsr_capa->ftm.preambles & + ~(BIT(NL80211_PREAMBLE_LEGACY) | + BIT(NL80211_PREAMBLE_HT) | + BIT(NL80211_PREAMBLE_VHT) | + BIT(NL80211_PREAMBLE_DMG)))) + return -EINVAL; + if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths & + ~(BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_80P80) | + BIT(NL80211_CHAN_WIDTH_160) | + BIT(NL80211_CHAN_WIDTH_5) | + BIT(NL80211_CHAN_WIDTH_10)))) + return -EINVAL; + } + /* * if a wiphy has unsupported modes for regulatory channel enforcement, * opt-out of enforcement checking @@ -1087,6 +1130,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, ASSERT_RTNL(); ASSERT_WDEV_LOCK(wdev); + cfg80211_pmsr_wdev_down(wdev); + switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: __cfg80211_leave_ibss(rdev, dev, true); @@ -1174,6 +1219,9 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev, spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); + INIT_LIST_HEAD(&wdev->pmsr_list); + spin_lock_init(&wdev->pmsr_lock); + INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); /* * We get here also when the interface changes network namespaces, diff --git a/net/wireless/core.h b/net/wireless/core.h index c61dbba8bf47..c5d6f3418601 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,6 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> + * Copyright (C) 2018 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -530,4 +531,8 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, void cfg80211_cqm_config_free(struct wireless_dev *wdev); +void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid); +void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev); +void cfg80211_pmsr_free_wk(struct work_struct *work); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8d763725498c..10ec05589795 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -240,7 +240,63 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { .len = U8_MAX }, }; -static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { +static const struct nla_policy +nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { + [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = + NLA_POLICY_MAX(NLA_U8, 15), + [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, + [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] = + NLA_POLICY_MAX(NLA_U8, 15), + [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] = + NLA_POLICY_MAX(NLA_U8, 15), + [NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 }, + [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG }, +}; + +static const struct nla_policy +nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = { + [NL80211_PMSR_TYPE_FTM] = + NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX, + nl80211_pmsr_ftm_req_attr_policy), +}; + +static const struct nla_policy +nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = { + [NL80211_PMSR_REQ_ATTR_DATA] = + NLA_POLICY_NESTED(NL80211_PMSR_TYPE_MAX, + nl80211_pmsr_req_data_policy), + [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG }, +}; + +static const struct nla_policy +nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { + [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR, + /* + * we could specify this again to be the top-level policy, + * but that would open us up to recursion problems ... + */ + [NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED }, + [NL80211_PMSR_PEER_ATTR_REQ] = + NLA_POLICY_NESTED(NL80211_PMSR_REQ_ATTR_MAX, + nl80211_pmsr_req_attr_policy), + [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT }, +}; + +static const struct nla_policy +nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = { + [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT }, + [NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT }, + [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT }, + [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT }, + [NL80211_PMSR_ATTR_PEERS] = + NLA_POLICY_NESTED_ARRAY(NL80211_PMSR_PEER_ATTR_MAX, + nl80211_psmr_peer_attr_policy), +}; + +const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = 20-1 }, @@ -497,6 +553,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .type = NLA_NESTED, .validation_data = nl80211_ftm_responder_policy, }, + [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1), + [NL80211_ATTR_PEER_MEASUREMENTS] = + NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX, + nl80211_pmsr_attr_policy), }; /* policy for the key attributes */ @@ -637,9 +697,9 @@ nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = { [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 }, }; -static int nl80211_prepare_wdev_dump(struct netlink_callback *cb, - struct cfg80211_registered_device **rdev, - struct wireless_dev **wdev) +int nl80211_prepare_wdev_dump(struct netlink_callback *cb, + struct cfg80211_registered_device **rdev, + struct wireless_dev **wdev) { int err; @@ -684,8 +744,8 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb, } /* message building helper */ -static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, - int flags, u8 cmd) +void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, + int flags, u8 cmd) { /* since there is no private header just add the generic one */ return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd); @@ -1615,6 +1675,91 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, return -ENOBUFS; } +static int +nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap, + struct sk_buff *msg) +{ + struct nlattr *ftm; + + if (!cap->ftm.supported) + return 0; + + ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM); + if (!ftm) + return -ENOBUFS; + + if (cap->ftm.asap && nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_ASAP)) + return -ENOBUFS; + if (cap->ftm.non_asap && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP)) + return -ENOBUFS; + if (cap->ftm.request_lci && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI)) + return -ENOBUFS; + if (cap->ftm.request_civicloc && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC)) + return -ENOBUFS; + if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, + cap->ftm.preambles)) + return -ENOBUFS; + if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, + cap->ftm.bandwidths)) + return -ENOBUFS; + if (cap->ftm.max_bursts_exponent >= 0 && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, + cap->ftm.max_bursts_exponent)) + return -ENOBUFS; + if (cap->ftm.max_ftms_per_burst && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, + cap->ftm.max_ftms_per_burst)) + return -ENOBUFS; + + nla_nest_end(msg, ftm); + return 0; +} + +static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev, + struct sk_buff *msg) +{ + const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa; + struct nlattr *pmsr, *caps; + + if (!cap) + return 0; + + /* + * we don't need to clean up anything here since the caller + * will genlmsg_cancel() if we fail + */ + + pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS); + if (!pmsr) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEERS, cap->max_peers)) + return -ENOBUFS; + + if (cap->report_ap_tsf && + nla_put_flag(msg, NL80211_PMSR_ATTR_REPORT_AP_TSF)) + return -ENOBUFS; + + if (cap->randomize_mac_addr && + nla_put_flag(msg, NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR)) + return -ENOBUFS; + + caps = nla_nest_start(msg, NL80211_PMSR_ATTR_TYPE_CAPA); + if (!caps) + return -ENOBUFS; + + if (nl80211_send_pmsr_ftm_capa(cap, msg)) + return -ENOBUFS; + + nla_nest_end(msg, caps); + nla_nest_end(msg, pmsr); + + return 0; +} + struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; @@ -1706,6 +1851,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 1: if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, sizeof(u32) * rdev->wiphy.n_cipher_suites, @@ -1752,6 +1898,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 2: if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, rdev->wiphy.interface_modes)) @@ -1759,6 +1906,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 3: nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); if (!nl_bands) @@ -1784,6 +1932,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->chan_start++; if (state->split) break; + /* fall through */ default: /* add frequencies */ nl_freqs = nla_nest_start( @@ -1837,6 +1986,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 4: nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); if (!nl_cmds) @@ -1863,6 +2013,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 5: if (rdev->ops->remain_on_channel && (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && @@ -1880,6 +2031,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 6: #ifdef CONFIG_PM if (nl80211_send_wowlan(msg, rdev, state->split)) @@ -1890,6 +2042,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, #else state->split_start++; #endif + /* fall through */ case 7: if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, rdev->wiphy.software_iftypes)) @@ -1902,6 +2055,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, state->split_start++; if (state->split) break; + /* fall through */ case 8: if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, @@ -2118,6 +2272,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, goto nla_put_failure; } + state->split_start++; + break; + case 14: + if (nl80211_send_pmsr_capa(rdev, msg)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -2318,9 +2478,9 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_P2P_GO; } -static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, - struct genl_info *info, - struct cfg80211_chan_def *chandef) +int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_chan_def *chandef) { struct netlink_ext_ack *extack = info->extack; struct nlattr **attrs = info->attrs; @@ -2794,12 +2954,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) return 0; } -static inline u64 wdev_id(struct wireless_dev *wdev) -{ - return (u64)wdev->identifier | - ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32); -} - static int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *chandef) { @@ -2832,14 +2986,15 @@ static int nl80211_send_chandef(struct sk_buff *msg, static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, bool removal) + struct wireless_dev *wdev, + enum nl80211_commands cmd) { struct net_device *dev = wdev->netdev; - u8 cmd = NL80211_CMD_NEW_INTERFACE; void *hdr; - if (removal) - cmd = NL80211_CMD_DEL_INTERFACE; + WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE && + cmd != NL80211_CMD_DEL_INTERFACE && + cmd != NL80211_CMD_SET_INTERFACE); hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) @@ -2987,7 +3142,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev, false) < 0) { + rdev, wdev, + NL80211_CMD_NEW_INTERFACE) < 0) { goto out; } if_idx++; @@ -3017,7 +3173,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, - rdev, wdev, false) < 0) { + rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -3207,6 +3363,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (!err && params.use_4addr != -1) dev->ieee80211_ptr->use_4addr = params.use_4addr; + if (change && !err) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + + nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE); + } + return err; } @@ -3298,7 +3460,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) } if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, - rdev, wdev, false) < 0) { + rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -4521,8 +4683,7 @@ static int parse_station_flags(struct genl_info *info, return 0; } -static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, - int attr) +bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) { struct nlattr *rate; u32 bitrate; @@ -4731,6 +4892,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(LOCAL_PM, local_pm, u32); PUT_SINFO(PEER_PM, peer_pm, u32); PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); + PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8); if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); @@ -6122,7 +6284,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, cur_params.dot11MeshAwakeWindowDuration) || nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, - cur_params.plink_timeout)) + cur_params.plink_timeout) || + nla_put_u8(msg, NL80211_MESHCONF_CONNECTED_TO_GATE, + cur_params.dot11MeshConnectedToMeshGate)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -6179,6 +6343,7 @@ nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { NL80211_MESH_POWER_MAX), [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 }, + [NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1), }; static const struct nla_policy @@ -6290,6 +6455,9 @@ do { \ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_s32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask, + NL80211_MESHCONF_CONNECTED_TO_GATE, + nla_get_u8); /* * Check HT operation mode based on * IEEE 802.11-2016 9.4.2.57 HT Operation element. @@ -6855,8 +7023,8 @@ static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, return 0; } -static int nl80211_parse_random_mac(struct nlattr **attrs, - u8 *mac_addr, u8 *mac_addr_mask) +int nl80211_parse_random_mac(struct nlattr **attrs, + u8 *mac_addr, u8 *mac_addr_mask) { int i; @@ -7822,6 +7990,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, return err; } +static int nl80211_notify_radar_detection(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_chan_def chandef; + enum nl80211_dfs_regions dfs_region; + int err; + + dfs_region = reg_get_dfs_region(wiphy); + if (dfs_region == NL80211_DFS_UNSET) { + GENL_SET_ERR_MSG(info, + "DFS Region is not set. Unexpected Radar indication"); + return -EINVAL; + } + + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) { + GENL_SET_ERR_MSG(info, "Unable to extract chandef info"); + return err; + } + + err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype); + if (err < 0) { + GENL_SET_ERR_MSG(info, "chandef is invalid"); + return err; + } + + if (err == 0) { + GENL_SET_ERR_MSG(info, + "Unexpected Radar indication for chandef/iftype"); + return -EINVAL; + } + + /* Do not process this notification if radar is already detected + * by kernel on this channel, and return success. + */ + if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE) + return 0; + + cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_UNAVAILABLE); + + cfg80211_sched_dfs_chan_update(rdev); + + memcpy(&rdev->radar_chandef, &chandef, sizeof(chandef)); + + /* Propagate this notification to other radios as well */ + queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); + + return 0; +} + static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -13899,6 +14121,22 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_PEER_MEASUREMENT_START, + .doit = nl80211_pmsr_start, + .policy = nl80211_policy, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_NOTIFY_RADAR, + .doit = nl80211_notify_radar_detection, + .policy = nl80211_policy, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_family nl80211_fam __ro_after_init = { @@ -13946,15 +14184,11 @@ void nl80211_notify_iface(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; - WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE && - cmd != NL80211_CMD_DEL_INTERFACE); - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; - if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, - cmd == NL80211_CMD_DEL_INTERFACE) < 0) { + if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, cmd) < 0) { nlmsg_free(msg); return; } @@ -14573,7 +14807,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, } void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, - const u8* ie, u8 ie_len, gfp_t gfp) + const u8 *ie, u8 ie_len, + int sig_dbm, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -14599,7 +14834,9 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || (ie_len && ie && - nla_put(msg, NL80211_ATTR_IE, ie_len , ie))) + nla_put(msg, NL80211_ATTR_IE, ie_len, ie)) || + (sig_dbm && + nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -15882,6 +16119,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb, } else if (wdev->conn_owner_nlportid == notify->portid) { schedule_work(&wdev->disconnect_wk); } + + cfg80211_release_pmsr(wdev, notify->portid); } spin_lock_bh(&rdev->beacon_registrations_lock); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 79e47fe60c35..531c82dcba6b 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -1,4 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Portions of this file + * Copyright (C) 2018 Intel Corporation + */ #ifndef __NET_WIRELESS_NL80211_H #define __NET_WIRELESS_NL80211_H @@ -6,6 +10,30 @@ int nl80211_init(void); void nl80211_exit(void); + +extern const struct nla_policy nl80211_policy[NUM_NL80211_ATTR]; + +void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, + int flags, u8 cmd); +bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, + int attr); + +static inline u64 wdev_id(struct wireless_dev *wdev) +{ + return (u64)wdev->identifier | + ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32); +} + +int nl80211_prepare_wdev_dump(struct netlink_callback *cb, + struct cfg80211_registered_device **rdev, + struct wireless_dev **wdev); + +int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_chan_def *chandef); +int nl80211_parse_random_mac(struct nlattr **attrs, + u8 *mac_addr, u8 *mac_addr_mask); + void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, enum nl80211_commands cmd); void nl80211_notify_iface(struct cfg80211_registered_device *rdev, @@ -95,4 +123,8 @@ void nl80211_send_ap_stopped(struct wireless_dev *wdev); void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev); +/* peer measurement */ +int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info); +int nl80211_pmsr_dump_results(struct sk_buff *skb, struct netlink_callback *cb); + #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c new file mode 100644 index 000000000000..de9286703280 --- /dev/null +++ b/net/wireless/pmsr.c @@ -0,0 +1,590 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Intel Corporation + */ +#ifndef __PMSR_H +#define __PMSR_H +#include <net/cfg80211.h> +#include "core.h" +#include "nl80211.h" +#include "rdev-ops.h" + +static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, + struct nlattr *ftmreq, + struct cfg80211_pmsr_request_peer *out, + struct genl_info *info) +{ + const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa; + struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; + u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */ + + /* validate existing data */ + if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) { + NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth"); + return -EINVAL; + } + + /* no validation needed - was already done via nested policy */ + nla_parse_nested(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, NULL, NULL); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) + preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]); + + /* set up values - struct is 0-initialized */ + out->ftm.requested = true; + + switch (out->chandef.chan->band) { + case NL80211_BAND_60GHZ: + /* optional */ + break; + default: + if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) { + NL_SET_ERR_MSG(info->extack, + "FTM: must specify preamble"); + return -EINVAL; + } + } + + if (!(capa->ftm.preambles & BIT(preamble))) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], + "FTM: invalid preamble"); + return -EINVAL; + } + + out->ftm.preamble = preamble; + + out->ftm.burst_period = 0; + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) + out->ftm.burst_period = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); + + out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; + if (out->ftm.asap && !capa->ftm.asap) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP], + "FTM: ASAP mode not supported"); + return -EINVAL; + } + + if (!out->ftm.asap && !capa->ftm.non_asap) { + NL_SET_ERR_MSG(info->extack, + "FTM: non-ASAP mode not supported"); + return -EINVAL; + } + + out->ftm.num_bursts_exp = 0; + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) + out->ftm.num_bursts_exp = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); + + if (capa->ftm.max_bursts_exponent >= 0 && + out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP], + "FTM: max NUM_BURSTS_EXP must be set lower than the device limit"); + return -EINVAL; + } + + out->ftm.burst_duration = 15; + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) + out->ftm.burst_duration = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); + + out->ftm.ftms_per_burst = 0; + if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) + out->ftm.ftms_per_burst = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); + + if (capa->ftm.max_ftms_per_burst && + (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst || + out->ftm.ftms_per_burst == 0)) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], + "FTM: FTMs per burst must be set lower than the device limit but non-zero"); + return -EINVAL; + } + + out->ftm.ftmr_retries = 3; + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) + out->ftm.ftmr_retries = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); + + out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; + if (out->ftm.request_lci && !capa->ftm.request_lci) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI], + "FTM: LCI request not supported"); + } + + out->ftm.request_civicloc = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC]; + if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC], + "FTM: civic location request not supported"); + } + + return 0; +} + +static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, + struct nlattr *peer, + struct cfg80211_pmsr_request_peer *out, + struct genl_info *info) +{ + struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; + struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; + struct nlattr *treq; + int err, rem; + + /* no validation needed - was already done via nested policy */ + nla_parse_nested(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, NULL, NULL); + + if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] || + !tb[NL80211_PMSR_PEER_ATTR_CHAN] || + !tb[NL80211_PMSR_PEER_ATTR_REQ]) { + NL_SET_ERR_MSG_ATTR(info->extack, peer, + "insufficient peer data"); + return -EINVAL; + } + + memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN); + + /* reuse info->attrs */ + memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1)); + /* need to validate here, we don't want to have validation recursion */ + err = nla_parse_nested(info->attrs, NL80211_ATTR_MAX, + tb[NL80211_PMSR_PEER_ATTR_CHAN], + nl80211_policy, info->extack); + if (err) + return err; + + err = nl80211_parse_chandef(rdev, info, &out->chandef); + if (err) + return err; + + /* no validation needed - was already done via nested policy */ + nla_parse_nested(req, NL80211_PMSR_REQ_ATTR_MAX, + tb[NL80211_PMSR_PEER_ATTR_REQ], + NULL, NULL); + + if (!req[NL80211_PMSR_REQ_ATTR_DATA]) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_PEER_ATTR_REQ], + "missing request type/data"); + return -EINVAL; + } + + if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF]) + out->report_ap_tsf = true; + + if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) { + NL_SET_ERR_MSG_ATTR(info->extack, + req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF], + "reporting AP TSF is not supported"); + return -EINVAL; + } + + nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) { + switch (nla_type(treq)) { + case NL80211_PMSR_TYPE_FTM: + err = pmsr_parse_ftm(rdev, treq, out, info); + break; + default: + NL_SET_ERR_MSG_ATTR(info->extack, treq, + "unsupported measurement type"); + err = -EINVAL; + } + } + + if (err) + return err; + + return 0; +} + +int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS]; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + struct cfg80211_pmsr_request *req; + struct nlattr *peers, *peer; + int count, rem, err, idx; + + if (!rdev->wiphy.pmsr_capa) + return -EOPNOTSUPP; + + if (!reqattr) + return -EINVAL; + + peers = nla_find(nla_data(reqattr), nla_len(reqattr), + NL80211_PMSR_ATTR_PEERS); + if (!peers) + return -EINVAL; + + count = 0; + nla_for_each_nested(peer, peers, rem) { + count++; + + if (count > rdev->wiphy.pmsr_capa->max_peers) { + NL_SET_ERR_MSG_ATTR(info->extack, peer, + "Too many peers used"); + return -EINVAL; + } + } + + req = kzalloc(struct_size(req, peers, count), GFP_KERNEL); + if (!req) + return -ENOMEM; + + if (info->attrs[NL80211_ATTR_TIMEOUT]) + req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]); + + if (info->attrs[NL80211_ATTR_MAC]) { + if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[NL80211_ATTR_MAC], + "device cannot randomize MAC address"); + err = -EINVAL; + goto out_err; + } + + err = nl80211_parse_random_mac(info->attrs, req->mac_addr, + req->mac_addr_mask); + if (err) + goto out_err; + } else { + memcpy(req->mac_addr, nla_data(info->attrs[NL80211_ATTR_MAC]), + ETH_ALEN); + memset(req->mac_addr_mask, 0xff, ETH_ALEN); + } + + idx = 0; + nla_for_each_nested(peer, peers, rem) { + /* NB: this reuses info->attrs, but we no longer need it */ + err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info); + if (err) + goto out_err; + idx++; + } + + req->n_peers = count; + req->cookie = cfg80211_assign_cookie(rdev); + + err = rdev_start_pmsr(rdev, wdev, req); + if (err) + goto out_err; + + list_add_tail(&req->list, &wdev->pmsr_list); + + nl_set_extack_cookie_u64(info->extack, req->cookie); + return 0; +out_err: + kfree(req); + return err; +} + +void cfg80211_pmsr_complete(struct wireless_dev *wdev, + struct cfg80211_pmsr_request *req, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + + trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie); + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + goto free_request; + + hdr = nl80211hdr_put(msg, 0, 0, 0, + NL80211_CMD_PEER_MEASUREMENT_COMPLETE); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD)) + goto free_msg; + + if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, + NL80211_ATTR_PAD)) + goto free_msg; + + genlmsg_end(msg, hdr); + genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); + goto free_request; +free_msg: + nlmsg_free(msg); +free_request: + spin_lock_bh(&wdev->pmsr_lock); + list_del(&req->list); + spin_unlock_bh(&wdev->pmsr_lock); + kfree(req); +} +EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete); + +static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg, + struct cfg80211_pmsr_result *res) +{ + if (res->status == NL80211_PMSR_STATUS_FAILURE) { + if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + res->ftm.failure_reason)) + goto error; + + if (res->ftm.failure_reason == + NL80211_PMSR_FTM_FAILURE_PEER_BUSY && + res->ftm.busy_retry_time && + nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, + res->ftm.busy_retry_time)) + goto error; + + return 0; + } + +#define PUT(tp, attr, val) \ + do { \ + if (nla_put_##tp(msg, \ + NL80211_PMSR_FTM_RESP_ATTR_##attr, \ + res->ftm.val)) \ + goto error; \ + } while (0) + +#define PUTOPT(tp, attr, val) \ + do { \ + if (res->ftm.val##_valid) \ + PUT(tp, attr, val); \ + } while (0) + +#define PUT_U64(attr, val) \ + do { \ + if (nla_put_u64_64bit(msg, \ + NL80211_PMSR_FTM_RESP_ATTR_##attr,\ + res->ftm.val, \ + NL80211_PMSR_FTM_RESP_ATTR_PAD)) \ + goto error; \ + } while (0) + +#define PUTOPT_U64(attr, val) \ + do { \ + if (res->ftm.val##_valid) \ + PUT_U64(attr, val); \ + } while (0) + + if (res->ftm.burst_index >= 0) + PUT(u32, BURST_INDEX, burst_index); + PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts); + PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes); + PUT(u8, NUM_BURSTS_EXP, num_bursts_exp); + PUT(u8, BURST_DURATION, burst_duration); + PUT(u8, FTMS_PER_BURST, ftms_per_burst); + PUTOPT(s32, RSSI_AVG, rssi_avg); + PUTOPT(s32, RSSI_SPREAD, rssi_spread); + if (res->ftm.tx_rate_valid && + !nl80211_put_sta_rate(msg, &res->ftm.tx_rate, + NL80211_PMSR_FTM_RESP_ATTR_TX_RATE)) + goto error; + if (res->ftm.rx_rate_valid && + !nl80211_put_sta_rate(msg, &res->ftm.rx_rate, + NL80211_PMSR_FTM_RESP_ATTR_RX_RATE)) + goto error; + PUTOPT_U64(RTT_AVG, rtt_avg); + PUTOPT_U64(RTT_VARIANCE, rtt_variance); + PUTOPT_U64(RTT_SPREAD, rtt_spread); + PUTOPT_U64(DIST_AVG, dist_avg); + PUTOPT_U64(DIST_VARIANCE, dist_variance); + PUTOPT_U64(DIST_SPREAD, dist_spread); + if (res->ftm.lci && res->ftm.lci_len && + nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI, + res->ftm.lci_len, res->ftm.lci)) + goto error; + if (res->ftm.civicloc && res->ftm.civicloc_len && + nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, + res->ftm.civicloc_len, res->ftm.civicloc)) + goto error; +#undef PUT +#undef PUTOPT +#undef PUT_U64 +#undef PUTOPT_U64 + + return 0; +error: + return -ENOSPC; +} + +static int nl80211_pmsr_send_result(struct sk_buff *msg, + struct cfg80211_pmsr_result *res) +{ + struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata; + + pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS); + if (!pmsr) + goto error; + + peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS); + if (!peers) + goto error; + + peer = nla_nest_start(msg, 1); + if (!peer) + goto error; + + if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr)) + goto error; + + resp = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_RESP); + if (!resp) + goto error; + + if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) || + nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME, + res->host_time, NL80211_PMSR_RESP_ATTR_PAD)) + goto error; + + if (res->ap_tsf_valid && + nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF, + res->host_time, NL80211_PMSR_RESP_ATTR_PAD)) + goto error; + + if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL)) + goto error; + + data = nla_nest_start(msg, NL80211_PMSR_RESP_ATTR_DATA); + if (!data) + goto error; + + typedata = nla_nest_start(msg, res->type); + if (!typedata) + goto error; + + switch (res->type) { + case NL80211_PMSR_TYPE_FTM: + if (nl80211_pmsr_send_ftm_res(msg, res)) + goto error; + break; + default: + WARN_ON(1); + } + + nla_nest_end(msg, typedata); + nla_nest_end(msg, data); + nla_nest_end(msg, resp); + nla_nest_end(msg, peer); + nla_nest_end(msg, peers); + nla_nest_end(msg, pmsr); + + return 0; +error: + return -ENOSPC; +} + +void cfg80211_pmsr_report(struct wireless_dev *wdev, + struct cfg80211_pmsr_request *req, + struct cfg80211_pmsr_result *result, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie, + result->addr); + + /* + * Currently, only variable items are LCI and civic location, + * both of which are reasonably short so we don't need to + * worry about them here for the allocation. + */ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT); + if (!hdr) + goto free; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), + NL80211_ATTR_PAD)) + goto free; + + if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, + NL80211_ATTR_PAD)) + goto free; + + err = nl80211_pmsr_send_result(msg, result); + if (err) { + pr_err_ratelimited("peer measurement result: message didn't fit!"); + goto free; + } + + genlmsg_end(msg, hdr); + genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); + return; +free: + nlmsg_free(msg); +} +EXPORT_SYMBOL_GPL(cfg80211_pmsr_report); + +void cfg80211_pmsr_free_wk(struct work_struct *work) +{ + struct wireless_dev *wdev = container_of(work, struct wireless_dev, + pmsr_free_wk); + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct cfg80211_pmsr_request *req, *tmp; + LIST_HEAD(free_list); + + spin_lock_bh(&wdev->pmsr_lock); + list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) { + if (req->nl_portid) + continue; + list_move_tail(&req->list, &free_list); + } + spin_unlock_bh(&wdev->pmsr_lock); + + list_for_each_entry_safe(req, tmp, &free_list, list) { + wdev_lock(wdev); + rdev_abort_pmsr(rdev, wdev, req); + wdev_unlock(wdev); + + kfree(req); + } +} + +void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev) +{ + struct cfg80211_pmsr_request *req; + bool found = false; + + spin_lock_bh(&wdev->pmsr_lock); + list_for_each_entry(req, &wdev->pmsr_list, list) { + found = true; + req->nl_portid = 0; + } + spin_unlock_bh(&wdev->pmsr_lock); + + if (found) + schedule_work(&wdev->pmsr_free_wk); + flush_work(&wdev->pmsr_free_wk); + WARN_ON(!list_empty(&wdev->pmsr_list)); +} + +void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) +{ + struct cfg80211_pmsr_request *req; + + spin_lock_bh(&wdev->pmsr_lock); + list_for_each_entry(req, &wdev->pmsr_list, list) { + if (req->nl_portid == portid) { + req->nl_portid = 0; + schedule_work(&wdev->pmsr_free_wk); + } + } + spin_unlock_bh(&wdev->pmsr_lock); +} + +#endif /* __PMSR_H */ diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 51380b5c32f2..5cb48d135fab 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1247,4 +1247,29 @@ rdev_get_ftm_responder_stats(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_start_pmsr(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_pmsr_request *request) +{ + int ret = -EOPNOTSUPP; + + trace_rdev_start_pmsr(&rdev->wiphy, wdev, request->cookie); + if (rdev->ops->start_pmsr) + ret = rdev->ops->start_pmsr(&rdev->wiphy, wdev, request); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void +rdev_abort_pmsr(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_pmsr_request *request) +{ + trace_rdev_abort_pmsr(&rdev->wiphy, wdev, request->cookie); + if (rdev->ops->abort_pmsr) + rdev->ops->abort_pmsr(&rdev->wiphy, wdev, request); + trace_rdev_return_void(&rdev->wiphy); +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d0e7472dd9fd..5123667f4569 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1183,7 +1183,7 @@ cfg80211_inform_bss_data(struct wiphy *wiphy, switch (ftype) { case CFG80211_BSS_FTYPE_BEACON: ies->from_beacon = true; - /* fall through to assign */ + /* fall through */ case CFG80211_BSS_FTYPE_UNKNOWN: rcu_assign_pointer(tmp.pub.beacon_ies, ies); break; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index c6a9446b4e6b..44b2ce1bb13a 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -361,6 +361,24 @@ DECLARE_EVENT_CLASS(wiphy_wdev_evt, TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) ); +DECLARE_EVENT_CLASS(wiphy_wdev_cookie_evt, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u64, cookie) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->cookie = cookie; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %lld", + WIPHY_PR_ARG, WDEV_PR_ARG, + (unsigned long long)__entry->cookie) +); + DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), TP_ARGS(wiphy, wdev) @@ -770,9 +788,9 @@ DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer, ); TRACE_EVENT(rdev_dump_station, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx, u8 *mac), - TP_ARGS(wiphy, netdev, idx, mac), + TP_ARGS(wiphy, netdev, _idx, mac), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -783,7 +801,7 @@ TRACE_EVENT(rdev_dump_station, WIPHY_ASSIGN; NETDEV_ASSIGN; MAC_ASSIGN(sta_mac, mac); - __entry->idx = idx; + __entry->idx = _idx; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), @@ -847,9 +865,9 @@ DEFINE_EVENT(mpath_evt, rdev_get_mpath, ); TRACE_EVENT(rdev_dump_mpath, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx, u8 *dst, u8 *next_hop), - TP_ARGS(wiphy, netdev, idx, dst, next_hop), + TP_ARGS(wiphy, netdev, _idx, dst, next_hop), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -862,7 +880,7 @@ TRACE_EVENT(rdev_dump_mpath, NETDEV_ASSIGN; MAC_ASSIGN(dst, dst); MAC_ASSIGN(next_hop, next_hop); - __entry->idx = idx; + __entry->idx = _idx; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, @@ -892,9 +910,9 @@ TRACE_EVENT(rdev_get_mpp, ); TRACE_EVENT(rdev_dump_mpp, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx, u8 *dst, u8 *mpp), - TP_ARGS(wiphy, netdev, idx, mpp, dst), + TP_ARGS(wiphy, netdev, _idx, mpp, dst), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -907,7 +925,7 @@ TRACE_EVENT(rdev_dump_mpp, NETDEV_ASSIGN; MAC_ASSIGN(dst, dst); MAC_ASSIGN(mpp, mpp); - __entry->idx = idx; + __entry->idx = _idx; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " MAC_PR_FMT ", mpp: " MAC_PR_FMT, @@ -1673,8 +1691,8 @@ TRACE_EVENT(rdev_tdls_mgmt, ); TRACE_EVENT(rdev_dump_survey, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx), - TP_ARGS(wiphy, netdev, idx), + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx), + TP_ARGS(wiphy, netdev, _idx), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -1683,7 +1701,7 @@ TRACE_EVENT(rdev_dump_survey, TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; - __entry->idx = idx; + __entry->idx = _idx; ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx) @@ -2502,6 +2520,16 @@ TRACE_EVENT(rdev_get_ftm_responder_stats, __entry->out_of_window) ); +DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_start_pmsr, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie) +); + +DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_abort_pmsr, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ @@ -3294,6 +3322,46 @@ TRACE_EVENT(cfg80211_stop_iface, TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) ); + +TRACE_EVENT(cfg80211_pmsr_report, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + u64 cookie, const u8 *addr), + TP_ARGS(wiphy, wdev, cookie, addr), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u64, cookie) + MAC_ENTRY(addr) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->cookie = cookie; + MAC_ASSIGN(addr, addr); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld, " MAC_PR_FMT, + WIPHY_PR_ARG, WDEV_PR_ARG, + (unsigned long long)__entry->cookie, + MAC_PR_ARG(addr)) +); + +TRACE_EVENT(cfg80211_pmsr_complete, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), + TP_ARGS(wiphy, wdev, cookie), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(u64, cookie) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->cookie = cookie; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld", + WIPHY_PR_ARG, WDEV_PR_ARG, + (unsigned long long)__entry->cookie) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/util.c b/net/wireless/util.c index d473bd135da8..cd48cdd582c0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2015,33 +2015,32 @@ int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap, case IEEE80211_VHT_CHANWIDTH_160MHZ: if (supp_width == 0 && (ext_nss_bw == 1 || ext_nss_bw == 2)) - return DIV_ROUND_UP(max_vht_nss, 2); + return max_vht_nss / 2; if (supp_width == 0 && ext_nss_bw == 3) - return DIV_ROUND_UP(3 * max_vht_nss, 4); + return (3 * max_vht_nss) / 4; if (supp_width == 1 && ext_nss_bw == 3) return 2 * max_vht_nss; break; case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - if (supp_width == 0 && - (ext_nss_bw == 1 || ext_nss_bw == 2)) + if (supp_width == 0 && ext_nss_bw == 1) return 0; /* not possible */ if (supp_width == 0 && ext_nss_bw == 2) - return DIV_ROUND_UP(max_vht_nss, 2); + return max_vht_nss / 2; if (supp_width == 0 && ext_nss_bw == 3) - return DIV_ROUND_UP(3 * max_vht_nss, 4); + return (3 * max_vht_nss) / 4; if (supp_width == 1 && ext_nss_bw == 0) return 0; /* not possible */ if (supp_width == 1 && ext_nss_bw == 1) - return DIV_ROUND_UP(max_vht_nss, 2); + return max_vht_nss / 2; if (supp_width == 1 && ext_nss_bw == 2) - return DIV_ROUND_UP(3 * max_vht_nss, 4); + return (3 * max_vht_nss) / 4; break; } |