diff options
Diffstat (limited to 'drivers/net/wireless/mac80211_hwsim.c')
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 523 |
1 files changed, 405 insertions, 118 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 6f83af849f2e..6e55f153ff26 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -65,6 +65,10 @@ static bool support_p2p_device = true; module_param(support_p2p_device, bool, 0444); MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type"); +static bool mlo; +module_param(mlo, bool, 0444); +MODULE_PARM_DESC(mlo, "Support MLO"); + /** * enum hwsim_regtest - the type of regulatory tests we offer * @@ -224,6 +228,7 @@ static inline void hwsim_clear_magic(struct ieee80211_vif *vif) struct hwsim_sta_priv { u32 magic; + unsigned int last_link; }; #define HWSIM_STA_MAGIC 0x6d537749 @@ -290,8 +295,7 @@ static inline int hwsim_net_set_netgroup(struct net *net) { struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); - hwsim_net->netgroup = ida_simple_get(&hwsim_netgroup_ida, - 0, 0, GFP_KERNEL); + hwsim_net->netgroup = ida_alloc(&hwsim_netgroup_ida, GFP_KERNEL); return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM; } @@ -624,6 +628,12 @@ static struct platform_driver mac80211_hwsim_driver = { }, }; +struct mac80211_hwsim_link_data { + u32 link_id; + u64 beacon_int /* beacon interval in us */; + struct hrtimer beacon_timer; +}; + struct mac80211_hwsim_data { struct list_head list; struct rhash_head rht; @@ -669,18 +679,16 @@ struct mac80211_hwsim_data { struct ieee80211_channel *channel; enum nl80211_chan_width bw; - u64 beacon_int /* beacon interval in us */; unsigned int rx_filter; bool started, idle, scanning; struct mutex mutex; - struct hrtimer beacon_timer; enum ps_mode { PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL } ps; bool ps_poll_pending; struct dentry *debugfs; - uintptr_t pending_cookie; + atomic_t pending_cookie; struct sk_buff_head pending; /* packets pending */ /* * Only radios in the same group can communicate together (the @@ -710,6 +718,8 @@ struct mac80211_hwsim_data { /* RSSI in rx status of the receiver */ int rx_rssi; + + struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; }; static const struct rhashtable_params hwsim_rht_params = { @@ -777,6 +787,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_PERM_ADDR] = NLA_POLICY_ETH_ADDR_COMPAT, [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 }, [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY }, + [HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, }; #if IS_REACHABLE(CONFIG_VIRTIO) @@ -889,7 +900,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) rcu_read_lock(); mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); rcu_read_unlock(); } @@ -922,7 +933,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, rcu_read_lock(); mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); rcu_read_unlock(); } @@ -1072,7 +1083,8 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; u64 now = mac80211_hwsim_get_tsf(hw, vif); - u32 bcn_int = data->beacon_int; + /* MLD not supported here */ + u32 bcn_int = data->link_data[0].beacon_int; u64 delta = abs(tsf - now); /* adjust after beaconing with new timestamp at old TBTT */ @@ -1187,10 +1199,27 @@ struct mac80211_hwsim_addr_match_data { static void mac80211_hwsim_addr_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + int i; struct mac80211_hwsim_addr_match_data *md = data; - if (memcmp(mac, md->addr, ETH_ALEN) == 0) + if (memcmp(mac, md->addr, ETH_ALEN) == 0) { md->ret = true; + return; + } + + /* Match the link address */ + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *conf; + + conf = rcu_dereference(vif->link_conf[i]); + if (!conf) + continue; + + if (memcmp(conf->addr, md->addr, ETH_ALEN) == 0) { + md->ret = true; + return; + } + } } static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, @@ -1416,8 +1445,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, goto nla_put_failure; /* We create a cookie to identify this skb */ - data->pending_cookie++; - cookie = data->pending_cookie; + cookie = atomic_inc_return(&data->pending_cookie); info->rate_driver_data[0] = (void *)cookie; if (nla_put_u64_64bit(skb, HWSIM_ATTR_COOKIE, cookie, HWSIM_ATTR_PAD)) goto nla_put_failure; @@ -1464,15 +1492,26 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, struct ieee80211_vif *vif) { struct tx_iter_data *data = _data; + int i; - if (!vif->chanctx_conf) - return; + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *conf; + struct ieee80211_chanctx_conf *chanctx; - if (!hwsim_chans_compat(data->channel, - rcu_dereference(vif->chanctx_conf)->def.chan)) - return; + conf = rcu_dereference(vif->link_conf[i]); + if (!conf) + continue; + + chanctx = rcu_dereference(conf->chanctx_conf); + if (!chanctx) + continue; + + if (!hwsim_chans_compat(data->channel, chanctx->def.chan)) + continue; - data->receive = true; + data->receive = true; + return; + } } static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb) @@ -1662,6 +1701,51 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, return ack; } +static struct ieee80211_bss_conf * +mac80211_hwsim_select_tx_link(struct mac80211_hwsim_data *data, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_link_sta **link_sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + int i; + + if (!vif->valid_links) + return &vif->bss_conf; + + /* FIXME: handle multicast TX properly */ + if (is_multicast_ether_addr(hdr->addr1) || WARN_ON_ONCE(!sta)) { + unsigned int first_link = ffs(vif->valid_links) - 1; + + return rcu_dereference(vif->link_conf[first_link]); + } + + if (WARN_ON_ONCE(!sta->valid_links)) + return &vif->bss_conf; + + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *bss_conf; + unsigned int link_id; + + /* round-robin the available link IDs */ + link_id = (sp->last_link + i + 1) % ARRAY_SIZE(vif->link_conf); + + *link_sta = rcu_dereference(sta->link[link_id]); + if (!*link_sta) + continue; + + bss_conf = rcu_dereference(vif->link_conf[link_id]); + if (WARN_ON_ONCE(!bss_conf)) + continue; + + sp->last_link = link_id; + return bss_conf; + } + + return NULL; +} + static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -1687,7 +1771,47 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, } else if (txi->hw_queue == 4) { channel = data->tmp_chan; } else { - chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf); + u8 link = u32_get_bits(IEEE80211_SKB_CB(skb)->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + struct ieee80211_vif *vif = txi->control.vif; + struct ieee80211_link_sta *link_sta = NULL; + struct ieee80211_sta *sta = control->sta; + struct ieee80211_bss_conf *bss_conf; + + if (link != IEEE80211_LINK_UNSPECIFIED) { + bss_conf = rcu_dereference(txi->control.vif->link_conf[link]); + if (sta) + link_sta = rcu_dereference(sta->link[link]); + } else { + bss_conf = mac80211_hwsim_select_tx_link(data, vif, sta, + hdr, &link_sta); + } + + if (WARN_ON(!bss_conf)) { + ieee80211_free_txskb(hw, skb); + return; + } + + if (sta && sta->mlo) { + if (WARN_ON(!link_sta)) { + ieee80211_free_txskb(hw, skb); + return; + } + /* address translation to link addresses on TX */ + ether_addr_copy(hdr->addr1, link_sta->addr); + ether_addr_copy(hdr->addr2, bss_conf->addr); + /* translate A3 only if it's the BSSID */ + if (!ieee80211_has_tods(hdr->frame_control) && + !ieee80211_has_fromds(hdr->frame_control)) { + if (ether_addr_equal(hdr->addr3, sta->addr)) + ether_addr_copy(hdr->addr3, link_sta->addr); + else if (ether_addr_equal(hdr->addr3, vif->addr)) + ether_addr_copy(hdr->addr3, bss_conf->addr); + } + /* no need to look at A4, if present it's SA */ + } + + chanctx_conf = rcu_dereference(bss_conf->chanctx_conf); if (chanctx_conf) { channel = chanctx_conf->def.chan; confbw = chanctx_conf->def.width; @@ -1795,9 +1919,12 @@ static int mac80211_hwsim_start(struct ieee80211_hw *hw) static void mac80211_hwsim_stop(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *data = hw->priv; + int i; data->started = false; - hrtimer_cancel(&data->beacon_timer); + + for (i = 0; i < ARRAY_SIZE(data->link_data); i++) + hrtimer_cancel(&data->link_data[i].beacon_timer); while (!skb_queue_empty(&data->pending)) ieee80211_free_txskb(hw, skb_dequeue(&data->pending)); @@ -1888,7 +2015,12 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, struct ieee80211_vif *vif) { - struct mac80211_hwsim_data *data = arg; + struct mac80211_hwsim_link_data *link_data = arg; + u32 link_id = link_data->link_id; + struct ieee80211_bss_conf *link_conf; + struct mac80211_hwsim_data *data = + container_of(link_data, struct mac80211_hwsim_data, + link_data[link_id]); struct ieee80211_hw *hw = data->hw; struct ieee80211_tx_info *info; struct ieee80211_rate *txrate; @@ -1899,13 +2031,17 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, hwsim_check_magic(vif); + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + return; + if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_ADHOC && vif->type != NL80211_IFTYPE_OCB) return; - skb = ieee80211_beacon_get(hw, vif); + skb = ieee80211_beacon_get(hw, vif, link_data->link_id); if (skb == NULL) return; info = IEEE80211_SKB_CB(skb); @@ -1936,38 +2072,41 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, } mac80211_hwsim_tx_frame(hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_dereference(link_conf->chanctx_conf)->def.chan); while ((skb = ieee80211_get_buffered_bc(hw, vif)) != NULL) { mac80211_hwsim_tx_frame(hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_dereference(link_conf->chanctx_conf)->def.chan); } - if (vif->csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) + if (link_conf->csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) ieee80211_csa_finish(vif); } static enum hrtimer_restart mac80211_hwsim_beacon(struct hrtimer *timer) { + struct mac80211_hwsim_link_data *link_data = + container_of(timer, struct mac80211_hwsim_link_data, beacon_timer); struct mac80211_hwsim_data *data = - container_of(timer, struct mac80211_hwsim_data, beacon_timer); + container_of(link_data, struct mac80211_hwsim_data, + link_data[link_data->link_id]); struct ieee80211_hw *hw = data->hw; - u64 bcn_int = data->beacon_int; + u64 bcn_int = link_data->beacon_int; if (!data->started) return HRTIMER_NORESTART; ieee80211_iterate_active_interfaces_atomic( hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_beacon_tx, data); + mac80211_hwsim_beacon_tx, link_data); /* beacon at new TBTT + beacon interval */ if (data->bcn_delta) { bcn_int -= data->bcn_delta; data->bcn_delta = 0; } - hrtimer_forward_now(&data->beacon_timer, + hrtimer_forward_now(&link_data->beacon_timer, ns_to_ktime(bcn_int * NSEC_PER_USEC)); return HRTIMER_RESTART; } @@ -2051,16 +2190,21 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) } mutex_unlock(&data->mutex); - if (!data->started || !data->beacon_int) - hrtimer_cancel(&data->beacon_timer); - else if (!hrtimer_is_queued(&data->beacon_timer)) { - u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); - u32 bcn_int = data->beacon_int; - u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); + for (idx = 0; idx < ARRAY_SIZE(data->link_data); idx++) { + struct mac80211_hwsim_link_data *link_data = + &data->link_data[idx]; + + if (!data->started || !link_data->beacon_int) { + hrtimer_cancel(&link_data->beacon_timer); + } else if (!hrtimer_is_queued(&link_data->beacon_timer)) { + u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); + u32 bcn_int = link_data->beacon_int; + u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); - hrtimer_start(&data->beacon_timer, - ns_to_ktime(until_tbtt * NSEC_PER_USEC), - HRTIMER_MODE_REL_SOFT); + hrtimer_start(&link_data->beacon_timer, + ns_to_ktime(until_tbtt * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + } } return 0; @@ -2094,47 +2238,61 @@ static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac, (*count)++; } -static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, +static void mac80211_hwsim_vif_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) + u64 changed) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct mac80211_hwsim_data *data = hw->priv; hwsim_check_magic(vif); - wiphy_dbg(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n", + wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM)\n", __func__, changed, vif->addr); + if (changed & BSS_CHANGED_ASSOC) { + wiphy_dbg(hw->wiphy, " ASSOC: assoc=%d aid=%d\n", + vif->cfg.assoc, vif->cfg.aid); + vp->assoc = vif->cfg.assoc; + vp->aid = vif->cfg.aid; + } +} + +static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct mac80211_hwsim_data *data = hw->priv; + unsigned int link_id = info->link_id; + struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id]; + + hwsim_check_magic(vif); + + wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM, link id %u)\n", + __func__, (unsigned long long)changed, vif->addr, link_id); + if (changed & BSS_CHANGED_BSSID) { wiphy_dbg(hw->wiphy, "%s: BSSID changed: %pM\n", __func__, info->bssid); memcpy(vp->bssid, info->bssid, ETH_ALEN); } - if (changed & BSS_CHANGED_ASSOC) { - wiphy_dbg(hw->wiphy, " ASSOC: assoc=%d aid=%d\n", - info->assoc, info->aid); - vp->assoc = info->assoc; - vp->aid = info->aid; - } - if (changed & BSS_CHANGED_BEACON_ENABLED) { wiphy_dbg(hw->wiphy, " BCN EN: %d (BI=%u)\n", info->enable_beacon, info->beacon_int); vp->bcn_en = info->enable_beacon; if (data->started && - !hrtimer_is_queued(&data->beacon_timer) && + !hrtimer_is_queued(&link_data->beacon_timer) && info->enable_beacon) { u64 tsf, until_tbtt; u32 bcn_int; - data->beacon_int = info->beacon_int * 1024; + link_data->beacon_int = info->beacon_int * 1024; tsf = mac80211_hwsim_get_tsf(hw, vif); - bcn_int = data->beacon_int; + bcn_int = link_data->beacon_int; until_tbtt = bcn_int - do_div(tsf, bcn_int); - hrtimer_start(&data->beacon_timer, + hrtimer_start(&link_data->beacon_timer, ns_to_ktime(until_tbtt * NSEC_PER_USEC), HRTIMER_MODE_REL_SOFT); } else if (!info->enable_beacon) { @@ -2145,8 +2303,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, wiphy_dbg(hw->wiphy, " beaconing vifs remaining: %u", count); if (count == 0) { - hrtimer_cancel(&data->beacon_timer); - data->beacon_int = 0; + hrtimer_cancel(&link_data->beacon_timer); + link_data->beacon_int = 0; } } } @@ -2187,35 +2345,56 @@ mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; u32 bw = U32_MAX; - enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + int link_id; + + rcu_read_lock(); + for (link_id = 0; + link_id < ARRAY_SIZE(vif->link_conf); + link_id++) { + enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + struct ieee80211_bss_conf *vif_conf; + struct ieee80211_link_sta *link_sta; - switch (sta->deflink.bandwidth) { + link_sta = rcu_dereference(sta->link[link_id]); + + if (!link_sta) + continue; + + switch (link_sta->bandwidth) { #define C(_bw) case IEEE80211_STA_RX_BW_##_bw: bw = _bw; break - C(20); - C(40); - C(80); - C(160); - C(320); + C(20); + C(40); + C(80); + C(160); + C(320); #undef C - } + } - if (!data->use_chanctx) { - confbw = data->bw; - } else { - struct ieee80211_chanctx_conf *chanctx_conf; + if (!data->use_chanctx) { + confbw = data->bw; + } else { + struct ieee80211_chanctx_conf *chanctx_conf; + + vif_conf = rcu_dereference(vif->link_conf[link_id]); + if (WARN_ON(!vif_conf)) + continue; + + chanctx_conf = rcu_dereference(vif_conf->chanctx_conf); + + if (!WARN_ON(!chanctx_conf)) + confbw = chanctx_conf->def.width; + } + + WARN(bw > hwsim_get_chanwidth(confbw), + "intf %pM [link=%d]: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n", + vif->addr, link_id, sta->addr, bw, sta->deflink.bandwidth, + hwsim_get_chanwidth(data->bw), data->bw); - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (!WARN_ON(!chanctx_conf)) - confbw = chanctx_conf->def.width; - rcu_read_unlock(); } + rcu_read_unlock(); + - WARN(bw > hwsim_get_chanwidth(confbw), - "intf %pM: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n", - vif->addr, sta->addr, bw, sta->deflink.bandwidth, - hwsim_get_chanwidth(data->bw), data->bw); } static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, @@ -2239,6 +2418,21 @@ static int mac80211_hwsim_sta_remove(struct ieee80211_hw *hw, return 0; } +static int mac80211_hwsim_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + if (new_state == IEEE80211_STA_NOTEXIST) + return mac80211_hwsim_sta_remove(hw, vif, sta); + + if (old_state == IEEE80211_STA_NOTEXIST) + return mac80211_hwsim_sta_add(hw, vif, sta); + + return 0; +} + static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, @@ -2265,10 +2459,10 @@ static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, return 0; } -static int mac80211_hwsim_conf_tx( - struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) +static int mac80211_hwsim_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) { wiphy_dbg(hw->wiphy, "%s (queue=%d txop=%d cw_min=%d cw_max=%d aifs=%d)\n", @@ -2719,6 +2913,7 @@ static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw, static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { hwsim_check_magic(vif); @@ -2729,6 +2924,7 @@ static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw, static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { hwsim_check_magic(vif); @@ -2789,6 +2985,45 @@ static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) return 1; } +static int mac80211_hwsim_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return -EOPNOTSUPP; +} + +static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + unsigned long rem = old_links & ~new_links ?: BIT(0); + unsigned long add = new_links & ~old_links; + int i; + + for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS) + mac80211_hwsim_config_mac_nl(hw, old[i]->addr, false); + + for_each_set_bit(i, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + + /* FIXME: figure out how to get the locking here */ + link_conf = rcu_dereference_protected(vif->link_conf[i], 1); + if (WARN_ON(!link_conf)) + continue; + + mac80211_hwsim_config_mac_nl(hw, link_conf->addr, true); + } + + return 0; +} + +static int mac80211_hwsim_change_sta_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + return 0; +} + #define HWSIM_COMMON_OPS \ .tx = mac80211_hwsim_tx, \ .start = mac80211_hwsim_start, \ @@ -2798,43 +3033,58 @@ static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) .remove_interface = mac80211_hwsim_remove_interface, \ .config = mac80211_hwsim_config, \ .configure_filter = mac80211_hwsim_configure_filter, \ - .bss_info_changed = mac80211_hwsim_bss_info_changed, \ + .vif_cfg_changed = mac80211_hwsim_vif_info_changed, \ + .link_info_changed = mac80211_hwsim_link_info_changed, \ .tx_last_beacon = mac80211_hwsim_tx_last_beacon, \ - .sta_add = mac80211_hwsim_sta_add, \ - .sta_remove = mac80211_hwsim_sta_remove, \ .sta_notify = mac80211_hwsim_sta_notify, \ .sta_rc_update = mac80211_hwsim_sta_rc_update, \ - .set_tim = mac80211_hwsim_set_tim, \ .conf_tx = mac80211_hwsim_conf_tx, \ .get_survey = mac80211_hwsim_get_survey, \ CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) \ .ampdu_action = mac80211_hwsim_ampdu_action, \ .flush = mac80211_hwsim_flush, \ - .get_tsf = mac80211_hwsim_get_tsf, \ - .set_tsf = mac80211_hwsim_set_tsf, \ .get_et_sset_count = mac80211_hwsim_get_et_sset_count, \ .get_et_stats = mac80211_hwsim_get_et_stats, \ .get_et_strings = mac80211_hwsim_get_et_strings, +#define HWSIM_NON_MLO_OPS \ + .sta_add = mac80211_hwsim_sta_add, \ + .sta_remove = mac80211_hwsim_sta_remove, \ + .set_tim = mac80211_hwsim_set_tim, \ + .get_tsf = mac80211_hwsim_get_tsf, \ + .set_tsf = mac80211_hwsim_set_tsf, + static const struct ieee80211_ops mac80211_hwsim_ops = { HWSIM_COMMON_OPS + HWSIM_NON_MLO_OPS .sw_scan_start = mac80211_hwsim_sw_scan, .sw_scan_complete = mac80211_hwsim_sw_scan_complete, }; +#define HWSIM_CHANCTX_OPS \ + .hw_scan = mac80211_hwsim_hw_scan, \ + .cancel_hw_scan = mac80211_hwsim_cancel_hw_scan, \ + .remain_on_channel = mac80211_hwsim_roc, \ + .cancel_remain_on_channel = mac80211_hwsim_croc, \ + .add_chanctx = mac80211_hwsim_add_chanctx, \ + .remove_chanctx = mac80211_hwsim_remove_chanctx, \ + .change_chanctx = mac80211_hwsim_change_chanctx, \ + .assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx,\ + .unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx, + static const struct ieee80211_ops mac80211_hwsim_mchan_ops = { HWSIM_COMMON_OPS - .hw_scan = mac80211_hwsim_hw_scan, - .cancel_hw_scan = mac80211_hwsim_cancel_hw_scan, - .sw_scan_start = NULL, - .sw_scan_complete = NULL, - .remain_on_channel = mac80211_hwsim_roc, - .cancel_remain_on_channel = mac80211_hwsim_croc, - .add_chanctx = mac80211_hwsim_add_chanctx, - .remove_chanctx = mac80211_hwsim_remove_chanctx, - .change_chanctx = mac80211_hwsim_change_chanctx, - .assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx, - .unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx, + HWSIM_NON_MLO_OPS + HWSIM_CHANCTX_OPS +}; + +static const struct ieee80211_ops mac80211_hwsim_mlo_ops = { + HWSIM_COMMON_OPS + HWSIM_CHANCTX_OPS + .set_rts_threshold = mac80211_hwsim_set_rts_threshold, + .change_vif_links = mac80211_hwsim_change_vif_links, + .change_sta_links = mac80211_hwsim_change_sta_links, + .sta_state = mac80211_hwsim_sta_state, }; struct hwsim_new_radio_params { @@ -2851,6 +3101,7 @@ struct hwsim_new_radio_params { u32 iftypes; u32 *ciphers; u8 n_ciphers; + bool mlo; }; static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, @@ -3005,7 +3256,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, .phy_cap_info[0] = @@ -3158,7 +3409,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, .phy_cap_info[0] = @@ -3340,7 +3591,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_NSEP_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, .phy_cap_info[0] = @@ -3544,7 +3795,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, idx = hwsim_radio_idx++; spin_unlock_bh(&hwsim_radio_lock); - if (param->use_chanctx) + if (param->mlo) + ops = &mac80211_hwsim_mlo_ops; + else if (param->use_chanctx) ops = &mac80211_hwsim_mchan_ops; hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname); if (!hw) { @@ -3705,13 +3958,22 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(hw, PS_NULLFUNC_STACK); ieee80211_hw_set(hw, TDLS_WIDER_BW); - if (rctbl) - ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + if (param->mlo) { + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, AP_LINK_PS); + } else { + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(hw, PS_NULLFUNC_STACK); + if (rctbl) + ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); + } + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | @@ -3856,9 +4118,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); - hrtimer_init(&data->beacon_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS_SOFT); - data->beacon_timer.function = mac80211_hwsim_beacon; + for (i = 0; i < ARRAY_SIZE(data->link_data); i++) { + hrtimer_init(&data->link_data[i].beacon_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS_SOFT); + data->link_data[i].beacon_timer.function = + mac80211_hwsim_beacon; + data->link_data[i].link_id = i; + } err = ieee80211_register_hw(hw); if (err < 0) { @@ -4080,6 +4346,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, const u8 *src; unsigned int hwsim_flags; int i; + unsigned long flags; bool found = false; if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || @@ -4107,18 +4374,20 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, } /* look for the skb matching the cookie passed back from user */ + spin_lock_irqsave(&data2->pending.lock, flags); skb_queue_walk_safe(&data2->pending, skb, tmp) { - u64 skb_cookie; + uintptr_t skb_cookie; txi = IEEE80211_SKB_CB(skb); - skb_cookie = (u64)(uintptr_t)txi->rate_driver_data[0]; + skb_cookie = (uintptr_t)txi->rate_driver_data[0]; if (skb_cookie == ret_skb_cookie) { - skb_unlink(skb, &data2->pending); + __skb_unlink(skb, &data2->pending); found = true; break; } } + spin_unlock_irqrestore(&data2->pending.lock, flags); /* not found */ if (!found) @@ -4227,16 +4496,28 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); if (info->attrs[HWSIM_ATTR_FREQ]) { + struct tx_iter_data iter_data = {}; + /* throw away off-channel packets, but allow both the temporary - * ("hw" scan/remain-on-channel) and regular channel, since the - * internal datapath also allows this + * ("hw" scan/remain-on-channel), regular channels and links, + * since the internal datapath also allows this */ - mutex_lock(&data2->mutex); rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]); - if (rx_status.freq != channel->center_freq) { - mutex_unlock(&data2->mutex); + iter_data.channel = ieee80211_get_channel(data2->hw->wiphy, + rx_status.freq); + if (!iter_data.channel) goto out; + + mutex_lock(&data2->mutex); + if (!hwsim_chans_compat(iter_data.channel, channel)) { + ieee80211_iterate_active_interfaces_atomic( + data2->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_tx_iter, &iter_data); + if (!iter_data.receive) { + mutex_unlock(&data2->mutex); + goto out; + } } mutex_unlock(&data2->mutex); } else { @@ -4426,6 +4707,11 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) } } + param.mlo = info->attrs[HWSIM_ATTR_MLO_SUPPORT]; + + if (param.mlo) + param.use_chanctx = true; + if (info->attrs[HWSIM_ATTR_RADIO_NAME]) { hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]), nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]), @@ -4733,7 +5019,7 @@ static void __net_exit hwsim_exit_net(struct net *net) NULL); } - ida_simple_remove(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net)); + ida_free(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net)); } static struct pernet_operations hwsim_net_ops = { @@ -5084,7 +5370,8 @@ static int __init init_mac80211_hwsim(void) } param.p2p_device = support_p2p_device; - param.use_chanctx = channels > 1; + param.mlo = mlo; + param.use_chanctx = channels > 1 || mlo; param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; if (param.p2p_device) param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); |