[openwrt/openwrt] wpa_supplicant: add MLO client support
LEDE Commits
lede-commits at lists.infradead.org
Wed Sep 24 04:49:37 PDT 2025
nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/9aca8a97d7911230211d98a24e4dc4b0a0173ec2
commit 9aca8a97d7911230211d98a24e4dc4b0a0173ec2
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Sun Sep 21 15:39:35 2025 +0200
wpa_supplicant: add MLO client support
Can also be used for a client mode interface that is able to connect on
multiple bands individually, while handling hostapd state for the correct
band.
Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
.../files-ucode/usr/share/ucode/wifi/supplicant.uc | 2 +
.../files/lib/netifd/wireless-device.uc | 13 +
.../wifi-scripts/files/lib/netifd/wireless.uc | 19 +-
.../services/hostapd/files/wpa_supplicant.uc | 425 ++++++++++++++++++---
.../hostapd/patches/601-ucode_support.patch | 23 +-
.../services/hostapd/src/wpa_supplicant/ucode.c | 149 +++++++-
.../services/hostapd/src/wpa_supplicant/ucode.h | 5 +
7 files changed, 557 insertions(+), 79 deletions(-)
diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc
index 30e196ddce..3ef150694f 100644
--- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc
+++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc
@@ -248,6 +248,8 @@ export function generate(config_list, data, interface) {
iface: interface.config.ifname,
config: file_name,
'4addr': !!interface.config.wds,
+ mlo: !!interface.config.mlo,
+ freq_list: data.config.scan_list,
powersave: false
};
diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc
index d793ef8bfe..fa9a5faafb 100644
--- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc
+++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc
@@ -17,6 +17,16 @@ let wdev_handler = {};
let wdev_script_task, wdev_script_timeout;
let handler_timer;
+function supplicant_start_mlo()
+{
+ ubus.call({
+ object: "wpa_supplicant",
+ method: "mld_start",
+ return: "ignore",
+ data: { },
+ });
+}
+
function delete_wdev(name)
{
delete netifd.wireless.devices[name];
@@ -216,6 +226,9 @@ function run_next_handler()
{
while (!wdev_cur && length(wdev_handler) > 0)
__run_next_handler();
+
+ if (!wdev_cur && !length(wdev_handler))
+ supplicant_start_mlo();
}
function run_handler(wdev, op, cb)
diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc
index 360fe2503e..19c38d11e5 100644
--- a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc
+++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc
@@ -17,12 +17,12 @@ let wireless = netifd.wireless = {
path: realpath(netifd.main_path + "/wireless"),
};
-function hostapd_update_mlo()
+function wpad_update_mlo(service, mode)
{
let config = {};
for (let ifname, data in wireless.mlo) {
- if (data.mode != "ap")
+ if (data.mode != mode)
continue;
data.phy = find_phy(data.radio_config[0], true);
@@ -33,17 +33,28 @@ function hostapd_update_mlo()
}
ubus.call({
- object: "hostapd",
+ object: service,
method: "mld_set",
return: "ignore",
data: { config },
});
}
+function hostapd_update_mlo()
+{
+ wpad_update_mlo("hostapd", "ap");
+}
+
+function supplicant_update_mlo()
+{
+ wpad_update_mlo("wpa_supplicant", "sta");
+}
+
function update_config(new_devices, mlo_vifs)
{
wireless.mlo = mlo_vifs;
hostapd_update_mlo();
+ supplicant_update_mlo();
for (let name, dev in wireless.devices)
if (!new_devices[name])
@@ -516,6 +527,8 @@ wireless.obj = ubus.publish("network.wireless", ubus_obj);
wireless.listener = ubus.listener("ubus.object.add", (event, msg) => {
if (msg.path == "hostapd")
hostapd_update_mlo();
+ else if (msg.path == "wpa_supplicant")
+ supplicant_update_mlo();
});
return {
diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc
index dd371154e1..b4cafe80a6 100644
--- a/package/network/services/hostapd/files/wpa_supplicant.uc
+++ b/package/network/services/hostapd/files/wpa_supplicant.uc
@@ -13,6 +13,7 @@ function ex_handler(e)
}
libubus.guard(ex_handler);
+wpas.data.mld = {};
wpas.data.config = {};
wpas.data.iface_phy = {};
wpas.data.macaddr_list = {};
@@ -77,50 +78,39 @@ function prepare_config(config, radio)
return { config };
}
-function set_config(config_name, phy_name, radio, num_global_macaddr, macaddr_base, config_list)
+function phy_dev_open(phy_name)
{
- let phy = wpas.data.config[config_name];
-
- if (radio < 0)
- radio = null;
-
+ let phy = wpas.data.config[phy_name];
if (!phy) {
- phy = vlist_new(iface_cb, false);
- phy.name = phy_name;
- wpas.data.config[config_name] = phy;
+ warn(`Missing phy config for ${phy_name}\n`);
+ return;
}
- phy.radio = radio;
- phy.num_global_macaddr = num_global_macaddr;
- phy.macaddr_base = macaddr_base;
+ let phydev = phy_open(phy.name, phy.radio);
+ if (!phydev)
+ return;
- let values = [];
- for (let config in config_list)
- push(values, [ config.iface, prepare_config(config) ]);
+ let macaddr_list = wpas.data.macaddr_list[phy_name];
+ phydev.macaddr_init(macaddr_list, {
+ num_global: phy.num_global_macaddr,
+ macaddr_base: phy.macaddr_base,
+ });
- phy.update(values);
+ return phydev;
}
function start_pending(phy_name)
{
let phy = wpas.data.config[phy_name];
- let ubus = wpas.data.ubus;
-
if (!phy || !phy.data)
return;
- let phydev = phy_open(phy.name, phy.radio);
+ let phydev = phy_dev_open(phy_name);
if (!phydev) {
wpas.printf(`Could not open phy ${phy_name}`);
return;
}
- let macaddr_list = wpas.data.macaddr_list[phy_name];
- phydev.macaddr_init(macaddr_list, {
- num_global: phy.num_global_macaddr,
- macaddr_base: phy.macaddr_base,
- });
-
for (let ifname in phy.data)
iface_start(phydev, phy.data[ifname]);
}
@@ -136,6 +126,235 @@ function phy_name(phy, radio)
return phy;
}
+function mld_remove(data)
+{
+ if (!data.radio_mask_up)
+ return;
+
+ let name = data.name;
+ wpas.printf(`Remove MLD interface ${name}`);
+ wpas.remove_iface(name);
+ wdev_remove(name);
+ data.radio_mask_up = 0;
+}
+
+function mld_first_phy(data)
+{
+ let mask = data.radio_mask_present;
+
+ for (let i = 0; mask; i++, mask >>= 1)
+ if (mask & 1)
+ return i;
+}
+
+function mld_radio_index(data, freq)
+{
+ let phys = data.phy_config;
+ for (let i = 0; i < length(phys); i++)
+ if (phys[i] && index(phys[i].freq_list, freq) >= 0)
+ return i;
+}
+
+function mld_add(data, phy_list)
+{
+ let name = data.name;
+ phy_list ??= [];
+
+ wpas.printf(`Add MLD interface ${name}`);
+
+ let radio = mld_first_phy(data);
+ if (radio == null)
+ return;
+
+ let phy_name = data.phy + '.' + radio;
+ let phydev = phy_list[phy_name];
+ if (!phydev) {
+ phydev = phy_dev_open(phy_name);
+ if (!phydev)
+ return;
+
+ phy_list[phy_name] = phydev;
+ }
+
+ let wdev_config = { ...data.config, radio_mask: data.radio_mask };
+ let ret = phydev.wdev_add(name, wdev_config);
+ if (ret)
+ wpas.printf(`Failed to create device ${name}: ${ret}`);
+
+ let first_config = data.phy_config[radio];
+
+ wdev_set_up(name, true);
+ wpas.add_iface(first_config);
+
+ let iface = wpas.interfaces[name];
+ if (!iface) {
+ wpas.printf(`Interface ${name} not found after adding\n`);
+ wpas.remove_iface(name);
+ wdev_remove(name);
+ return;
+ }
+
+ if (length(data.freq_list) > 0)
+ iface.config('freq_list', data.freq_list);
+
+ data.radio_mask_up = data.radio_mask_present;
+}
+
+function mld_remove_links(data)
+{
+ // TODO
+ mld_remove(data);
+}
+
+function mld_add_links(data)
+{
+ // TODO: incremental update
+ mld_remove(data);
+ mld_add(data);
+}
+
+function mld_set_config(config)
+{
+ let prev_mld = { ...wpas.data.mld };
+ let new_mld = {};
+ let phy_list = {};
+ let new_config = !length(prev_mld);
+
+ wpas.printf(`Set MLD config: ${keys(config)}`);
+
+ for (let name, data in config) {
+ let prev = prev_mld[name];
+ if (prev && is_equal(prev.config, data)) {
+ new_mld[name] = prev;
+ delete prev_mld[name];
+ continue;
+ }
+
+ let radio_mask = 0;
+ for (let r in data.radios)
+ if (r != null)
+ radio_mask |= 1 << r;
+
+ new_mld[name] = {
+ name,
+ config: data,
+ phy: data.phy,
+ phy_config: [],
+ radio_mask,
+ radio_mask_up: 0,
+ radio_mask_present: 0,
+ };
+ }
+
+ for (let name, data in prev_mld)
+ mld_remove(data);
+
+ wpas.data.mld = new_mld;
+
+}
+
+function mld_set_iface_config(name, data, radio, config)
+{
+ wpas.printf(`Set MLD interface ${name} radio ${radio} config: ${keys(config)}`);
+
+ data.phy_config[radio] = config;
+ if (config)
+ data.radio_mask_present |= 1 << radio;
+ else
+ data.radio_mask_present &= ~(1 << radio);
+
+ let freq_list;
+ for (let config in data.phy_config) {
+ if (!config || !config.freq_list)
+ continue;
+ if (!freq_list)
+ freq_list = [ ...config.freq_list ];
+ else
+ push(freq_list, ...config.freq_list);
+ }
+
+ data.freq_list = freq_list;
+}
+
+function mld_update_iface(name, data) {
+ if (!data.radio_mask_up)
+ return;
+
+ if (!data.radio_mask_present) {
+ mld_remove(data);
+ return;
+ }
+
+ let mask = data.radio_mask_up & ~data.radio_mask_present;
+ if (!mask)
+ return;
+
+ mld_remove_links(data);
+}
+
+function mld_update_phy(phy, ifaces) {
+ for (let name, data in wpas.data.mld) {
+ if (data.phy != phy.name)
+ continue;
+
+ mld_set_iface_config(name, data, phy.radio, ifaces[name]);
+ mld_update_iface(name, data);
+ }
+}
+
+function mld_start() {
+ wpas.printf(`Start pending MLD interfaces\n`);
+
+ let phy_list = {};
+ for (let name, data in wpas.data.mld) {
+ wpas.printf(`MLD interface ${name} present=${data.radio_mask_present} up=${data.radio_mask_up}`);
+ let add_mask = data.radio_mask_present & ~data.radio_mask_up;
+ if (!add_mask)
+ continue;
+
+ if (!data.radio_mask_up)
+ mld_add(data, phy_list);
+ else
+ mld_add_links(data);
+ }
+}
+
+function mld_bss_allowed(data, bss) {
+ if (!data.freq_list)
+ return true;
+
+ return index(data.freq_list, bss.freq) >= 0;
+}
+
+function set_config(config_name, phy_name, radio, num_global_macaddr, macaddr_base, config_list)
+{
+ let phy = wpas.data.config[config_name];
+
+ if (radio < 0)
+ radio = null;
+
+ if (!phy) {
+ phy = vlist_new(iface_cb, false);
+ phy.name = phy_name;
+ wpas.data.config[config_name] = phy;
+ }
+
+ phy.radio = radio;
+ phy.num_global_macaddr = num_global_macaddr;
+ phy.macaddr_base = macaddr_base;
+
+ let values = [];
+ let mlo_ifaces = {};
+ for (let config in config_list)
+ if (config.mlo)
+ mlo_ifaces[config.iface] = config;
+ else
+ push(values, [ config.iface, prepare_config(config) ]);
+
+ mld_update_phy(phy, mlo_ifaces);
+ phy.update(values);
+}
+
let main_obj = {
phy_set_state: {
args: {
@@ -214,6 +433,25 @@ let main_obj = {
return libubus.STATUS_NOT_FOUND;
}
},
+ mld_set: {
+ args: {
+ config: {}
+ },
+ call: function(req) {
+ if (!req.args.config)
+ return libubus.STATUS_INVALID_ARGUMENT;
+
+ mld_set_config(req.args.config);
+ return 0;
+ }
+ },
+ mld_start: {
+ args: {},
+ call: function(req) {
+ mld_start();
+ return 0;
+ }
+ },
config_set: {
args: {
phy: "",
@@ -315,12 +553,30 @@ function iface_event(type, name, data) {
ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
}
-function iface_hostapd_notify(phy, ifname, iface, state)
+function iface_hostapd_fill_radio_link(mld, radio, msg, link)
+{
+ let config = mld.phy_config[radio];
+ if (!config)
+ return;
+
+ let freq_list = config.freq_list;
+ if (!freq_list)
+ return;
+
+ if (!link || index(freq_list, link.frequency) < 0)
+ return;
+
+ msg.frequency = link.frequency;
+ msg.sec_chan_offset = link.sec_chan_offset;
+}
+
+function iface_hostapd_notify(ifname, iface, state)
{
- let ubus = wpas.data.ubus;
let status = iface.status();
- let msg = { phy: phy };
+ let ubus = wpas.data.ubus;
+ let msg = {};
+ let mld = wpas.data.mld[ifname];
switch (state) {
case "DISCONNECTED":
case "AUTHENTICATING":
@@ -333,26 +589,76 @@ function iface_hostapd_notify(phy, ifname, iface, state)
break;
case "COMPLETED":
msg.up = true;
- msg.frequency = status.frequency;
- msg.sec_chan_offset = status.sec_chan_offset;
+ if (!mld) {
+ msg.frequency = status.frequency;
+ msg.sec_chan_offset = status.sec_chan_offset;
+ }
break;
default:
return;
}
- ubus.call("hostapd", "apsta_state", msg);
+ if (!mld) {
+ msg.phy = wpas.data.iface_phy[ifname];
+ if (!phy) {
+ wpas.printf(`no PHY for ifname ${ifname}`);
+ return;
+ }
+ ubus.call("hostapd", "apsta_state", msg);
+ return;
+ }
+
+ let radio_mask = mld.radio_mask;
+ for (let i = 0; radio_mask; i++, radio_mask >>= 1) {
+ if (!(radio_mask & 1)) {
+ wpas.printf(`skip radio ${i}`);
+ continue;
+ }
+
+ let radio_msg = {
+ ...msg,
+ phy: mld.phy,
+ radio: i,
+ };
+
+ if (state == "COMPLETED") {
+ if (status.links)
+ for (let link in status.links)
+ iface_hostapd_fill_radio_link(mld, i, radio_msg, link);
+ else
+ iface_hostapd_fill_radio_link(mld, i, radio_msg, status);
+ }
+
+ ubus.call("hostapd", "apsta_state", radio_msg);
+ }
}
-function iface_channel_switch(phy, ifname, iface, info)
+function iface_channel_switch(ifname, iface, info)
{
let msg = {
- phy: phy,
up: true,
csa: true,
csa_count: info.csa_count ? info.csa_count - 1 : 0,
frequency: info.frequency,
sec_chan_offset: info.sec_chan_offset,
};
+
+ let mld = wpas.data.mld[ifname];
+ if (mld) {
+ msg.phy = mld.phy;
+ msg.radio = mld_radio_index(mld, info.frequency);
+ if (msg.radio == null) {
+ wpas.printf(`PHY ${mld.phy} radio for frequency ${info.frequency} not found`);
+ return;
+ }
+ } else {
+ msg.phy = wpas.data.iface_phy[ifname];
+ if (!msg.phy) {
+ wpas.printf(`no PHY for ifname ${ifname}`);
+ return;
+ }
+ }
+
ubus.call("hostapd", "apsta_state", msg);
}
@@ -362,6 +668,13 @@ return {
set_config(phy, []);
wpas.ubus.disconnect();
},
+ bss_allowed: function(ifname, bss) {
+ let mld = wpas.data.mld[ifname];
+ if (!mld)
+ return true;
+
+ return mld_bss_allowed(mld, bss);
+ },
iface_add: function(name, obj) {
iface_event("add", name);
},
@@ -369,39 +682,35 @@ return {
iface_event("remove", name);
},
state: function(ifname, iface, state) {
- let phy = wpas.data.iface_phy[ifname];
- if (!phy) {
- wpas.printf(`no PHY for ifname ${ifname}`);
- return;
- }
+ try {
+ iface_hostapd_notify(ifname, iface, state);
- iface_hostapd_notify(phy, ifname, iface, state);
+ if (state != "COMPLETED")
+ return;
- if (state != "COMPLETED")
- return;
+ let phy = wpas.data.iface_phy[ifname];
+ if (!phy)
+ return;
- let phy_data = wpas.data.config[phy];
- if (!phy_data)
- return;
+ let phy_data = wpas.data.config[phy];
+ if (!phy_data)
+ return;
- let iface_data = phy_data.data[ifname];
- if (!iface_data)
- return;
+ let iface_data = phy_data.data[ifname];
+ if (!iface_data)
+ return;
- let wdev_config = iface_data.config;
- if (!wdev_config || wdev_config.mode != "mesh")
- return;
+ let wdev_config = iface_data.config;
+ if (!wdev_config || wdev_config.mode != "mesh")
+ return;
- wdev_set_mesh_params(ifname, wdev_config);
+ wdev_set_mesh_params(ifname, wdev_config);
+ } catch (e) {
+ ex_handler(e);
+ }
},
event: function(ifname, iface, ev, info) {
- let phy = wpas.data.iface_phy[ifname];
- if (!phy) {
- wpas.printf(`no PHY for ifname ${ifname}`);
- return;
- }
-
if (ev == "CH_SWITCH_STARTED")
- iface_channel_switch(phy, ifname, iface, info);
+ iface_channel_switch(ifname, iface, info);
}
};
diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch
index 30780ca76e..436acf9220 100644
--- a/package/network/services/hostapd/patches/601-ucode_support.patch
+++ b/package/network/services/hostapd/patches/601-ucode_support.patch
@@ -702,7 +702,28 @@ as adding/removing interfaces.
CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
-@@ -6293,6 +6293,7 @@ void supplicant_event(void *ctx, enum wp
+@@ -53,6 +53,7 @@
+ #include "wmm_ac.h"
+ #include "nan_usd.h"
+ #include "dpp_supplicant.h"
++#include "ucode.h"
+
+
+ #define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5
+@@ -1706,6 +1707,12 @@ struct wpa_ssid * wpa_scan_res_match(str
+ return NULL;
+ }
+
++ if (!wpas_ucode_bss_allowed(wpa_s, bss)) {
++ if (debug_print)
++ wpa_dbg(wpa_s, MSG_DEBUG, " skip - denied by ucode handler");
++ return NULL;
++ }
++
+ for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
+ if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
+ bss, bssid_ignore_count, debug_print, link))
+@@ -6293,6 +6300,7 @@ void supplicant_event(void *ctx, enum wp
event_to_string(event), event);
#endif /* CONFIG_NO_STDOUT_DEBUG */
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.c b/package/network/services/hostapd/src/wpa_supplicant/ucode.c
index 88d63e0b21..7f0249a423 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ucode.c
+++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.c
@@ -6,6 +6,7 @@
#include "wpa_supplicant_i.h"
#include "wps_supplicant.h"
#include "ctrl_iface.h"
+#include "config.h"
#include "bss.h"
#include "ucode.h"
@@ -41,6 +42,21 @@ wpas_ucode_update_interfaces(void)
ucv_object_add(ucv_prototype_get(global), "interfaces", ifs);
}
+static uc_value_t *
+wpas_ucode_bss_get_uval(struct wpa_bss *bss)
+{
+ uc_value_t *val;
+
+ val = ucv_object_new(vm);
+ ucv_object_add(val, "freq", ucv_int64_new(bss->freq));
+ ucv_object_add(val, "ssid", ucv_string_new_length(bss->ssid, bss->ssid_len));
+ ucv_object_add(val, "snr", ucv_int64_new(bss->snr));
+ ucv_object_add(val, "signal", ucv_int64_new(bss->level));
+ ucv_object_add(val, "noise", ucv_int64_new(bss->noise));
+
+ return val;
+}
+
void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
{
uc_value_t *val;
@@ -71,6 +87,25 @@ void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
ucv_put(val);
}
+bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ uc_value_t *val;
+ bool ret = true;
+
+ if (wpa_ucode_call_prepare("bss_allowed"))
+ return true;
+
+ uc_value_push(ucv_string_new(wpa_s->ifname));
+ uc_value_push(wpas_ucode_bss_get_uval(bss));
+ val = wpa_ucode_call(2);
+
+ if (ucv_type(val) == UC_BOOLEAN)
+ ret = ucv_boolean_get(val);
+ ucv_put(val);
+
+ return ret;
+}
+
void wpas_ucode_update_state(struct wpa_supplicant *wpa_s)
{
const char *state;
@@ -203,6 +238,29 @@ out:
return ucv_int64_new(ret);
}
+static void
+uc_wpas_iface_status_bss(uc_value_t *ret, struct wpa_bss *bss)
+{
+ int sec_chan = 0;
+ const u8 *ie;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= 2) {
+ const struct ieee80211_ht_operation *ht_oper;
+ int sec;
+
+ ht_oper = (const void *) (ie + 2);
+ sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ sec_chan = 1;
+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ sec_chan = -1;
+ }
+
+ ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
+ ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+}
+
static uc_value_t *
uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
{
@@ -218,25 +276,29 @@ uc_wpas_iface_status(uc_vm_t *vm, size_t nargs)
ucv_object_add(ret, "state", ucv_string_new(wpa_supplicant_state_txt(wpa_s->wpa_state)));
bss = wpa_s->current_bss;
- if (bss) {
- int sec_chan = 0;
- const u8 *ie;
-
- ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
- if (ie && ie[1] >= 2) {
- const struct ieee80211_ht_operation *ht_oper;
- int sec;
-
- ht_oper = (const void *) (ie + 2);
- sec = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
- sec_chan = 1;
- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
- sec_chan = -1;
+ if (bss)
+ uc_wpas_iface_status_bss(ret, bss);
+
+ if (wpa_s->valid_links) {
+ unsigned int valid_links = wpa_s->valid_links;
+ uc_value_t *link, *links;
+
+ links = ucv_array_new(vm);
+
+ for (size_t i = 0;
+ valid_links && i < ARRAY_SIZE(wpa_s->links);
+ i++, valid_links >>= 1) {
+ bss = wpa_s->links[i].bss;
+
+ if (!(valid_links & 1) || !bss)
+ continue;
+
+ link = ucv_object_new(vm);
+ uc_wpas_iface_status_bss(link, bss);
+ ucv_array_set(links, i, link);
}
- ucv_object_add(ret, "sec_chan_offset", ucv_int64_new(sec_chan));
- ucv_object_add(ret, "frequency", ucv_int64_new(bss->freq));
+ ucv_object_add(ret, "links", links);
}
#ifdef CONFIG_MESH
@@ -276,6 +338,58 @@ uc_wpas_iface_ctrl(uc_vm_t *vm, size_t nargs)
return ret;
}
+static uc_value_t *
+uc_wpas_iface_config(uc_vm_t *vm, size_t nargs)
+{
+ struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
+ uc_value_t *arg = uc_fn_arg(0);
+ uc_value_t *val = uc_fn_arg(1);
+ uc_value_t *ret = NULL;
+ bool get = nargs == 1;
+ const char *name;
+ size_t len = 0;
+
+ if (!wpa_s || ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ name = ucv_string_get(arg);
+ if (!strcmp(name, "freq_list")) {
+ if (get) {
+ int *cur = wpa_s->conf->freq_list;
+ if (!cur)
+ return NULL;
+
+ ret = ucv_array_new(vm);
+ while (*cur)
+ ucv_array_set(ret, len++, ucv_int64_new(*(cur++)));
+ } else {
+ size_t len = ucv_array_length(val);
+ int *freq_list;
+
+ if (ucv_type(val) != UC_ARRAY)
+ return NULL;
+
+ freq_list = calloc(len + 1, sizeof(*freq_list));
+ for (size_t i = 0; i < len; i++) {
+ uc_value_t *cur = ucv_array_get(val, i);
+
+ if (ucv_type(cur) != UC_INTEGER) {
+ free(freq_list);
+ return NULL;
+ }
+
+ freq_list[i] = ucv_int64_get(cur);
+ }
+
+ free(wpa_s->conf->freq_list);
+ wpa_s->conf->freq_list = freq_list;
+ ret = ucv_boolean_new(true);
+ }
+ }
+
+ return ret;
+}
+
int wpas_ucode_init(struct wpa_global *gl)
{
static const uc_function_list_t global_fns[] = {
@@ -288,6 +402,7 @@ int wpas_ucode_init(struct wpa_global *gl)
static const uc_function_list_t iface_fns[] = {
{ "status", uc_wpas_iface_status },
{ "ctrl", uc_wpas_iface_ctrl },
+ { "config", uc_wpas_iface_config },
};
uc_value_t *data, *proto;
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.h b/package/network/services/hostapd/src/wpa_supplicant/ucode.h
index a429a0ed87..fd339fa3e9 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ucode.h
+++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.h
@@ -20,6 +20,7 @@ void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
void wpas_ucode_update_state(struct wpa_supplicant *wpa_s);
void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, union wpa_event_data *data);
+bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
#else
static inline int wpas_ucode_init(struct wpa_global *gl)
{
@@ -44,6 +45,10 @@ static inline void wpas_ucode_event(struct wpa_supplicant *wpa_s, int event, uni
{
}
+static inline bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return true;
+}
#endif
#endif
More information about the lede-commits
mailing list