config_add_string path phy 'macaddr:macaddr'
config_add_string tx_burst
config_add_string distance
- config_add_int beacon_int chanbw frag rts
+ config_add_int radio beacon_int chanbw frag rts
config_add_int rxantenna txantenna txpower min_tx_power
config_add_int num_global_macaddr multiple_bssid
config_add_boolean noscan ht_coex acs_exclude_dfs background_radar
}
[ "$staidx" -gt 0 -o "$start_disabled" -eq 1 ] && append hostapd_cfg "start_disabled=1" "$N"
- cat >> /var/run/hostapd-$phy.conf <<EOF
+ cat >> /var/run/hostapd-$phy$vif_phy_suffix.conf <<EOF
$hostapd_cfg
bssid=$macaddr
${default_macaddr:+#default_macaddr}
local phy="$1"
local id="${macidx:-0}"
- wdev_tool "$phy" get_macaddr id=$id num_global=$num_global_macaddr mbssid=${multiple_bssid:-0}
+ wdev_tool "$phy$phy_suffix" get_macaddr id=$id num_global=$num_global_macaddr mbssid=${multiple_bssid:-0}
}
get_board_phy_name() (
monitor) prefix=mon;;
esac
- mac80211_set_ifname "$phy" "$prefix"
+ mac80211_set_ifname "$phy$vif_phy_suffix" "$prefix"
}
append active_ifnames "$ifname"
json_get_vars vif_txpower
json_select ..
- [ -z "$vif_txpower" ] || iw dev "$ifname" set txpower fixed "${vif_txpower%%.*}00"
+ set_default vif_txpower "$txpower"
+ if [ -n "$vif_txpower" ]; then
+ iw dev "$ifname" set txpower fixed "${vif_txpower%%.*}00"
+ else
+ iw dev "$ifname" set txpower auto
+ fi
}
wpa_supplicant_init_config() {
wpa_supplicant_set_config() {
local phy="$1"
+ local radio="$2"
local prev
json_set_namespace wpa_supp prev
json_close_array
json_add_string phy "$phy"
+ json_add_int radio "$radio"
json_add_int num_global_macaddr "$num_global_macaddr"
json_add_boolean defer 1
local data="$(json_dump)"
}
hostapd_set_config() {
+ local phy="$1"
+ local radio="$2"
+
[ -n "$hostapd_ctrl" ] || {
- ubus_call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"${hostapd_conf_file}.prev"'" }' > /dev/null
+ ubus_call hostapd config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": "", "prev_config": "'"${hostapd_conf_file}.prev"'" }' > /dev/null
return 0;
}
ubus wait_for hostapd
- local hostapd_res="$(ubus_call hostapd config_set "{ \"phy\": \"$phy\", \"config\":\"${hostapd_conf_file}\", \"prev_config\": \"${hostapd_conf_file}.prev\"}")"
+ local hostapd_res="$(ubus_call hostapd config_set "{ \"phy\": \"$phy\", \"radio\": $radio, \"config\":\"${hostapd_conf_file}\", \"prev_config\": \"${hostapd_conf_file}.prev\"}")"
ret="$?"
[ "$ret" != 0 -o -z "$hostapd_res" ] && {
wireless_setup_failed HOSTAPD_START_FAILED
wpa_supplicant_start() {
local phy="$1"
+ local radio="$2"
[ -n "$wpa_supp_init" ] || return 0
- ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "num_global_macaddr": '"$num_global_macaddr"' }' > /dev/null
+ ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "num_global_macaddr": '"$num_global_macaddr"' }' > /dev/null
}
mac80211_setup_supplicant() {
}
mac80211_reset_config() {
- local phy="$1"
+ hostapd_conf_file="/var/run/hostapd-$phy$vif_phy_suffix.conf"
+ ubus_call hostapd config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null
+ ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": [] }' > /dev/null
+ wdev_tool "$phy$phy_suffix" set_config '{}'
+}
- hostapd_conf_file="/var/run/hostapd-$phy.conf"
- ubus_call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null
- ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "config": [] }' > /dev/null
- wdev_tool "$phy" set_config '{}'
+mac80211_set_suffix() {
+ [ "$radio" = "-1" ] && radio=
+ phy_suffix="${radio:+:$radio}"
+ vif_phy_suffix="${radio:+.$radio}"
+ set_default radio -1
}
drv_mac80211_setup() {
json_select config
json_get_vars \
- phy macaddr path \
+ radio phy macaddr path \
country chanbw distance \
txpower \
rxantenna txantenna \
json_get_values scan_list scan_list
json_select ..
+ mac80211_set_suffix
+
json_select data && {
json_get_var prev_rxantenna rxantenna
json_get_var prev_txantenna txantenna
}
}
- hostapd_conf_file="/var/run/hostapd-$phy.conf"
+ hostapd_conf_file="/var/run/hostapd-$phy$vif_phy_suffix.conf"
macidx=0
staidx=0
[ "$rxantenna" = "all" ] && rxantenna=0xffffffff
[ "$rxantenna" = "$prev_rxantenna" -a "$txantenna" = "$prev_txantenna" ] || mac80211_reset_config "$phy"
- wireless_set_data phy="$phy" txantenna="$txantenna" rxantenna="$rxantenna"
+ wireless_set_data phy="$phy" radio="$radio" txantenna="$txantenna" rxantenna="$rxantenna"
iw phy "$phy" set antenna $txantenna $rxantenna >/dev/null 2>&1
iw phy "$phy" set distance "$distance" >/dev/null 2>&1
- if [ -n "$txpower" ]; then
- iw phy "$phy" set txpower fixed "${txpower%%.*}00"
- else
- iw phy "$phy" set txpower auto
- fi
-
[ -n "$frag" ] && iw phy "$phy" set frag "${frag%%.*}"
[ -n "$rts" ] && iw phy "$phy" set rts "${rts%%.*}"
for_each_interface "ap sta adhoc mesh monitor" mac80211_prepare_vif
for_each_interface "ap sta adhoc mesh monitor" mac80211_setup_vif
- [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy"
- [ -x /usr/sbin/hostapd ] && hostapd_set_config "$phy"
+ [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy" "$radio"
+ [ -x /usr/sbin/hostapd ] && hostapd_set_config "$phy" "$radio"
- [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy"
+ [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy" "$radio"
json_set_namespace wdev_uc prev
- wdev_tool "$phy" set_config "$(json_dump)" $active_ifnames
+ wdev_tool "$phy$phy_suffix" set_config "$(json_dump)" $active_ifnames
json_set_namespace "$prev"
for_each_interface "ap sta adhoc mesh monitor" mac80211_set_vif_txpower
drv_mac80211_teardown() {
json_select data
- json_get_vars phy
+ json_get_vars phy radio
json_select ..
[ -n "$phy" ] || {
echo "Bug: PHY is undefined for device '$1'"
return 1
}
+ mac80211_set_suffix
mac80211_reset_config "$phy"
-
- for wdev in $(list_phy_interfaces "$phy"); do
- ip link set dev "$wdev" down
- iw dev "$wdev" del
- done
}
add_driver mac80211
let config = uci.cursor().get_all("wireless") ?? {};
-function radio_exists(path, macaddr, phy) {
+function radio_exists(path, macaddr, phy, radio) {
for (let name, s in config) {
if (s[".type"] != "wifi-device")
continue;
+ if (radio != null && int(s.radio) != radio)
+ continue;
if (s.macaddr & lc(s.macaddr) == lc(macaddr))
return true;
if (s.phy == phy)
if (!info || !length(info.bands))
continue;
- while (config[`radio${idx}`])
- idx++;
- let name = "radio" + idx++;
+ let radios = length(info.radios) > 0 ? info.radios : [{ bands: info.bands }];
+ for (let radio in radios) {
+ while (config[`radio${idx}`])
+ idx++;
+ let name = "radio" + idx;
- let s = "wireless." + name;
- let si = "wireless.default_" + name;
+ let s = "wireless." + name;
+ let si = "wireless.default_" + name;
- let band_name = filter(bands_order, (b) => info.bands[b])[0];
- if (!band_name)
- continue;
+ let band_name = filter(bands_order, (b) => radio.bands[b])[0];
+ if (!band_name)
+ continue;
- let band = info.bands[band_name];
- let channel = band.default_channel ?? "auto";
+ let band = info.bands[band_name];
+ let rband = radio.bands[band_name];
+ let channel = rband.default_channel ?? "auto";
- let width = band.max_width;
- if (band_name == "2G")
- width = 20;
- else if (width > 80)
- width = 80;
+ let width = band.max_width;
+ if (band_name == "2G")
+ width = 20;
+ else if (width > 80)
+ width = 80;
- let htmode = filter(htmode_order, (m) => band[lc(m)])[0];
- if (htmode)
- htmode += width;
- else
- htmode = "NOHT";
+ let htmode = filter(htmode_order, (m) => band[lc(m)])[0];
+ if (htmode)
+ htmode += width;
+ else
+ htmode = "NOHT";
- if (!phy.path)
- continue;
+ if (!phy.path)
+ continue;
- let macaddr = trim(readfile(`/sys/class/ieee80211/${phy_name}/macaddress`));
- if (radio_exists(phy.path, macaddr, phy_name))
- continue;
+ let macaddr = trim(readfile(`/sys/class/ieee80211/${phy_name}/macaddress`));
+ if (radio_exists(phy.path, macaddr, phy_name, radio.index))
+ continue;
- let id = `phy='${phy_name}'`;
- if (match(phy_name, /^phy[0-9]/))
- id = `path='${phy.path}'`;
+ let id = `phy='${phy_name}'`;
+ if (match(phy_name, /^phy[0-9]/))
+ id = `path='${phy.path}'`;
- band_name = lc(band_name);
+ band_name = lc(band_name);
- let country, defaults, num_global_macaddr;
- if (board.wlan.defaults) {
- defaults = board.wlan.defaults.ssids?.[band_name]?.ssid ? board.wlan.defaults.ssids?.[band_name] : board.wlan.defaults.ssids?.all;
- country = board.wlan.defaults.country;
- if (!country && band_name != '2g')
- defaults = null;
- num_global_macaddr = board.wlan.defaults.ssids?.[band_name]?.mac_count;
- }
+ let country, defaults, num_global_macaddr;
+ if (board.wlan.defaults) {
+ defaults = board.wlan.defaults.ssids?.[band_name]?.ssid ? board.wlan.defaults.ssids?.[band_name] : board.wlan.defaults.ssids?.all;
+ country = board.wlan.defaults.country;
+ if (!country && band_name != '2g')
+ defaults = null;
+ num_global_macaddr = board.wlan.defaults.ssids?.[band_name]?.mac_count;
+ }
- print(`set ${s}=wifi-device
+ if (length(info.radios) > 0)
+ id += `\nset ${s}.radio='${radio.index}'`;
+
+ print(`set ${s}=wifi-device
set ${s}.type='mac80211'
set ${s}.${id}
set ${s}.band='${band_name}'
set ${si}.key='${defaults?.key || ""}'
`);
- commit = true;
+ config[name] = {};
+ commit = true;
+ }
}
if (commit)
import * as nl80211 from "nl80211";
import * as rtnl from "rtnl";
-import { readfile, glob, basename, readlink } from "fs";
+import { readfile, glob, basename, readlink, open } from "fs";
const iftypes = {
ap: nl80211.const.NL80211_IFTYPE_AP,
return null;
}
+function wdev_set_radio_mask(name, mask)
+{
+ nl80211.request(nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
+ dev: name,
+ vif_radio_mask: mask
+ });
+}
+
function wdev_create(phy, name, data)
{
let phyidx = int(readfile(`/sys/class/ieee80211/${phy}/index`));
req["4addr"] = data["4addr"];
if (data.macaddr)
req.mac = data.macaddr;
+ if (data.radio != null && data.radio >= 0)
+ req.vif_radio_mask = 1 << data.radio;
nl80211.error();
let reuse_ifname = find_reusable_wdev(phyidx);
if (reuse_ifname &&
(reuse_ifname == name ||
- rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false))
- nl80211.request(
- nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
- wiphy: phyidx,
- dev: name,
- iftype: iftypes[data.mode],
- });
- else
+ rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false)) {
+ req.dev = req.ifname;
+ delete req.ifname;
+ nl80211.request(nl80211.const.NL80211_CMD_SET_INTERFACE, 0, req);
+ } else {
nl80211.request(
nl80211.const.NL80211_CMD_NEW_INTERFACE,
nl80211.const.NLM_F_CREATE,
req);
+ }
let error = nl80211.error();
if (error)
},
macaddr_generate: function(data) {
- let phy = this.name;
+ let phy = this.phy;
+ let radio_idx = this.radio;
let idx = int(data.id ?? 0);
let mbssid = int(data.mbssid ?? 0) > 0;
let num_global = int(data.num_global ?? 1);
if (!base_addr)
return null;
- if (!idx && !mbssid)
- return base_addr;
-
let base_mask = phy_sysfs_file(phy, "address_mask");
if (!base_mask)
return null;
- if (base_mask == "00:00:00:00:00:00" && idx >= num_global) {
+ if (base_mask == "00:00:00:00:00:00" &&
+ (radio_idx > 0 || idx >= num_global)) {
let addrs = split(phy_sysfs_file(phy, "addresses"), "\n");
- if (idx < length(addrs))
- return addrs[idx];
+ if (radio_idx != null) {
+ if (radio_idx && radio_idx < length(addrs))
+ base_addr = addrs[radio_idx];
+ else
+ idx += radio_idx * 16;
+ } else {
+ if (idx < length(addrs))
+ return addrs[idx];
- base_mask = "ff:ff:ff:ff:ff:ff";
+ base_mask = "ff:ff:ff:ff:ff:ff";
+ }
}
+ if (!idx && !mbssid)
+ return base_addr;
+
let addr = macaddr_split(base_addr);
let mask = macaddr_split(base_mask);
let type;
}
},
+ wdev_add: function(name, data) {
+ let phydev = this;
+ wdev_create(this.phy, name, {
+ ...data,
+ radio: this.radio,
+ });
+ },
+
for_each_wdev: function(cb) {
- let wdevs = glob(`/sys/class/ieee80211/${this.name}/device/net/*`);
- wdevs = map(wdevs, (arg) => basename(arg));
+ let wdevs = nl80211.request(
+ nl80211.const.NL80211_CMD_GET_INTERFACE,
+ nl80211.const.NLM_F_DUMP,
+ { wiphy: this.idx }
+ );
+
+ let mac_wdev = {};
for (let wdev in wdevs) {
- if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != this.name)
+ if (wdev.iftype == nl80211.const.NL80211_IFTYPE_AP_VLAN)
+ continue;
+ if (this.radio != null && wdev.vif_radio_mask != null &&
+ !(wdev.vif_radio_mask & (1 << this.radio)))
continue;
+ mac_wdev[wdev.mac] = wdev;
+ }
- cb(wdev);
+ for (let wdev in wdevs) {
+ if (!mac_wdev[wdev.mac])
+ continue;
+
+ cb(wdev.ifname);
}
}
};
-function phy_open(phy)
+function phy_open(phy, radio)
{
let phyidx = readfile(`/sys/class/ieee80211/${phy}/index`);
if (!phyidx)
return null;
+ let name = phy;
+ if (radio === "" || radio < 0)
+ radio = null;
+ if (radio != null)
+ name += "." + radio;
+
return proto({
- name: phy,
- idx: int(phyidx)
+ phy, name, radio,
+ idx: int(phyidx),
}, phy_proto);
}
function vlist_new(cb) {
return proto({
- cb: cb,
- data: {}
- }, vlist_proto);
+ cb: cb,
+ data: {}
+ }, vlist_proto);
}
-export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };
+export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_radio_mask, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };
#!/usr/bin/env ucode
'use strict';
-import { vlist_new, is_equal, wdev_create, wdev_set_mesh_params, wdev_remove, wdev_set_up, phy_open } from "/usr/share/hostap/common.uc";
+import { vlist_new, is_equal, wdev_set_mesh_params, wdev_remove, wdev_set_up, phy_open } from "/usr/share/hostap/common.uc";
import { readfile, writefile, basename, readlink, glob } from "fs";
let libubus = require("ubus");
let keep_devices = {};
-let phy = shift(ARGV);
+let phy_name = shift(ARGV);
let command = shift(ARGV);
-let phydev;
+let phy, phydev;
function iface_stop(wdev)
{
wdev_config[key] = wdev[key];
if (!wdev_config.macaddr && wdev.mode != "monitor")
wdev_config.macaddr = phydev.macaddr_next();
- wdev_create(phy, ifname, wdev_config);
+ phydev.wdev_add(ifname, wdev_config);
wdev_set_up(ifname, true);
let htmode = wdev.htmode || "NOHT";
if (wdev.freq)
delete config[key].ifname;
}
-function add_existing(phy, config)
+function add_existing(phydev, config)
{
- let wdevs = glob(`/sys/class/ieee80211/${phy}/device/net/*`);
- wdevs = map(wdevs, (arg) => basename(arg));
- for (let wdev in wdevs) {
+ phydev.for_each_wdev((wdev) => {
if (config[wdev])
- continue;
-
- if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != phy)
- continue;
+ return;
if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
config[wdev] = {};
- }
+ });
}
function usage()
const commands = {
set_config: function(args) {
- let statefile = `/var/run/wdev-${phy}.json`;
+ let statefile = `/var/run/wdev-${phy_name}.json`;
let new_config = shift(args);
for (let dev in ARGV)
if (type(old_config) == "object")
config.data = old_config;
- add_existing(phy, config.data);
+ add_existing(phydev, config.data);
add_ifname(config.data);
drop_inactive(config.data);
let ubus = libubus.connect();
- let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phy });
+ let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.name, radio: phydev.radio ?? -1 });
let macaddr_list = [];
if (type(data) == "object" && data.macaddr)
macaddr_list = data.macaddr;
let macaddr = phydev.macaddr_generate(data);
if (!macaddr) {
- warn(`Could not get MAC address for phy ${phy}\n`);
+ warn(`Could not get MAC address for phy ${phy_name}\n`);
exit(1);
}
},
};
-if (!phy || !command | !commands[command])
+if (!phy_name || !command | !commands[command])
usage();
-phydev = phy_open(phy);
+let phy_split = split(phy_name, ":");
+phydev = phy_open(phy_split[0], phy_split[1]);
+phy = phydev.phy;
if (!phydev) {
- warn(`PHY ${phy} does not exist\n`);
+ warn(`PHY ${phy_name} does not exist\n`);
exit(1);
}
return 0;
}
+function freq_range_match(ranges, freq) {
+ freq *= 1000;
+ for (let range in ranges) {
+ if (freq >= range[0] && freq <= range[1])
+ return true;
+ }
+ return false;
+}
+
function wiphy_detect() {
let phys = nl.request(nl.const.NL80211_CMD_GET_WIPHY, nl.const.NLM_F_DUMP, { split_wiphy_dump: true });
if (!phys)
antenna_rx: phy.wiphy_antenna_avail_rx,
antenna_tx: phy.wiphy_antenna_avail_tx,
bands: {},
+ radios: []
};
+ for (let radio in phy.radios) {
+ // S1G is not supported yet
+ radio.freq_ranges = filter(radio.freq_ranges,
+ (range) => range.end > 2000000
+ );
+
+ if (!length(radio.freq_ranges))
+ continue;
+
+ push(info.radios, {
+ index: radio.index,
+ freq_ranges: map(radio.freq_ranges,
+ (range) => [ range.start, range.end ]
+ ),
+ bands: {}
+ });
+ }
+
let bands = info.bands;
for (let band in phy.wiphy_bands) {
if (!band || !band.freqs)
if (eht_phy_cap && he_phy_cap & 2)
push(modes, "EHT40");
+ for (let radio in info.radios) {
+ let freq_match = filter(band.freqs,
+ (freq) => freq_range_match(radio.freq_ranges, freq.freq)
+ );
+ if (!length(freq_match))
+ continue;
+
+ let radio_band = {};
+ radio.bands[band_name] = radio_band;
+
+ freq_match = filter(freq_match,
+ (freq) => !freq.disabled
+ );
+
+ let freq = freq_match[0];
+ if (freq)
+ radio_band.default_channel = freq_to_channel(freq.freq);
+ }
+
for (let freq in band.freqs) {
if (freq.disabled)
continue;
let libubus = require("ubus");
import { open, readfile } from "fs";
-import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
+import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask } from "common";
let ubus = libubus.connect(null, 60);
wdev_remove(bss.ifname);
}
-function iface_gen_config(phy, config, start_disabled)
+function iface_gen_config(config, start_disabled)
{
let str = `data:
${join("\n", config.radio.data)}
function iface_add(phy, config, phy_status)
{
- let config_inline = iface_gen_config(phy, config, !!phy_status);
+ let config_inline = iface_gen_config(config, !!phy_status);
let bss = config.bss[0];
let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
return macaddr_list;
}
-function iface_update_supplicant_macaddr(phy, config)
+function iface_update_supplicant_macaddr(phydev, config)
{
let macaddr_list = [];
for (let i = 0; i < length(config.bss); i++)
push(macaddr_list, config.bss[i].bssid);
- ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
+ ubus.defer("wpa_supplicant", "phy_set_macaddr_list", {
+ phy: phydev.name,
+ radio: phydev.radio ?? -1,
+ macaddr: macaddr_list
+ });
}
function __iface_pending_next(pending, state, ret, data)
delete pending.defer;
switch (state) {
case "init":
- let macaddr_list = [];
- for (let i = 0; i < length(config.bss); i++)
- push(macaddr_list, config.bss[i].bssid);
- pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
+ iface_update_supplicant_macaddr(phydev, config);
return "create_bss";
case "create_bss":
- let err = wdev_create(phy, bss.ifname, { mode: "ap" });
+ let err = phydev.wdev_add(bss.ifname, {
+ mode: "ap",
+ radio: phydev.radio,
+ });
if (err) {
hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
return null;
}
- pending.call("wpa_supplicant", "phy_status", { phy: phy });
+ pending.call("wpa_supplicant", "phy_status", {
+ phy: phydev.phy,
+ radio: phydev.radio,
+ });
return "check_phy";
case "check_phy":
let phy_status = data;
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
}
- pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
+ pending.call("wpa_supplicant", "phy_set_state", {
+ phy: phydev.phy,
+ radio: phydev.radio,
+ stop: true
+ });
return "wpas_stopped";
case "wpas_stopped":
if (!iface_add(phy, config))
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
- pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
+ pending.call("wpa_supplicant", "phy_set_state", {
+ phy: phydev.phy,
+ radio: phydev.radio,
+ stop: false
+ });
return null;
case "done":
default:
let cfg = this;
while (pending) {
- this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
- if (!this.next_state) {
- __iface_pending_next(cfg, "done");
+ try {
+ this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
+ if (!this.next_state) {
+ __iface_pending_next(cfg, "done");
+ return;
+ }
+ } catch(e) {
+ hostapd.printf(`Exception: ${e}\n${e.stacktrace[0].context}`);
return;
}
pending = !this.defer;
return hostapd.bss[ifname];
}
-function iface_reload_config(phydev, config, old_config)
+function iface_reload_config(name, phydev, config, old_config)
{
let phy = phydev.name;
if (is_equal(old_config.bss, config.bss))
return true;
- if (hostapd.data.pending_config[phy])
+ if (hostapd.data.pending_config[name])
return false;
if (!old_config.bss || !old_config.bss[0])
return false;
- let iface = hostapd.interfaces[phy];
+ let iface = hostapd.interfaces[name];
let iface_name = old_config.bss[0].ifname;
if (!iface) {
hostapd.printf(`Could not find previous interface ${iface_name}`);
return false;
let ifname = old_config.bss[i].ifname;
- hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
+ hostapd.printf(`Remove bss '${ifname}' on phy '${name}'`);
prev_bss.delete();
wdev_remove(ifname);
}
let addr = phydev.macaddr_next(i);
if (!addr) {
- hostapd.printf(`Failed to generate mac address for phy ${phy}`);
+ hostapd.printf(`Failed to generate mac address for phy ${name}`);
return false;
}
bsscfg.bssid = addr;
}
- let config_inline = iface_gen_config(phy, config);
+ let config_inline = iface_gen_config(config);
// Step 7: fill in the gaps with new interfaces
for (let i = 0; i < length(config.bss); i++) {
if (bss)
continue;
- hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
+ hostapd.printf(`Add bss ${ifname} on phy ${name}`);
bss_list[i] = iface.add_bss(config_inline, i);
if (!bss_list[i]) {
- hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
+ hostapd.printf(`Failed to add new bss ${ifname} on phy ${name}`);
return false;
}
}
// Step 8: update interface bss order
if (!iface.set_bss_order(bss_list)) {
- hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
+ hostapd.printf(`Failed to update BSS order on phy '${name}'`);
return false;
}
if (is_equal(config.bss[i], bss_list_cfg[i]))
continue;
- hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
+ hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${name}'`);
if (bss.set_config(config_inline, i) < 0) {
hostapd.printf(`Failed to set config for bss ${ifname}`);
return false;
return true;
}
-function iface_set_config(phy, config)
+function iface_set_config(name, config)
{
- let old_config = hostapd.data.config[phy];
+ let old_config = hostapd.data.config[name];
- hostapd.data.config[phy] = config;
+ hostapd.data.config[name] = config;
if (!config) {
- hostapd.remove_iface(phy);
+ hostapd.remove_iface(name);
return iface_remove(old_config);
}
- let phydev = phy_open(phy);
+ let phy = config.phy;
+ let phydev = phy_open(phy, config.radio_idx);
if (!phydev) {
hostapd.printf(`Failed to open phy ${phy}`);
return false;
}
try {
- let ret = iface_reload_config(phydev, config, old_config);
+ let ret = iface_reload_config(name, phydev, config, old_config);
if (ret) {
- iface_update_supplicant_macaddr(phy, config);
- hostapd.printf(`Reloaded settings for phy ${phy}`);
+ iface_update_supplicant_macaddr(phydev, config);
+ hostapd.printf(`Reloaded settings for phy ${name}`);
return 0;
}
} catch (e) {
- hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
+ hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
}
- hostapd.printf(`Restart interface for phy ${phy}`);
+ hostapd.printf(`Restart interface for phy ${name}`);
let ret = iface_restart(phydev, config, old_config);
return ret;
return bss;
}
-function iface_load_config(filename)
+function iface_load_config(phy, radio, filename)
{
let f = open(filename, "r");
if (!f)
return null;
+ if (radio < 0)
+ radio = null;
+
let config = {
+ phy,
+ radio_idx: radio,
radio: {
data: []
},
};
}
+function phy_name(phy, radio)
+{
+ if (!phy)
+ return null;
+
+ if (radio != null && radio >= 0)
+ phy += "." + radio;
+
+ return phy;
+}
+
function bss_config(bss_name) {
for (let phy, config in hostapd.data.config) {
if (!config)
reload: {
args: {
phy: "",
+ radio: 0,
},
call: ex_wrap(function(req) {
- let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
+ let phy_list = req.args.phy ? [ phy_name(req.args.phy, req.args.radio) ] : keys(hostapd.data.config);
for (let phy_name in phy_list) {
let phy = hostapd.data.config[phy_name];
- let config = iface_load_config(phy.orig_file);
+ let config = iface_load_config(phy.phy, radio, phy.orig_file);
iface_set_config(phy_name, config);
}
apsta_state: {
args: {
phy: "",
+ radio: 0,
up: true,
frequency: 0,
sec_chan_offset: 0,
csa_count: 0,
},
call: ex_wrap(function(req) {
- if (req.args.up == null || !req.args.phy)
+ let phy = phy_name(req.args.phy, req.args.radio);
+ if (req.args.up == null || !phy)
return libubus.STATUS_INVALID_ARGUMENT;
- let phy = req.args.phy;
let config = hostapd.data.config[phy];
if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
return 0;
},
config_get_macaddr_list: {
args: {
- phy: ""
+ phy: "",
+ radio: 0,
},
call: ex_wrap(function(req) {
- let phy = req.args.phy;
+ let phy = phy_name(req.args.phy, req.args.radio);
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
config_set: {
args: {
phy: "",
+ radio: 0,
config: "",
prev_config: "",
},
call: ex_wrap(function(req) {
let phy = req.args.phy;
+ let radio = req.args.radio;
+ let name = phy_name(phy, radio);
let file = req.args.config;
let prev_file = req.args.prev_config;
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
- if (prev_file && !hostapd.data.config[phy]) {
- let config = iface_load_config(prev_file);
+ if (prev_file && !hostapd.data.config[name]) {
+ let config = iface_load_config(phy, radio, prev_file);
if (config)
config.radio.data = [];
- hostapd.data.config[phy] = config;
+ hostapd.data.config[name] = config;
}
- let config = iface_load_config(file);
+ let config = iface_load_config(phy, radio, file);
- hostapd.printf(`Set new config for phy ${phy}: ${file}`);
- iface_set_config(phy, config);
+ hostapd.printf(`Set new config for phy ${name}: ${file}`);
+ iface_set_config(name, config);
if (hostapd.data.auth_obj)
- hostapd.data.auth_obj.notify("reload", { phy });
+ hostapd.data.auth_obj.notify("reload", { phy, radio });
return {
pid: hostapd.getpid()
return {
shutdown: function() {
for (let phy in hostapd.data.config)
- iface_set_config(phy, null);
+ iface_set_config(phy);
hostapd.udebug_set(null);
hostapd.ubus.disconnect();
},
+ bss_create: function(phy, name, obj) {
+ phy = hostapd.data.config[phy];
+ if (!phy)
+ return;
+
+ if (phy.radio_idx != null && phy.radio_idx >= 0)
+ wdev_set_radio_mask(name, 1 << phy.radio_idx);
+ },
bss_add: function(phy, name, obj) {
bss_event("add", name);
},
wpas.data.iface_phy[ifname] = phy;
wdev_remove(ifname);
- let ret = wdev_create(phy, ifname, wdev_config);
+ let ret = phydev.wdev_add(ifname, wdev_config);
if (ret)
wpas.printf(`Failed to create device ${ifname}: ${ret}`);
wdev_set_up(ifname, true);
iface_stop(old_if);
}
-function prepare_config(config)
+function prepare_config(config, radio)
{
config.config_data = readfile(config.config);
- return { config: config };
+ return { config };
}
-function set_config(phy_name, num_global_macaddr, config_list)
+function set_config(config_name, phy_name, radio, num_global_macaddr, config_list)
{
- let phy = wpas.data.config[phy_name];
+ let phy = wpas.data.config[config_name];
+
+ if (radio < 0)
+ radio = null;
if (!phy) {
phy = vlist_new(iface_cb, false);
- wpas.data.config[phy_name] = phy;
+ phy.name = phy_name;
+ wpas.data.config[config_name] = phy;
}
+ phy.radio = radio;
phy.num_global_macaddr = num_global_macaddr;
let values = [];
if (!phy || !phy.data)
return;
- let phydev = phy_open(phy_name);
+ let phydev = phy_open(phy.name, phy.radio);
if (!phydev) {
wpas.printf(`Could not open phy ${phy_name}`);
return;
iface_start(phydev, phy.data[ifname]);
}
+function phy_name(phy, radio)
+{
+ if (!phy)
+ return null;
+
+ if (radio != null && radio >= 0)
+ phy += "." + radio;
+
+ return phy;
+}
+
let main_obj = {
phy_set_state: {
args: {
phy: "",
+ radio: 0,
stop: true,
},
call: function(req) {
- if (!req.args.phy || req.args.stop == null)
+ let name = phy_name(req.args.phy, req.args.radio);
+ if (!name || req.args.stop == null)
return libubus.STATUS_INVALID_ARGUMENT;
- let phy = wpas.data.config[req.args.phy];
+ let phy = wpas.data.config[name];
if (!phy)
return libubus.STATUS_NOT_FOUND;
for (let ifname in phy.data)
iface_stop(phy.data[ifname]);
} else {
- start_pending(req.args.phy);
+ start_pending(name);
}
} catch (e) {
wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
phy_set_macaddr_list: {
args: {
phy: "",
+ radio: 0,
macaddr: [],
},
call: function(req) {
- let phy = req.args.phy;
+ let phy = phy_name(req.args.phy, req.args.radio);
if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
},
phy_status: {
args: {
- phy: ""
+ phy: "",
+ radio: 0,
},
call: function(req) {
- if (!req.args.phy)
+ let phy = phy_name(req.args.phy, req.args.radio);
+ if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
- let phy = wpas.data.config[req.args.phy];
+ phy = wpas.data.config[phy];
if (!phy)
return libubus.STATUS_NOT_FOUND;
config_set: {
args: {
phy: "",
+ radio: 0,
num_global_macaddr: 0,
config: [],
defer: true,
},
call: function(req) {
- if (!req.args.phy)
+ let phy = phy_name(req.args.phy, req.args.radio);
+ if (!phy)
return libubus.STATUS_INVALID_ARGUMENT;
- wpas.printf(`Set new config for phy ${req.args.phy}`);
+ wpas.printf(`Set new config for phy ${phy}`);
try {
if (req.args.config)
- set_config(req.args.phy, req.args.num_global_macaddr, req.args.config);
+ set_config(phy, req.args.phy, req.args.radio, req.args.num_global_macaddr, req.args.config);
if (!req.args.defer)
- start_pending(req.args.phy);
+ start_pending(phy);
} catch (e) {
wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
return libubus.STATUS_INVALID_ARGUMENT;