Skip to content

RDKB-61540: MLO link reconfiguration for BPi-R4 - #583

Closed
bmilcz-comcast wants to merge 1 commit into
rdkcentral:developfrom
bmilcz-comcast:bpi-link-reconf
Closed

bmilcz-comcast wants to merge 1 commit into
rdkcentral:developfrom
bmilcz-comcast:bpi-link-reconf

Conversation

@bmilcz-comcast

Copy link
Copy Markdown
Contributor

Reason for change:Enable possibility of dynamically enabling/disabling MLO links for multiple MLD devices.
Test Procedure: Verify build, test MLO VAPs, enable/disable links in MLO VAPs, check if appropriate singular VAP gets reset properly, confirm connectivity with a client device.
Risks: None
Priority: P2

@bmilcz-comcast
bmilcz-comcast marked this pull request as draft February 23, 2026 13:53
Comment thread platform/banana-pi/platform.c Outdated
sizeof(vap->u.bss_info.mld_info.common_info.mld_addr));
}

if ((wifi_hal_is_mld_enabled(interface) == vap->u.bss_info.mld_info.common_info.mld_enable) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could it be rolled in to main 'if' perhaps ?

Suggested change
if ((wifi_hal_is_mld_enabled(interface) == vap->u.bss_info.mld_info.common_info.mld_enable) &&
if (vap->u.bss_info.mld_info.common_info.mld_enable) {
if ( vap->u.bss_info.enabled == interface->vap_info.u.bss_info.enabled) {
continue;
strncpy(interface->mld_name, mld_name, sizeof(interface->mld_name) - 1);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really - this is why I left this long TODO just above this. The problem is that normally, we receive config in a top-down fashion (OneWifi -> rdk-wifi-hal) and current DML is accustomed to this as well (setting the link IDs from OneWifi). On Banana Pi there was a requirement to not do that, as this was just an implementation done for a specific platform.

Because of that, to keep DML in sync I had to update in a reverse fashion (rdk-wifi-hal -> OneWifi), you can see this in wifi_hal.c

But unfortunately, even if I do this there is an issue where the DML cache that is used during setting of values is not getting that update. That results in any changes done to adjacent settings (say SSID for VAP) will cause to write the "old" link ID from the cache (unless one explicitly changes this via set) to try and override the values, which is why I have to then override this each time this function is called to prevent that.

Comment thread src/wifi_hal_nl80211.c Outdated
}

if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, interface->index) < 0) {
nlmsg_free(msg);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, already checked and freed before return in line ~8259

Suggested change
nlmsg_free(msg);

Comment thread src/wifi_hal_nl80211.c Outdated
frame_type = (WLAN_FC_TYPE_MGMT << 2) | (stypes[i] << 4);

if (nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, frame_type) < 0) {
nlmsg_free(msg);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above
not needed, already checked and freed before return in line ~8259

Comment thread src/wifi_hal_nl80211.c Outdated
}

if (nla_put(msg, NL80211_ATTR_FRAME_MATCH, 0, NULL) < 0) {
nlmsg_free(msg);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above, not needed, already checked and freed before return in line ~8259

@bmilcz-comcast
bmilcz-comcast marked this pull request as ready for review February 26, 2026 15:08
@bmilcz-comcast

Copy link
Copy Markdown
Contributor Author

recheck

Comment thread platform/banana-pi/platform.c
Comment thread platform/banana-pi/platform.c Outdated
nl80211_interface_enable(interface->mld_name, false);
}

if (old_first_interface == interface) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be moved under if above?
if (old_first_interface == interface) {
// First interface pointer could point to invalid data
// for shared resources.1
....
//unregister frame handlers..

@bmilcz-comcast bmilcz-comcast Mar 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is possible, although that will cause the deinit_bss call to be duplicated in here (as we have to maintain order in what we do, as i.e. we cannot unregister netlink handlers before calling deinit_bss).
Still, your suggestion might be better style so I will attempt to do it.

Comment thread platform/banana-pi/platform.c Outdated
unregister_data_frame_socket(interface);
}

if (new_first_interface != NULL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that dependent on old_new_interface being equal to interface? if not, move it outside the block
if yes - why? ;)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we happen to remove a first link from the MLD group, we have to "move" the resources (as well as our handlers) into the next link and interface which will become the "new" first link for this MLD group in case that when removing the old one we still had at least 1 link left.

It happens only in this case, so this is dependant on old_first_interface.

}

