let libubus = require("ubus");
+import * as uloop from "uloop";
import { open, readfile, access } from "fs";
import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common";
return phydev.macaddr_init(macaddr_list, macaddr_data);
}
+function csa_timer_cancel(name)
+{
+ let timers = hostapd.data.csa_timer;
+ if (timers && timers[name]) {
+ timers[name].cancel();
+ delete timers[name];
+ }
+}
+
function iface_restart(phydev, config, old_config)
{
let phy = phydev.name;
let pending = hostapd.data.pending_config[phy];
+ csa_timer_cancel(phy);
+
if (pending)
pending.abort();
(freq >= 5490 && freq <= 5730));
}
+function iface_csa_check(name)
+{
+ if (hostapd.data.csa_timer)
+ delete hostapd.data.csa_timer[name];
+
+ let config = hostapd.data.config[name];
+ let iface = hostapd.interfaces[name];
+ if (!config || !iface || !iface.csa_in_progress())
+ return;
+
+ hostapd.printf(`Config channel switch on phy ${name} did not complete, restarting`);
+ let phydev = phy_open(config.phy, config.radio_idx);
+ if (phydev)
+ iface_restart(phydev, config, config);
+}
+
function iface_channel_switch(name, config)
{
let radio = config.radio;
return false;
hostapd.printf(`Channel switch to ${radio.channel} on phy ${name}`);
+
+ /*
+ * Verify the switch actually completes; if the driver never finishes it,
+ * fall back to a full restart. Armed only here, so ubus- or apsta-
+ * triggered channel switches are left to their own recovery.
+ */
+ hostapd.data.csa_timer ??= {};
+ csa_timer_cancel(name);
+
+ let beacon_int = 100;
+ for (let line in config.radio.data) {
+ let m = match(line, /^beacon_int=([0-9]+)/);
+ if (m) {
+ beacon_int = int(m[1]);
+ break;
+ }
+ }
+
+ hostapd.data.csa_timer[name] = uloop.timer(freq_info.csa_count * beacon_int + 2000,
+ () => iface_csa_check(name));
+
return true;
}
return ucv_boolean_new(!ret);
}
+static uc_value_t *
+uc_hostapd_iface_csa_in_progress(uc_vm_t *vm, size_t nargs)
+{
+ struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+
+ if (!iface)
+ return NULL;
+
+ return ucv_boolean_new(hostapd_csa_in_progress(iface));
+}
+
static uc_value_t *
uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
{
{ "stop", uc_hostapd_iface_stop },
{ "start", uc_hostapd_iface_start },
{ "switch_channel", uc_hostapd_iface_switch_channel },
+ { "csa_in_progress", uc_hostapd_iface_csa_in_progress },
};
uc_value_t *data, *proto;