* @uhr_cap: UHR capabilities of this STA
* @s1g_cap: S1G capabilities of this STA
* @agg: per-link data for multi-link aggregation
- * @bandwidth: current bandwidth the station can receive with
+ * @bandwidth: current bandwidth the station can receive with.
+ * This is the minimum between the peer's capabilities and our own
+ * operating channel width; Invalid for NAN since that is operating on
+ * multiple channels.
* @rx_nss: in HT/VHT, the maximum number of spatial streams the
* station can receive at the moment, changed by operating mode
* notifications and capabilities. The value is only valid after
- * the station moves to associated state.
+ * the station moves to associated state. Invalid for NAN since it
+ * operates on multiple configurations of rx_nss.
* @txpwr: the station tx power configuration
*
*/
* @valid_links: bitmap of valid links, or 0 for non-MLO
* @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not.
* @epp_peer: indicates that the peer is an EPP peer.
+ * @nmi: For NDI stations, pointer to the NMI station of the peer.
*/
struct ieee80211_sta {
u8 addr[ETH_ALEN] __aligned(2);
struct ieee80211_link_sta deflink;
struct ieee80211_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
+ struct ieee80211_sta __rcu *nmi;
+
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
* station has a unique address, i.e. each station entry can be identified
* by just its MAC address; this prevents, for example, the same station
* from connecting to two virtual AP interfaces at the same time.
+ * Note that this doesn't apply for NAN, in which the peer's NMI address
+ * can be equal to its NDI address.
*
* @IEEE80211_HW_SUPPORTS_REORDERING_BUFFER: Hardware (or driver) manages the
* reordering buffer internally, guaranteeing mac80211 receives frames in
enum sta_link_apply_mode mode,
struct link_station_parameters *params)
{
- struct ieee80211_supported_band *sband;
+ struct ieee80211_supported_band *sband = NULL;
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 link_id = params->link_id < 0 ? 0 : params->link_id;
struct ieee80211_link_data *link =
struct link_sta_info *link_sta =
rcu_dereference_protected(sta->link[link_id],
lockdep_is_held(&local->hw.wiphy->mtx));
+ const struct ieee80211_sta_ht_cap *own_ht_cap;
+ const struct ieee80211_sta_vht_cap *own_vht_cap;
+ const struct ieee80211_sta_he_cap *own_he_cap;
bool changes = params->link_mac ||
params->txpwr_set ||
params->supported_rates_len ||
if (!link || !link_sta)
return -EINVAL;
- sband = ieee80211_get_link_sband(link);
- if (!sband)
+ /*
+ * We should not have any changes in NDI station, its capabilities are
+ * copied from the NMI sta
+ */
+ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
return -EINVAL;
+ if (sdata->vif.type == NL80211_IFTYPE_NAN) {
+ own_ht_cap = &local->hw.wiphy->nan_capa.phy.ht;
+ own_vht_cap = &local->hw.wiphy->nan_capa.phy.vht;
+ own_he_cap = &local->hw.wiphy->nan_capa.phy.he;
+ } else {
+ sband = ieee80211_get_link_sband(link);
+ if (!sband)
+ return -EINVAL;
+
+ own_ht_cap = &sband->ht_cap;
+ own_vht_cap = &sband->vht_cap;
+ own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+ }
+
if (params->link_mac) {
if (mode == STA_LINK_MODE_NEW) {
memcpy(link_sta->addr, params->link_mac, ETH_ALEN);
return ret;
}
+ if (sdata->vif.type == NL80211_IFTYPE_NAN) {
+ static const u8 all_ofdm_rates[] = {
+ 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
+ };
+
+ /* Set the same supported_rates for all bands */
+ for (int i = 0; i < NUM_NL80211_BANDS; i++) {
+ struct ieee80211_supported_band *tmp =
+ sdata->local->hw.wiphy->bands[i];
+
+ if ((i != NL80211_BAND_2GHZ && i != NL80211_BAND_5GHZ) ||
+ !tmp)
+ continue;
+
+ if (!ieee80211_parse_bitrates(tmp, all_ofdm_rates,
+ sizeof(all_ofdm_rates),
+ &link_sta->pub->supp_rates[i]))
+ return -EINVAL;
+ }
+ }
+
if (params->supported_rates &&
params->supported_rates_len &&
!ieee80211_parse_bitrates(sband, params->supported_rates,
return -EINVAL;
if (params->ht_capa)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, own_ht_cap,
params->ht_capa, link_sta);
/* VHT can override some HT caps such as the A-MSDU max length */
if (params->vht_capa)
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
- &sband->vht_cap,
+ own_vht_cap,
params->vht_capa, NULL,
link_sta);
if (params->he_capa)
- ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
- (void *)params->he_capa,
- params->he_capa_len,
- (void *)params->he_6ghz_capa,
- link_sta);
+ _ieee80211_he_cap_ie_to_sta_he_cap(sdata,
+ own_he_cap,
+ (void *)params->he_capa,
+ params->he_capa_len,
+ (sband && sband->band == NL80211_BAND_6GHZ) ?
+ (void *)params->he_6ghz_capa : NULL,
+ link_sta);
if (params->he_capa && params->eht_capa)
ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
if (params->airtime_weight)
sta->airtime_weight = params->airtime_weight;
+ if (params->nmi_mac) {
+ struct ieee80211_sub_if_data *nmi =
+ rcu_dereference_wiphy(local->hw.wiphy,
+ sdata->u.nan_data.nmi);
+ struct sta_info *nmi_sta;
+
+ if (WARN_ON(!nmi))
+ return -EINVAL;
+
+ nmi_sta = sta_info_get(nmi, params->nmi_mac);
+ if (!nmi_sta)
+ return -ENOENT;
+ rcu_assign_pointer(sta->sta.nmi, &nmi_sta->sta);
+
+ /* For NAN_DATA stations, copy capabilities from the NMI station */
+ if (!nmi_sta->deflink.pub->ht_cap.ht_supported)
+ return -EINVAL;
+
+ sta->deflink.pub->ht_cap = nmi_sta->deflink.pub->ht_cap;
+ sta->deflink.pub->vht_cap = nmi_sta->deflink.pub->vht_cap;
+ sta->deflink.pub->he_cap = nmi_sta->deflink.pub->he_cap;
+ memcpy(&sta->deflink.pub->supp_rates,
+ &nmi_sta->deflink.pub->supp_rates,
+ sizeof(sta->deflink.pub->supp_rates));
+ }
+
/* set the STA state after all sta info from usermode has been set */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
else
statype = CFG80211_STA_AP_CLIENT_UNASSOC;
break;
+ case NL80211_IFTYPE_NAN:
+ statype = CFG80211_STA_NAN_MGMT;
+ break;
+ case NL80211_IFTYPE_NAN_DATA:
+ statype = CFG80211_STA_NAN_DATA;
+ break;
default:
return -EOPNOTSUPP;
}
}
}
+ /* NAN capabilties should not change */
+ if (statype == CFG80211_STA_NAN_DATA &&
+ sta->deflink.pub->ht_cap.ht_supported &&
+ (params->link_sta_params.ht_capa ||
+ params->link_sta_params.vht_capa ||
+ params->link_sta_params.he_capa))
+ return -EINVAL;
+
err = sta_apply_parameters(local, sta, params);
if (err)
return err;
if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he)
return;
+ /* NDI station are using the capabilities from the NMI station */
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
+ return;
+
own_he_cap = *own_he_cap_ptr;
/* Make sure size is OK */
he_cap->has_he = true;
link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
- link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
+ if (sdata->vif.type != NL80211_IFTYPE_NAN)
+ link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
if (he_6ghz_capa)
ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
if (!ht_cap_ie || !own_cap_ptr->ht_supported)
goto apply;
+ /* NDI station are using the capabilities from the NMI station */
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
+ return 0;
+
ht_cap.ht_supported = true;
own_cap = *own_cap_ptr;
rcu_read_lock();
link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]);
- if (WARN_ON(!link_conf))
+ if (WARN_ON(!link_conf)) {
width = NL80211_CHAN_WIDTH_20_NOHT;
- else
+ } else if (sdata->vif.type == NL80211_IFTYPE_NAN ||
+ sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
+ /* In NAN, link_sta->bandwidth is invalid since NAN operates on
+ * multiple channels. Just take the maximum.
+ */
+ width = NL80211_CHAN_WIDTH_320;
+ } else {
width = link_conf->chanreq.oper.width;
+ }
switch (width) {
default:
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
- sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sta->sdata->vif.type == NL80211_IFTYPE_NAN ||
+ sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
enum ieee80211_smps_mode smps_mode;
switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
* (because if we remove a STA after ops->remove_interface()
* the driver will have removed the vif info already!)
*
- * For AP_VLANs stations may exist since there's nothing else that
- * would have removed them, but in other modes there shouldn't
- * be any stations.
+ * For AP_VLANs, NAN and NAN_DATA stations may exist since there's
+ * nothing else that would have removed them, but in other modes there
+ * shouldn't be any stations.
*/
flushed = sta_info_flush(sdata, -1);
- WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0);
+ WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_NAN &&
+ sdata->vif.type != NL80211_IFTYPE_NAN_DATA && flushed > 0);
/* don't count this interface for allmulti while it is down */
if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
static int sta_info_insert_check(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_sta *same_addr_sta;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
!is_valid_ether_addr(sta->sta.addr)))
return -EINVAL;
+ if (!ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR))
+ return 0;
+
/* The RCU read lock is required by rhashtable due to
* asynchronous resize/rehash. We also require the mutex
* for correctness.
*/
rcu_read_lock();
- if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) &&
- ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) {
+ same_addr_sta = ieee80211_find_sta_by_ifaddr(&sdata->local->hw,
+ sta->addr, NULL);
+ /* For NAN, a peer can re-use */
+ if (same_addr_sta && same_addr_sta != rcu_access_pointer(sta->sta.nmi)) {
rcu_read_unlock();
return -ENOTUNIQ;
}
lockdep_assert_wiphy(local->hw.wiphy);
+ if (sdata->vif.type == NL80211_IFTYPE_NAN) {
+ struct sta_info *sta_iter, *tmp;
+
+ /* Remove all NDI stations associated with this NMI STA */
+ list_for_each_entry_safe(sta_iter, tmp, &local->sta_list, list) {
+ if (rcu_access_pointer(sta_iter->sta.nmi) != &sta->sta)
+ continue;
+ sta_info_destroy_addr(sta_iter->sdata, sta_iter->addr);
+ }
+ }
+
/*
* Before removing the station from the driver and
* rate control, it might still start new aggregation
* @status_stats.ack_signal_filled: last ACK signal validity
* @status_stats.avg_ack_signal: average ACK signal
* @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
- * taken from HT/VHT capabilities or VHT operating mode notification
+ * taken from HT/VHT capabilities or VHT operating mode notification.
+ * Invalid for NAN since that is operating on multiple bands.
* @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX
* @rx_omi_bw_tx: RX OMI bandwidth restriction to apply for TX
* @rx_omi_bw_staging: RX OMI bandwidth restriction to apply later
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *ndi_sdata;
+ struct sta_info *sta;
int res;
res = drv_start_nan(local, sdata, &sdata->u.nan.conf);
}
}
+ /* Add NMI stations (stations on the NAN interface) */
+ list_for_each_entry(sta, &local->sta_list, list) {
+ enum ieee80211_sta_state state;
+
+ if (!sta->uploaded || sta->sdata != sdata)
+ continue;
+
+ for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state;
+ state++) {
+ res = drv_sta_state(local, sdata, sta, state,
+ state + 1);
+ if (WARN_ON(res))
+ return res;
+ }
+ }
+
+ /* Add NDI stations (stations on NAN_DATA interfaces) */
+ list_for_each_entry(sta, &local->sta_list, list) {
+ enum ieee80211_sta_state state;
+
+ if (!sta->uploaded ||
+ sta->sdata->vif.type != NL80211_IFTYPE_NAN_DATA)
+ continue;
+
+ if (WARN_ON(!sta->sta.nmi))
+ continue;
+
+ for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state;
+ state++) {
+ res = drv_sta_state(local, sta->sdata, sta, state,
+ state + 1);
+ if (WARN_ON(res))
+ return res;
+ }
+ }
+
return 0;
}
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
break;
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
+ /* NAN stations are handled later */
+ break;
case NL80211_IFTYPE_ADHOC:
if (sdata->vif.cfg.ibss_joined)
WARN_ON(drv_join_ibss(local, sdata));
if (!vht_cap_ie || !own_vht_cap->vht_supported)
return;
+ /* NDI station are using the capabilities from the NMI station */
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
+ return;
+
if (sband) {
/* Allow VHT if at least one channel on the sband supports 80 MHz */
bool have_80mhz = false;
IEEE80211_STA_RX_BW_160;
}
- link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
+ if (sdata->vif.type != NL80211_IFTYPE_NAN)
+ link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
/*
* Work around the Cisco 9115 FW 17.3 bug by taking the min of
} else {
struct ieee80211_bss_conf *link_conf;
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA ||
+ sdata->vif.type == NL80211_IFTYPE_NAN))
+ return IEEE80211_STA_RX_BW_20;
+
rcu_read_lock();
link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
band = link_conf->chanreq.oper.chan->band;
} else {
struct ieee80211_bss_conf *link_conf;
+ /* NAN operates on multiple channels so a chandef must be given */
+ if (WARN_ON_ONCE(sta->sdata->vif.type == NL80211_IFTYPE_NAN ||
+ sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
+ return IEEE80211_STA_RX_BW_20;
+
rcu_read_lock();
link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]);
if (WARN_ON_ONCE(!link_conf)) {