g_wifi_hal.mld_count--;
g_wifi_hal.mld_array = new_mld_array;

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the refcount (so the number of VAPs currently residing in MLD) reaches 0 we have to cleanup that hostapd_mld since it is no longer used.

MLD pointers are stored in g_wifi_hal.mld_array which can hold a pointer to more than one MLD - if that is the case (mld_count > 1 && refcount == 0), we remove the hostapd_mld to which this VAP refers to.

In the scenario you mentioned, and considering the place you commended on specifically, if the mld_count was == 1 and if this conditional was triggered (refcount == 0), then we are effectively removing the only MLD which was in the array, thus setting it to NULL.

Comment thread src/wifi_hal_nl80211.c
wifi_hal_info_print(
"%s:%d: Mgmt frames already registered for %s and frame_type %d \n", __func__,
__LINE__, wifi_hal_get_interface_name(interface), frame_type);
usleep(wait_usec);

This comment was marked as resolved.

Comment thread src/wifi_hal_nl80211.c
{
if (interface->mgmt_frames_registered == 0) {
wifi_hal_dbg_print("%s:%d: interface:%s mgmt frames not registered\n", __func__, __LINE__,
wifi_hal_get_interface_name(interface));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you (rightfully) decided to use interface->name, it wouldnt hurt to change closest use of it too ;)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm thinking of doing it in opposite way and use wifi_hal_get_interface_name() instead :D as it takes into account if this should return the name of the MLD or interface. In general, it is hard to determine at times what is best to return in those helpers because of how the current implementation treats MLO (swapping the interface indexes and different behaviour for interface depending on mld_enable).

Let me know what you think on this.

Comment thread src/wifi_hal_nl80211.c
{
if (interface->spurious_frames_registered == 0) {
wifi_hal_dbg_print("%s:%d: interface:%s spurious frames not registered\n", __func__,
__LINE__, wifi_hal_get_interface_name(interface));

This comment was marked as duplicate.

Comment thread src/wifi_hal_nl80211.c

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on this occasion, this could probably moved above, to the beginning?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think? i.e. have you ever seen this log - if so, putting it at the top will speed up execution. if not - none issue.

Comment thread src/wifi_hal_nl80211_utils.c Outdated
interface->in_reconf = true;

wifi_hal_info_print("%s:%d: interface:%s disable AP\n", __func__, __LINE__, interface_name);
nl80211_enable_ap(interface, false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to catch error here, even under (if (unlikely( ... < 0) )..

// MLO links share some resources (i.e. RADIUS settings) of the first
// link - first take care of it, then attempt to set the rest
wifi_interface_info_t *first_interface = wifi_hal_get_first_mld_interface(interface);

This comment was marked as resolved.

@bmilcz-comcast
bmilcz-comcast force-pushed the bpi-link-reconf branch 4 times, most recently from 133c355 to 78240ea Compare March 5, 2026 13:14
Comment thread src/wifi_hal_hostapd.c
Comment on lines 2252 to 2253
int ret = RETURN_ERR;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (unlikely(interface == NULL)) {
return ret;
}

Comment thread src/wifi_hal_hostapd.c Outdated
wifi_interface_info_t *first_interface = wifi_hal_get_first_mld_interface(interface);

if (first_interface != interface) {
if (first_interface != interface && interface != NULL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (first_interface != interface && interface != NULL) {
if (first_interface != interface) {

Comment thread src/wifi_hal_nl80211.c
#ifdef CONFIG_GENERIC_MLO
if (wifi_hal_is_mld_enabled(interface)) {
to_mac_str(mld_mac, mld_mac_str);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to move this at the start of previous ifdef, line 1915

#ifdef CONFIG_GENERIC_MLO
mld_mac = wifi_hal_get_mld_mac_address(interface);
to_mac_str ....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All mac_str conversions are done here - isn't it better to keep it this way ?

Comment thread src/wifi_hal_nl80211.c

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think? i.e. have you ever seen this log - if so, putting it at the top will speed up execution. if not - none issue.

Comment thread src/wifi_hal_nl80211_utils.c Outdated
}

if (interface->mld_name[0] == '\0') {
if ((interface->mld_name[0] == '\0') || (wifi_hal_is_mld_enabled(interface) == false) ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evaluation is iirc done left to right, so in the interest of speed it might make sense to reverse the order of checks - if vap is disabled no use in further checks .

int wifi_hal_set_mld_link_id(wifi_interface_info_t *interface, int link_id)
int wifi_hal_get_mld_id(wifi_interface_info_t *interface)
{
if (interface == NULL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least log entry of hitting this function
wifi_hal_debug_print("... Entry") would be nice for easier debugging.

Comment thread src/wifi_hal_nl80211_utils.c Outdated
return NL80211_DRV_LINK_ID_NA;
}

if (interface->vap_info.vap_mode == wifi_vap_mode_ap) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any thoughts of supporting sta vaps here? i.e. iadding this might break the mlo sta connection when its ready ;)
Perhaps check != wifi_vap_mode_monitor would be enough? or dropping this if altogether.

Comment thread src/wifi_hal_nl80211_utils.c Outdated
char *interface_name = wifi_hal_get_interface_name(interface);
wifi_radio_info_t *radio = get_radio_by_rdk_index(interface->rdk_radio_index);

if (radio == NULL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

radio is not used in this function at all, if i see correctly.
Depending on scenario, might be worth checking if interface != NULL - but if its checked in caller of this function then this whole block might just be safely removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right - my bad, removing

Comment thread src/wifi_hal_nl80211_utils.c Outdated
return -1;
}

wifi_hal_info_print("%s:%d: interface:%s reload hostapd config\n", __func__, __LINE__,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info is enabled by default iitc, lets not overwhelm the buffer.
Debug level here should be enough

Comment thread src/wifi_hal_nl80211_utils.c Outdated
}
interface->bss_started = false;

wifi_hal_info_print("%s:%d: interface:%s free hostapd data\n", __func__, __LINE__,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch to debug level?

Comment thread src/wifi_hal_nl80211_utils.c Outdated
char *interface_name = wifi_hal_get_interface_name(interface);
wifi_radio_info_t *radio = get_radio_by_rdk_index(interface->rdk_radio_index);

wifi_hal_info_print("%s:%d: interface:%s update hostapd params\n", __func__, __LINE__,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch to debug level pls

if (interface->vap_info.u.bss_info.enabled && radio->configured && radio->oper_param.enable) {
wifi_hal_info_print("%s:%d: interface:%s enable ap\n", __func__, __LINE__, interface_name);
// only for single VAPs
interface->beacon_set = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in reload_interface() above, line ~ 6022 this is under
#ifndef CONFIG_GENERIC_MLO

shouldnt that be the case here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell what was the reason for putting that above in the #ifndef (this is how it was in original function), but normally this flag should be closely tied with start_bss as it influences how further nl80211 driver behaves (wifi_drv_set_ap) and what kind of command it sends. So current placement in my opinion is fine - that #ifndef though - no idea :)

return -1;
}

if (restart_interface(first_interface) < 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one convention please.
by numbers looks like this one should be adjusted to pure
if (restart...) ;)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also,
why reload preceeds restart ? shouldnt this be either-or? or fail safe - if reload wasnt enough, restart whole darn thing ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By convention I understand error checking - I made a mistake in reload, I want to be explicit about the values I expect, will fix

'reload' is supposed to, well - reload hostapd config :) and deinitialize some hostapd BSS data
'restart' is just restarting the BSS back into work.

I was naming that while doing merge on original reload_single_vap/reload_single_mlo functions while extracting common code and this is what I came up with. If you have a suggestion for different naming scheme

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for dynamic enable/disable and reconfiguration of MLO links for multiple MLD devices, with Banana Pi R4–specific handling to ensure links and netlink handlers are correctly reset and restarted during MLO VAP changes.

Changes:

  • Adds MLD-ID APIs and expands exported nl80211 registration/unregistration helpers (mgmt/spurious/data frames).
  • Refactors VAP reload/restart logic into shared helpers, including an MLO-aware reload path.
  • Updates Banana Pi platform code to dynamically set up/tear down MLO links, manage hostapd MLD objects, and reload affected links.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/wifi_hal_priv.h Adds branch prediction macros and exports new/updated MLO + nl80211 helper prototypes.
src/wifi_hal_nl80211_utils.c Updates MLD grouping logic and introduces new reload/restart helpers for VAP and MLO reconfiguration.
src/wifi_hal_nl80211.c Improves (retry-based) registration of mgmt/spurious frames, adds unregister helpers, and exposes data frame socket registration APIs.
src/wifi_hal_hostapd.c Adds a defensive NULL check in update_hostap_interface_params().
src/wifi_hal.c Hooks MLO reload behavior into VAP create/update path and updates cached link-id feedback.
platform/banana-pi/platform.c Implements dynamic MLO setup/teardown for BPi-R4, including hostapd MLD allocation/deallocation and handler cleanup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +6113 to +6118
for (int i = g_wifi_hal.num_radios - 1; i >= 0; i--) {
wifi_interface_info_t *interface_iter = NULL;
wifi_radio_info_t *radio = get_radio_by_rdk_index(i);

hash_map_foreach(radio->interface_map, interface_iter) {

Comment thread src/wifi_hal_nl80211.c
Comment on lines +16036 to +16044
wifi_hal_info_print("%s:%d: interface:%s ifindex:%d nl sock sta:%d, nl sock br_sock:%d\n",
__func__, __LINE__, interface->name, interface->index, interface->u.sta.sta_sock_fd,
interface->u.ap.br_sock_fd);

if (interface->u.sta.sta_sock_fd != 0) {
close(interface->u.sta.sta_sock_fd);
interface->u.sta.sta_sock_fd = -1;
}

Comment on lines +541 to +545
if (wifi_hal_get_mac_address(mld_name, mld_mac) < 0) {
wifi_hal_error_print("%s:%d: Failed to get MAC address for interface %s\n",
__func__, __LINE__, mld_name);
return -1;
}
Comment thread src/wifi_hal_nl80211_utils.c Outdated
Comment on lines +6182 to +6185
if (reload_interface(interface) < 0 && restart_interface(interface) < 0) {
return -1;
}

Comment thread src/wifi_hal_nl80211.c
Comment on lines +8236 to 8238
ret = execute_send_and_recv(interface->nl_cb, interface->nl_event, msg,
mgmt_frame_register_handler, interface, NULL, NULL);

Comment thread src/wifi_hal_nl80211.c
Comment on lines +15954 to +15958
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, interface->index) < 0) {
wifi_hal_error_print("%s:%d: failed set interface index in message for %s interface\n",
__func__, __LINE__, wifi_hal_get_interface_name(interface));
goto error;
}
Comment on lines +1222 to +1235
if (g_wifi_hal.mld_count > 1) {
// There are still other MLDs in the current config
new_mld_array = realloc(g_wifi_hal.mld_array,
(g_wifi_hal.mld_count - 1) * sizeof(struct hostapd_mld *));
if (new_mld_array == NULL) {
wifi_hal_error_print("%s:%d Failed to reallocate MLD array\n", __func__, __LINE__);
return -1;
}
} else {
new_mld_array = NULL;
}

g_wifi_hal.mld_count--;
g_wifi_hal.mld_array = new_mld_array;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be refactored in future to a linked list to simplify maintenance.

Comment thread src/wifi_hal_priv.h
Comment on lines +248 to +254
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
Comment on lines +6061 to +6063
char *interface_name = wifi_hal_get_interface_name(interface);
wifi_radio_info_t *radio = get_radio_by_rdk_index(interface->rdk_radio_index);

@bmilcz-comcast
bmilcz-comcast force-pushed the bpi-link-reconf branch 3 times, most recently from 106da04 to aa2ad4e Compare March 18, 2026 09:17
Reason for change:Enable possibility of dynamically enabling/disabling
MLO links for multiple MLD devices.
Test Procedure: Verify build, test MLO VAPs, enable/disable links in MLO
VAPs, check if appropriate singular VAP gets reset properly, confirm
connectivity with a client device.
Risks: None
Priority: P2

Signed-off-by: Brayan Milczarek <brayan_milczarek@comcast.com>
@bmilcz-comcast

Copy link
Copy Markdown
Contributor Author

just code-style changes

@bmilcz-comcast

Copy link
Copy Markdown
Contributor Author

This PR was not merged due to ongoing issues introduced by other contributions.
This was long enough that the WAs that had to be implemented while working on link reconfig that actually this implementation took a slightly newer shape - due to this, I'm closing this PR and opening a new one for link reconfiguration.

New PR - rdkcentral/OneWifi#1133

@github-actions github-actions Bot locked and limited conversation to collaborators May 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants