local sysconfig = require 'gluon.sysconfig' local site = require 'gluon.site' local util = require 'gluon.util' local unistd = require 'posix.unistd' local dirent = require 'posix.dirent' local M = {} local function find_phy_by_path(path) local device_path, phy_offset = string.match(path, "^(.+)%+(%d+)$") -- Special handling required for multi-phy devices if device_path == nil then device_path = path phy_offset = '0' end -- Find the device path. Either it's located at /sys/devices or /sys/devices/platform local path_prefix = '' if not unistd.access('/sys/devices/' .. device_path .. '/ieee80211') then path_prefix = 'platform/' end -- Get all available PHYs of the device and dertermine the one with the lowest index local phy_names = dirent.dir('/sys/devices/' .. path_prefix .. device_path .. '/ieee80211') local device_phy_idxs = {} for _, v in ipairs(phy_names) do local phy_idx = v:match('^phy(%d+)$') if phy_idx ~= nil then table.insert(device_phy_idxs, tonumber(phy_idx)) end end table.sort(device_phy_idxs) -- Index starts at 1 return 'phy' .. device_phy_idxs[tonumber(phy_offset) + 1] end local function find_phy_by_macaddr(macaddr) local addr = macaddr:lower() for _, file in ipairs(util.glob('/sys/class/ieee80211/*/macaddress')) do if util.trim(util.readfile(file)) == addr then return file:match('([^/]+)/macaddress$') end end end function M.find_phy(config) if not config or config.type ~= 'mac80211' then return nil elseif config.path then return find_phy_by_path(config.path) elseif config.macaddr then return find_phy_by_macaddr(config.macaddr) else return nil end end local function get_addresses(radio) local phy = M.find_phy(radio) if not phy then return function() end end return io.lines('/sys/class/ieee80211/' .. phy .. '/addresses') end local function get_wlan_mac_from_driver(radio, vif) local primary = sysconfig.primary_mac:lower() local addresses = {} for address in get_addresses(radio) do if address:lower() ~= primary then table.insert(addresses, address) end end -- Make sure we have at least 4 addresses if #addresses < 4 then return nil end for i, addr in ipairs(addresses) do if i == vif then return addr end end end function M.get_wlan_mac(_, radio, index, vif) local addr = get_wlan_mac_from_driver(radio, vif) if addr then return addr end return util.generate_mac(4*(index-1) + (vif-1)) end -- Iterate over all radios defined in UCI calling -- f(radio, index, site.wifiX) for each radio found while passing -- site.wifi24 for 2.4 GHz devices and site.wifi5 for 5 GHz ones. function M.foreach_radio(uci, f) local radios = {} uci:foreach('wireless', 'wifi-device', function(radio) table.insert(radios, radio) end) for index, radio in ipairs(radios) do local hwmode = radio.hwmode if hwmode == '11g' or hwmode == '11ng' then f(radio, index, site.wifi24) elseif hwmode == '11a' or hwmode == '11na' then f(radio, index, site.wifi5) end end end function M.preserve_channels(uci) return uci:get_first('gluon-core', 'wireless', 'preserve_channels') end function M.device_supports_wpa3() return unistd.access('/lib/gluon/features/wpa3') end function M.device_supports_mfp(uci) local supports_mfp = true if not M.device_supports_wpa3() then return false end uci:foreach('wireless', 'wifi-device', function(radio) local phy = M.find_phy(radio) local phypath = '/sys/kernel/debug/ieee80211/' .. phy .. '/' if not util.file_contains_line(phypath .. 'hwflags', 'MFP_CAPABLE') then supports_mfp = false return false end end) return supports_mfp end function M.device_uses_wlan(uci) local ret = false uci:foreach('wireless', 'wifi-device', function() ret = true return false end) return ret end function M.device_uses_11a(uci) local ret = false uci:foreach('wireless', 'wifi-device', function(radio) if radio.hwmode == '11a' or radio.hwmode == '11na' then ret = true return false end end) return ret end return M