local wa = require "luci.tools.webadmin"
local nw = require "luci.model.network"
+local ut = require "luci.util"
+local nt = require "luci.sys".net
local fs = require "nixio.fs"
arg[1] = arg[1] or ""
m = Map("wireless", "",
translate("The <em>Device Configuration</em> section covers physical settings of the radio " ..
- "hardware such as channel, transmit power or antenna selection which is shared among all " ..
+ "hardware such as channel, transmit power or antenna selection which are shared among all " ..
"defined wireless networks (if the radio hardware is multi-SSID capable). Per network settings " ..
"like encryption or operation mode are grouped in the <em>Interface Configuration</em>."))
wdev:set("disabled", nil)
nw:commit("wireless")
- luci.sys.call("(env -i /sbin/wifi down; env -i /sbin/wifi up) >/dev/null 2>/dev/null")
+ luci.sys.call("(env -i /bin/ubus call network reload) >/dev/null 2>/dev/null")
luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless", arg[1]))
return
m.title = luci.util.pcdata(wnet:get_i18n())
+local function txpower_list(iw)
+ local list = iw.txpwrlist or { }
+ local off = tonumber(iw.txpower_offset) or 0
+ local new = { }
+ local prev = -1
+ local _, val
+ for _, val in ipairs(list) do
+ local dbm = val.dbm + off
+ local mw = math.floor(10 ^ (dbm / 10))
+ if mw ~= prev then
+ prev = mw
+ new[#new+1] = {
+ display_dbm = dbm,
+ display_mw = mw,
+ driver_dbm = val.dbm,
+ driver_mw = val.mw
+ }
+ end
+ end
+ return new
+end
+
+local function txpower_current(pwr, list)
+ pwr = tonumber(pwr)
+ if pwr ~= nil then
+ local _, item
+ for _, item in ipairs(list) do
+ if item.driver_dbm >= pwr then
+ return item.driver_dbm
+ end
+ end
+ end
+ return (list[#list] and list[#list].driver_dbm) or pwr or 0
+end
+
local iw = luci.sys.wifi.getiwinfo(arg[1])
-local hw_modes = iw.hwmodelist or { }
-local tx_powers = iw.txpwrlist or { }
-local tx_power = tostring(
- (iw.txpower and iw.txpower > 0 and iw.txpower) or
- (#tx_powers > 0 and tx_powers[#tx_powers].dbm)
-)
+local hw_modes = iw.hwmodelist or { }
+local tx_power_list = txpower_list(iw)
+local tx_power_cur = txpower_current(wdev:get("txpower"), tx_power_list)
s = m:section(NamedSection, wdev:name(), "wifi-device", translate("Device Configuration"))
s.addremove = false
local hwtype = wdev:get("type")
-local htcaps = wdev:get("ht_capab") and true or false
-- NanoFoo
local nsantenna = wdev:get("antenna")
--- Check whether there is a client interface on the same radio,
--- if yes, lock the channel choice as the station will dicatate the freq
-local has_sta = nil
+-- Check whether there are client interfaces on the same radio,
+-- if yes, lock the channel choice as these stations will dicatate the freq
+local found_sta = nil
local _, net
-for _, net in ipairs(wdev:get_wifinets()) do
- if net:mode() == "sta" and net:id() ~= wnet:id() then
- has_sta = net
- break
+if wnet:mode() ~= "sta" then
+ for _, net in ipairs(wdev:get_wifinets()) do
+ if net:mode() == "sta" then
+ if not found_sta then
+ found_sta = {}
+ found_sta.channel = net:channel()
+ found_sta.names = {}
+ end
+ found_sta.names[#found_sta.names+1] = net:shortname()
+ end
end
end
-if has_sta then
+if found_sta then
ch = s:taboption("general", DummyValue, "choice", translate("Channel"))
- ch.value = translatef("Locked to channel %d used by %s",
- has_sta:channel(), has_sta:shortname())
+ ch.value = translatef("Locked to channel %d used by: %s",
+ found_sta.channel, table.concat(found_sta.names, ", "))
else
- ch = s:taboption("general", Value, "channel", translate("Channel"))
- ch:value("auto", translate("auto"))
- for _, f in ipairs(iw and iw.freqlist or luci.sys.wifi.channels()) do
- if not f.restricted then
- ch:value(f.channel, "%i (%.3f GHz)" %{ f.channel, f.mhz / 1000 })
- end
+ ch = s:taboption("general", Value, "_mode_freq", '<br />'..translate("Operating frequency"))
+ ch.hwmodes = iw.hwmodelist
+ ch.freqlist = iw.freqlist
+ ch.template = "cbi/wireless_modefreq"
+
+ function ch.cfgvalue(self, section)
+ return {
+ m:get(section, "hwmode") or "",
+ m:get(section, "channel") or "auto",
+ m:get(section, "htmode") or ""
+ }
+ end
+
+ function ch.formvalue(self, section)
+ return {
+ m:formvalue(self:cbid(section) .. ".band") or (iw.hwmodelist.g and "11g" or "11a"),
+ m:formvalue(self:cbid(section) .. ".channel") or "auto",
+ m:formvalue(self:cbid(section) .. ".htmode") or ""
+ }
+ end
+
+ function ch.write(self, section, value)
+ m:set(section, "hwmode", value[1])
+ m:set(section, "channel", value[2])
+ m:set(section, "htmode", value[3])
end
end
------------------- MAC80211 Device ------------------
if hwtype == "mac80211" then
- tp = s:taboption("general",
- (tx_powers and #tx_powers > 0) and ListValue or Value,
- "txpower", translate("Transmit Power"), "dBm")
-
- tp.rmempty = true
- tp.default = tx_power
- for _, p in ipairs(tx_powers or {}) do
- tp:value(p.dbm, "%i dBm (%i mW)" %{ p.dbm, p.mw })
- end
+ if #tx_power_list > 1 then
+ tp = s:taboption("general", ListValue,
+ "txpower", translate("Transmit Power"), "dBm")
+ tp.rmempty = true
+ tp.default = tx_power_cur
+ function tp.cfgvalue(...)
+ return txpower_current(Value.cfgvalue(...), tx_power_list)
+ end
- mode = s:taboption("advanced", ListValue, "hwmode", translate("Mode"))
- mode:value("", translate("auto"))
- if hw_modes.b then mode:value("11b", "802.11b") end
- if hw_modes.g then mode:value("11g", "802.11g") end
- if hw_modes.a then mode:value("11a", "802.11a") end
-
- if htcaps then
- if hw_modes.g and hw_modes.n then mode:value("11ng", "802.11g+n") end
- if hw_modes.a and hw_modes.n then mode:value("11na", "802.11a+n") end
-
- htmode = s:taboption("advanced", ListValue, "htmode", translate("HT mode"))
- htmode:depends("hwmode", "11na")
- htmode:depends("hwmode", "11ng")
- htmode:value("HT20", "20MHz")
- htmode:value("HT40-", translate("40MHz 2nd channel below"))
- htmode:value("HT40+", translate("40MHz 2nd channel above"))
-
- --htcapab = s:taboption("advanced", DynamicList, "ht_capab", translate("HT capabilities"))
- --htcapab:depends("hwmode", "11na")
- --htcapab:depends("hwmode", "11ng")
+ for _, p in ipairs(tx_power_list) do
+ tp:value(p.driver_dbm, "%i dBm (%i mW)"
+ %{ p.display_dbm, p.display_mw })
+ end
end
local cl = iw and iw.countrylist
s:taboption("advanced", Value, "distance", translate("Distance Optimization"),
translate("Distance to farthest network member in meters."))
+ -- external antenna profiles
+ local eal = iw and iw.extant
+ if eal and #eal > 0 then
+ ea = s:taboption("advanced", ListValue, "extant", translate("Antenna Configuration"))
+ for _, eap in ipairs(eal) do
+ ea:value(eap.id, "%s (%s)" %{ eap.name, eap.description })
+ if eap.selected then
+ ea.default = eap.id
+ end
+ end
+ end
+
s:taboption("advanced", Value, "frag", translate("Fragmentation Threshold"))
s:taboption("advanced", Value, "rts", translate("RTS/CTS Threshold"))
end
if hwtype == "atheros" then
tp = s:taboption("general",
- (#tx_powers > 0) and ListValue or Value,
+ (#tx_power_list > 0) and ListValue or Value,
"txpower", translate("Transmit Power"), "dBm")
tp.rmempty = true
- tp.default = tx_power
- for _, p in ipairs(tx_powers or {}) do
- tp:value(p.dbm, "%i dBm (%i mW)" %{ p.dbm, p.mw })
+ tp.default = tx_power_cur
+
+ function tp.cfgvalue(...)
+ return txpower_current(Value.cfgvalue(...), tx_power_list)
end
- mode = s:taboption("advanced", ListValue, "hwmode", translate("Mode"))
- mode:value("", translate("auto"))
- if hw_modes.b then mode:value("11b", "802.11b") end
- if hw_modes.g then mode:value("11g", "802.11g") end
- if hw_modes.a then mode:value("11a", "802.11a") end
- if hw_modes.g then mode:value("11bg", "802.11b+g") end
- if hw_modes.g then mode:value("11gst", "802.11g + Turbo") end
- if hw_modes.a then mode:value("11ast", "802.11a + Turbo") end
- mode:value("fh", translate("Frequency Hopping"))
+ for _, p in ipairs(tx_power_list) do
+ tp:value(p.driver_dbm, "%i dBm (%i mW)"
+ %{ p.display_dbm, p.display_mw })
+ end
s:taboption("advanced", Flag, "diversity", translate("Diversity")).rmempty = false
if hwtype == "broadcom" then
tp = s:taboption("general",
- (#tx_powers > 0) and ListValue or Value,
+ (#tx_power_list > 0) and ListValue or Value,
"txpower", translate("Transmit Power"), "dBm")
tp.rmempty = true
- tp.default = tx_power
- for _, p in ipairs(tx_powers or {}) do
- tp:value(p.dbm, "%i dBm (%i mW)" %{ p.dbm, p.mw })
+ tp.default = tx_power_cur
+
+ function tp.cfgvalue(...)
+ return txpower_current(Value.cfgvalue(...), tx_power_list)
end
- mode = s:taboption("advanced", ListValue, "hwmode", translate("Mode"))
- mode:value("11bg", "802.11b+g")
- mode:value("11b", "802.11b")
- mode:value("11g", "802.11g")
- mode:value("11gst", "802.11g + Turbo")
+ for _, p in ipairs(tx_power_list) do
+ tp:value(p.driver_dbm, "%i dBm (%i mW)"
+ %{ p.display_dbm, p.display_mw })
+ end
ant1 = s:taboption("advanced", ListValue, "txantenna", translate("Transmitter Antenna"))
ant1.widget = "radio"
bssid = s:taboption("general", Value, "bssid", translate("<abbr title=\"Basic Service Set Identifier\">BSSID</abbr>"))
network = s:taboption("general", Value, "network", translate("Network"),
- translate("Choose the network you want to attach to this wireless interface. " ..
- "Select <em>unspecified</em> to not attach any network or fill out the " ..
- "<em>create</em> field to define a new network."))
+ translate("Choose the network(s) you want to attach to this wireless interface or " ..
+ "fill out the <em>create</em> field to define a new network."))
network.rmempty = true
network.template = "cbi/network_netlist"
-network.widget = "radio"
+network.widget = "checkbox"
+network.novirtual = true
function network.write(self, section, value)
local i = nw:get_interface(section)
if n then n:del_interface(i) end
end
else
- local n = nw:get_network(value)
- if n then
- n:set("type", "bridge")
- n:add_interface(i)
+ local v
+ for _, v in ipairs(i:get_networks()) do
+ v:del_interface(i)
+ end
+ for v in ut.imatch(value) do
+ local n = nw:get_network(v)
+ if n then
+ if not n:is_empty() then
+ n:set("type", "bridge")
+ end
+ n:add_interface(i)
+ end
end
end
end
ml.datatype = "macaddr"
ml:depends({macfilter="allow"})
ml:depends({macfilter="deny"})
+ nt.mac_hints(function(mac, name) ml:value(mac, "%s (%s)" %{ mac, name }) end)
mode:value("ap-wds", "%s (%s)" % {translate("Access Point"), translate("WDS")})
mode:value("sta-wds", "%s (%s)" % {translate("Client"), translate("WDS")})
hidden = s:taboption("general", Flag, "hidden", translate("Hide <abbr title=\"Extended Service Set Identifier\">ESSID</abbr>"))
hidden:depends({mode="ap"})
hidden:depends({mode="ap-wds"})
+
+ wmm = s:taboption("general", Flag, "wmm", translate("WMM Mode"))
+ wmm:depends({mode="ap"})
+ wmm:depends({mode="ap-wds"})
+ wmm.default = wmm.enabled
end
ml.datatype = "macaddr"
ml:depends({macpolicy="allow"})
ml:depends({macpolicy="deny"})
+ nt.mac_hints(function(mac, name) ml:value(mac, "%s (%s)" %{ mac, name }) end)
s:taboption("advanced", Value, "rate", translate("Transmission Rate"))
s:taboption("advanced", Value, "mcast_rate", translate("Multicast Rate"))
ml = s:taboption("macfilter", DynamicList, "maclist", translate("MAC-List"))
ml:depends({macpolicy="allow"})
ml:depends({macpolicy="deny"})
+ nt.mac_hints(function(mac, name) ml:value(mac, "%s (%s)" %{ mac, name }) end)
s:taboption("advanced", Value, "rate", translate("Transmission Rate"))
s:taboption("advanced", Value, "frag", translate("Fragmentation Threshold"))
encr:value("none", "No Encryption")
-encr:value("wep-open", translate("WEP Open System"), {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
-encr:value("wep-shared", translate("WEP Shared Key"), {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+encr:value("wep-open", translate("WEP Open System"), {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"}, {mode="ahdemo"}, {mode="wds"})
+encr:value("wep-shared", translate("WEP Shared Key"), {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"}, {mode="adhoc"}, {mode="ahdemo"}, {mode="wds"})
if hwtype == "atheros" or hwtype == "mac80211" or hwtype == "prism2" then
local supplicant = fs.access("/usr/sbin/wpa_supplicant")
local hostapd = fs.access("/usr/sbin/hostapd")
+ -- Probe EAP support
+ local has_ap_eap = (os.execute("hostapd -veap >/dev/null 2>/dev/null") == 0)
+ local has_sta_eap = (os.execute("wpa_supplicant -veap >/dev/null 2>/dev/null") == 0)
+
if hostapd and supplicant then
- encr:value("psk", "WPA-PSK")
- encr:value("psk2", "WPA2-PSK")
- encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode")
- encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
- encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ encr:value("psk", "WPA-PSK", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ encr:value("psk2", "WPA2-PSK", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ if has_ap_eap and has_sta_eap then
+ encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="sta"}, {mode="ap-wds"}, {mode="sta-wds"})
+ end
elseif hostapd and not supplicant then
- encr:value("psk", "WPA-PSK", {mode="ap"}, {mode="ap-wds"}, {mode="adhoc"}, {mode="ahdemo"})
- encr:value("psk2", "WPA2-PSK", {mode="ap"}, {mode="ap-wds"}, {mode="adhoc"}, {mode="ahdemo"})
- encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="ap"}, {mode="ap-wds"}, {mode="adhoc"}, {mode="ahdemo"})
- encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="ap-wds"})
- encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="ap-wds"})
+ encr:value("psk", "WPA-PSK", {mode="ap"}, {mode="ap-wds"})
+ encr:value("psk2", "WPA2-PSK", {mode="ap"}, {mode="ap-wds"})
+ encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="ap"}, {mode="ap-wds"})
+ if has_ap_eap then
+ encr:value("wpa", "WPA-EAP", {mode="ap"}, {mode="ap-wds"})
+ encr:value("wpa2", "WPA2-EAP", {mode="ap"}, {mode="ap-wds"})
+ end
encr.description = translate(
"WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP " ..
"and ad-hoc mode) to be installed."
encr:value("psk", "WPA-PSK", {mode="sta"}, {mode="sta-wds"})
encr:value("psk2", "WPA2-PSK", {mode="sta"}, {mode="sta-wds"})
encr:value("psk-mixed", "WPA-PSK/WPA2-PSK Mixed Mode", {mode="sta"}, {mode="sta-wds"})
- encr:value("wpa", "WPA-EAP", {mode="sta"}, {mode="sta-wds"})
- encr:value("wpa2", "WPA2-EAP", {mode="sta"}, {mode="sta-wds"})
+ if has_sta_eap then
+ encr:value("wpa", "WPA-EAP", {mode="sta"}, {mode="sta-wds"})
+ encr:value("wpa2", "WPA2-EAP", {mode="sta"}, {mode="sta-wds"})
+ end
encr.description = translate(
"WPA-Encryption requires wpa_supplicant (for client mode) or hostapd (for AP " ..
"and ad-hoc mode) to be installed."
cacert:depends({mode="sta-wds", encryption="wpa"})
cacert:depends({mode="sta-wds", encryption="wpa2"})
+ clientcert = s:taboption("encryption", FileUpload, "client_cert", translate("Path to Client-Certificate"))
+ clientcert:depends({mode="sta", encryption="wpa"})
+ clientcert:depends({mode="sta", encryption="wpa2"})
+ clientcert:depends({mode="sta-wds", encryption="wpa"})
+ clientcert:depends({mode="sta-wds", encryption="wpa2"})
+
privkey = s:taboption("encryption", FileUpload, "priv_key", translate("Path to Private Key"))
privkey:depends({mode="sta", eap_type="tls", encryption="wpa2"})
privkey:depends({mode="sta", eap_type="tls", encryption="wpa"})
password:depends({mode="sta-wds", eap_type="ttls", encryption="wpa"})
end
+if hwtype == "atheros" or hwtype == "mac80211" or hwtype == "prism2" then
+ local wpasupplicant = fs.access("/usr/sbin/wpa_supplicant")
+ local hostcli = fs.access("/usr/sbin/hostapd_cli")
+ if hostcli and wpasupplicant then
+ wps = s:taboption("encryption", Flag, "wps_pushbutton", translate("Enable WPS pushbutton, requires WPA(2)-PSK"))
+ wps.enabled = "1"
+ wps.disabled = "0"
+ wps.rmempty = false
+ wps:depends("encryption", "psk")
+ wps:depends("encryption", "psk2")
+ wps:depends("encryption", "psk-mixed")
+ end
+end
+
return m