From 3297e767256ab4f43b1df9ac531266c132c7d9a6 Mon Sep 17 00:00:00 2001 From: a-gave Date: Fri, 26 Dec 2025 02:32:50 +0100 Subject: [PATCH] lime-system: wireless: add option distance_use_auto_if_available Closes #932 Introduce the wifi option `distance_use_auto_if_available` by default true to enforce the value "auto" as distance for radio drivers that supports it like kmod-ath9k when compiled with CONFIG_PACKAGE_ATH_DYNACK=y Implement a check if driver supports auto setting. At firstboot run 'iw phyX set distance auto' relying on the iw error i.e. 'command failed: Not supported (-95)' if unsupported Save the result in lime-autogen in a format like lime-node.radio0.dynack=0 Re run the iw command on the same phy when the macaddress has changed in case of powering off and plugging a different usb wireless adapter Implement a check if distance 'auto' is requested for a driver that does not support it. Use the lime-defaults value as safer option than allowing to set "auto", which could lead to the minimum being applied at next reboot, potentially compromising long distance wireless links. If auto is requested for a driver that doesn't support it iw will fail with 'command failed: Not supported (-95)', the value auto is not applied and the previous valid value i.e. '1000' is kept However at next reboot if the value auto was saved in /etc/config/wireless the coverage will be set to 0 on tested hardware. To verify the applied coverage on a given phyX: iw phy phyX info | grep Coverage In ath9k check the ack timeout in uS, followed by A for auto-determined or S set: cat /sys/kernel/debug/ieee80211/phy0/ath9k/ack_to Further resources and links - "[RFC] API for setting ACK timeout" thread on ml linux wireless https://www.spinics.net/lists/linux-wireless/msg43572.html - Introduction in linux of setting coverage class for long distance wireless links: https://linux-wireless.vger.kernel.narkive.com/qo3X9KdF/patch-0-4-setting-coverage-class-and-ack-timeout-and-slot-time - Introduction in iw of dynack: https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/commit/?id=e642142d68850b7eb161489a984ec6817b10c51b - Support of dynack in openwrt: https://git.openwrt.org/?p=openwrt%2Fopenwrt.git&a=search&s=dynack - Comments from communities using dynack: https://www.arednmesh.org/content/auto-distance-setting-0 Due to different driver implementations, producing a list of wireless drivers that support different distance/coverage optimizations for outdoor usage could be complex. Looking at the source code a starting point could be to check drivers that handle NL80211_ATTR_WIPHY_COVERAGE_CLASS i.e. grep -rl 'distance\|coverage' /drivers/net/wireless/ Among those, currently dynack is supported just by ath9k Tested on these devices running lime master and openwrt 24.10.x or snapshot: - ubnt_litebeam-m5-xw - gemtek_wvrtm-127acn - gemtek_wvrtm-130acn - cudy_wr3000s-v1 - openwrt_one (usb adapter rtl8192cu) --- .../lime-docs/files/www/docs/lime-example.txt | 1 + .../files/etc/config/lime-defaults | 1 + .../files/usr/lib/lua/lime/wireless.lua | 51 ++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/lime-docs/files/www/docs/lime-example.txt b/packages/lime-docs/files/www/docs/lime-example.txt index 37a54e531..44dbe22f2 100644 --- a/packages/lime-docs/files/www/docs/lime-example.txt +++ b/packages/lime-docs/files/www/docs/lime-example.txt @@ -154,6 +154,7 @@ config lime wifi # that rescan all available frequencies in active radios. option unstuck_timeout '300' # Timeout in seconds that defines how long the mentioned above workaround should go. + option distance_use_auto_if_available true ######################################################### ### WiFi specific band options diff --git a/packages/lime-system/files/etc/config/lime-defaults b/packages/lime-system/files/etc/config/lime-defaults index dd13cadd6..1dc41cf2b 100644 --- a/packages/lime-system/files/etc/config/lime-defaults +++ b/packages/lime-system/files/etc/config/lime-defaults @@ -64,6 +64,7 @@ config lime wifi option ieee80211s_mesh_id 'LiMe' option unstuck_interval '10' option unstuck_timeout '300' + option distance_use_auto_if_available true config lime-wifi-band '2ghz' option channel '11' diff --git a/packages/lime-system/files/usr/lib/lua/lime/wireless.lua b/packages/lime-system/files/usr/lib/lua/lime/wireless.lua index 7c55a9054..c463cb8e4 100644 --- a/packages/lime-system/files/usr/lib/lua/lime/wireless.lua +++ b/packages/lime-system/files/usr/lib/lua/lime/wireless.lua @@ -212,7 +212,7 @@ function wireless.configure() local uci = config.get_uci_cursor() uci:set("wireless", radioName, "disabled", 0) - uci:set("wireless", radioName, "distance", options["distance"]) + uci:set("wireless", radioName, "distance", wireless.get_distance(radioName, options)) uci:set("wireless", radioName, "noscan", 1) uci:set("wireless", radioName, "channel", channel) if options["country"] then uci:set("wireless", radioName, "country", options["country"]) end @@ -238,6 +238,8 @@ function wireless.configure() and (not key:match("distance")) and (not key:match("unstuck_interval")) and (not key:match("unstuck_timeout")) + and (not key:match("dynack")) + and (not key:match("phy_macaddr")) and (not (wireless.isMode(keyPrefix) and keyPrefix ~= modeName))) if isGoodOption then local nk = key:gsub("^"..modeName.."_", "") @@ -322,4 +324,51 @@ function wireless.set_band_config(band, cfg) utils.unsafe_shell('lime-config') end +--! Check if the driver supports dynack. +--! At firstboot run 'iw phyX set distance auto'. Rely on the iw error i.e. 'command failed: Not supported (-95)' +--! and save the result in lime-autogen i.e. lime-autogen.radio0.dynack=0 +--! Re run on the same phy when the macaddress has changed i.e. in case of plugging a different usb wireless adapter +function wireless.is_distance_auto_available(radioName) + local phy = "phy"..utils.indexFromName(radioName) + local phy_mac = utils.unsafe_shell("cat /sys/class/ieee80211/"..phy.."/macaddress 2>/dev/null"):gsub("\n","") + local uci = config.get_uci_cursor() + + uci:set(config.UCI_AUTOGEN_NAME, radioName, "wifi") + --! mac unchanged + if (phy_mac == uci:get(config.UCI_AUTOGEN_NAME, radioName, "phy_macaddr")) then + if uci:get(config.UCI_AUTOGEN_NAME, radioName, "dynack") ~= nil then + return uci:get(config.UCI_AUTOGEN_NAME, radioName, "dynack") + end + end + + local iw_cmd = utils.unsafe_shell("iw "..phy.." set distance auto 2>/dev/null; echo $(( $? == 0 ))"):gsub("\n","") + uci:set(config.UCI_AUTOGEN_NAME, radioName, "phy_macaddr", phy_mac) + uci:set(config.UCI_AUTOGEN_NAME, radioName, "dynack", iw_cmd) + uci:commit(config.UCI_AUTOGEN_NAME) + return iw_cmd +end + +--! If distance 'auto' is requested for a driver that does not support it, +--! use the lime-defaults value as safer option than allowing to set "auto" +--! which could lead to the minimum being applied at next reboot +--! potentially compromising long distance wireless links. +function wireless.apply_distance_auto_fallback(radioName,options) + if options["distance"] ~= "auto" then return options["distance"] end + local uci = config.get_uci_cursor() + local band = wireless.getRadioBand(radioName) + local distance_default = uci:get(config.UCI_DEFAULTS_NAME, band, "distance") + print('WARNING: wireless.get_distance: invalid "option distance \'auto\'" for '..band..' '..radioName.. + '. Fallback to lime-defaults value: '.. distance_default) + return distance_default +end + +function wireless.get_distance(radioName, options) + --! distance auto requested or enforced + if (options["distance"] == "auto" or options["distance_use_auto_if_available"] == 'true') then + if wireless.is_distance_auto_available(radioName) == '1' then return "auto" + else return wireless.apply_distance_auto_fallback(radioName,options) end + --! distance set + else return options["distance"] end +end + return wireless