From 01689b1b95b1fc8880abfbde6c56f45e8e5b5c91 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 13 Oct 2023 17:18:55 +0530 Subject: [PATCH] hostapd: add channel switch support with MLO During channel switch, if its a multi-link operation, there is a requirement to send link_id on which channel switch should take place. Also, once CSA related events are received, it needs to be passed to appropriate link BSS and hence hostapd_data context. Add support for the above and hence enable channel switch with MLO. Signed-off-by: Aditya Kumar Singh --- src/ap/hostapd.c | 7 ++++ src/drivers/driver_nl80211.c | 36 +++++++++++++++++--- src/drivers/driver_nl80211.h | 1 + src/drivers/driver_nl80211_event.c | 54 ++++++++++++++++++------------ 4 files changed, 73 insertions(+), 25 deletions(-) --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -4308,6 +4308,13 @@ static int hostapd_fill_csa_settings(str settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon; settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp; + settings->freq_params.link_id = -1; + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + settings->freq_params.link_id = hapd->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ + return 0; } --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4153,6 +4153,22 @@ struct i802_link * nl80211_get_link(stru return bss->flink; } +struct i802_link * nl80211_get_link_or_null(struct i802_bss *bss, u8 link_id) +{ + unsigned int i; + + if (link_id > MAX_NUM_MLD_LINKS) + return NULL; + + for (i = 0; i < bss->n_links; i++) { + if (bss->links[i].link_id != link_id) + continue; + + return &bss->links[i]; + } + + return NULL; +} static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq) { @@ -11241,6 +11257,17 @@ static int nl80211_switch_channel(void * if (settings->handle_dfs && nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS)) goto fail; + if (nl80211_link_valid(bss, settings->freq_params.link_id)) { + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request on link_id=%d", + settings->freq_params.link_id); + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, + settings->freq_params.link_id)) { + nlmsg_free(msg); + return -1; + } + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)", --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -326,6 +326,7 @@ const char * nl80211_iftype_str(enum nl8 void nl80211_restore_ap_mode(struct i802_bss *bss); struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id); +struct i802_link * nl80211_get_link_or_null(struct i802_bss *bss, u8 link_id); int nl80211_valid_link(struct i802_bss *bss, s8 link_id); #ifdef ANDROID --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -194,7 +194,6 @@ static const char * nl80211_command_to_s return "NL80211_CMD_UNKNOWN"; } - static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { @@ -1198,7 +1197,7 @@ static int calculate_chan_offset(int wid } -static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, +static void mlme_event_ch_switch(struct i802_bss *bss, struct nlattr *ifindex, struct nlattr *link, struct nlattr *freq, struct nlattr *type, struct nlattr *bw, struct nlattr *cf1, @@ -1209,24 +1208,34 @@ static void mlme_event_ch_switch(struct struct nlattr *ru_punct_ofdma, int finished) { - struct i802_bss *bss; + struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data data; int ht_enabled = 1; int chan_offset = 0; - int ifidx; + int link_id = -1; + struct i802_link *mld_link = bss->flink; + void *ctx = bss->ctx; - wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event", - finished ? "" : " started"); + wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event for %s", + finished ? "" : " started", bss->ifname); if (!freq) return; - ifidx = nla_get_u32(ifindex); - bss = get_bss_ifindex(drv, ifidx); - if (bss == NULL) { - wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", - ifidx); - return; + if (link) { + link_id = nla_get_u8(link); + mld_link = nl80211_get_link_or_null(bss, link_id); + if (!mld_link) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignoring Channel switch%s event on invalid link id %d", + finished ? "" : " started", link_id); + return; + } + + wpa_printf(MSG_DEBUG, + "nl80211: Channel switch%s event for link %d", + finished ? "" : " started", link_id); + ctx = mld_link->ctx; } if (type) { @@ -1280,13 +1289,10 @@ static void mlme_event_ch_switch(struct } if (finished) - bss->flink->freq = data.ch_switch.freq; - - if (link) { - u8 link_id = nla_get_u8(link); + mld_link->freq = data.ch_switch.freq; - if (link_id < MAX_NUM_MLD_LINKS && - drv->sta_mlo_info.valid_links & BIT(link_id)) { + if (link_id != -1 && is_sta_interface(drv->nlmode)) { + if (drv->sta_mlo_info.valid_links & BIT(link_id)) { data.ch_switch.link_id = link_id; drv->sta_mlo_info.links[link_id].freq = data.ch_switch.freq; @@ -1302,7 +1308,13 @@ static void mlme_event_ch_switch(struct drv->assoc_freq = data.ch_switch.freq; - wpa_supplicant_event(bss->ctx, finished ? + if (is_ap_interface(drv->nlmode)) { + wpa_supplicant_event(ctx, finished ? + EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data); + return; + } + + wpa_supplicant_event(ctx, finished ? EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data); } @@ -3944,7 +3956,7 @@ static void do_process_drv_event(struct tb[NL80211_ATTR_MLO_LINKS]); break; case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: - mlme_event_ch_switch(drv, + mlme_event_ch_switch(bss, tb[NL80211_ATTR_IFINDEX], tb[NL80211_ATTR_MLO_LINK_ID], tb[NL80211_ATTR_WIPHY_FREQ], @@ -3959,7 +3971,7 @@ static void do_process_drv_event(struct 0); break; case NL80211_CMD_CH_SWITCH_NOTIFY: - mlme_event_ch_switch(drv, + mlme_event_ch_switch(bss, tb[NL80211_ATTR_IFINDEX], tb[NL80211_ATTR_MLO_LINK_ID], tb[NL80211_ATTR_WIPHY_FREQ],