* Performance optimizations

* libs/core: Added bytecode stripping function to luci.util
* libs/core: Added smart indexcache that automatically updates cached index-files on change
* libs/web: Enabled template caching support
* Core Translation part 4
This commit is contained in:
Steven Barth 2008-06-01 12:12:18 +00:00
parent 1da5feb9f7
commit b454395a8d
14 changed files with 204 additions and 83 deletions

View file

@ -102,3 +102,31 @@ a_srv_dropbear1 = "Dropbear offers SSH network shell access and an integrated SC
a_srv_d_pwauth = "Password authentication" a_srv_d_pwauth = "Password authentication"
a_srv_d_pwauth1 = "Allow SSH password authentication" a_srv_d_pwauth1 = "Allow SSH password authentication"
a_w_wifi1 = [[On this pages you find confiugration options for WLAN based wireless networks.]]
a_w_wifi2 = [[You can easily integrate your 802.11a/b/g/n-devices into your physical network and use
the virtual adapter support to build wireless repeaters or offer several networks with one device.]]
a_w_wifi3 = [[There is support for Managed, Client, Ad-Hoc and WDS operating modes as well as
WPA and WPA2 encryption for secure communnication.]]
a_w_devices1 = "Here you can configure installed wifi devices."
a_w_channel = "Channel"
a_w_txantenna = "Transmit Antenna"
a_w_rxantenna = "Receive Antenna"
a_w_distance1 = "Distance to furthest station (m)"
a_w_diversity = "Diversity"
a_w_countrycode = "Country Code"
a_w_connlimit = "Connection Limit"
a_w_networks1 = [[You can run several wifi networks with one device. Be aware that there are certain
hardware and driverspecific restrictions. Normally you can operate 1 Ad-Hoc or up to 3 Master-Mode and 1 Client-Mode
network simultaneously.]]
a_w_netid = "Network Name (ESSID)"
a_w_network1 = "Add the Wifi network to physical network"
a_w_txpwr = "Transmit Power"
a_w_brcmburst = "Broadcom Frameburst"
a_w_athburst = "Atheros Frameburst"
a_w_radiussrv = "Radius-Server"
a_w_radiusport = "Radius-Port"
a_w_apisolation = "AP-Isolation"
a_w_apisolation1 = "Prevents Client to Client communication"
a_w_hideessid = "Hide ESSID"

View file

@ -13,9 +13,12 @@ delete = "Delete"
descr = "Description" descr = "Description"
design = "Design" design = "Design"
device = "Device" device = "Device"
devices = "Devices"
disable = "disable" disable = "disable"
distance = "Distance"
enable = "enable" enable = "enable"
encryption = "Encryption"
error = "Error" error = "Error"
filesystem = "Filesystem" filesystem = "Filesystem"
@ -27,11 +30,17 @@ hostname = "Hostname"
install = "Install" install = "Install"
installed = "installed" installed = "installed"
key = "Key"
language = "Language" language = "Language"
load = "Load" load = "Load"
manpage = "see '%s' manpage" manpage = "see '%s' manpage"
mode = "Mode"
network = "Network"
networks = "Networks"
notinstalled = "not installed" notinstalled = "not installed"
ok = "OK" ok = "OK"
@ -53,8 +62,11 @@ statistics = "Statistics"
syslog = "System Log" syslog = "System Log"
system = "System" system = "System"
type = "Type"
unknownerror = "Unknown Error" unknownerror = "Unknown Error"
version = "Version" version = "Version"
webui = "Web UI" webui = "Web UI"
wifi = "Wifi"

View file

@ -27,6 +27,7 @@ limitations under the License.
module("luci.fs", package.seeall) module("luci.fs", package.seeall)
require("posix") require("posix")
posix.umask("rwx------")
-- Glob -- Glob
glob = posix.glob glob = posix.glob

View file

@ -96,6 +96,13 @@ function contains(table, value)
end end
-- Dumps and strips a Lua-Function
function dump(f)
local d = string.dump(f)
return d and strip_bytecode(d)
end
-- Dumps a table to stdout (useful for testing and debugging) -- Dumps a table to stdout (useful for testing and debugging)
function dumptable(t, i) function dumptable(t, i)
i = i or 0 i = i or 0
@ -181,12 +188,77 @@ function split(str, pat, max, regex)
return t return t
end end
-- Bytecode stripping function by Peter Cawley from http://lua-users.org/lists/lua-l/2008-02/msg01158.html
function strip_bytecode(dump)
local version, format, endian, int, size, ins, num = dump:byte(5, 11)
local subint
if endian == 1 then
subint = function(dump, i, l)
local val = 0
for n = l, 1, -1 do
val = val * 256 + dump:byte(i + n - 1)
end
return val, i + l
end
else
subint = function(dump, i, l)
local val = 0
for n = 1, l, 1 do
val = val * 256 + dump:byte(i + n - 1)
end
return val, i + l
end
end
local strip_function
strip_function = function(dump)
local count, offset = subint(dump, 1, size)
local stripped, dirty = string.rep("\0", size), offset + count
offset = offset + count + int * 2 + 4
offset = offset + int + subint(dump, offset, int) * ins
count, offset = subint(dump, offset, int)
for n = 1, count do
local t
t, offset = subint(dump, offset, 1)
if t == 1 then
offset = offset + 1
elseif t == 4 then
offset = offset + size + subint(dump, offset, size)
elseif t == 3 then
offset = offset + num
end
end
count, offset = subint(dump, offset, int)
stripped = stripped .. dump:sub(dirty, offset - 1)
for n = 1, count do
local proto, off = strip_function(dump:sub(offset, -1))
stripped, offset = stripped .. proto, offset + off - 1
end
offset = offset + subint(dump, offset, int) * int + int
count, offset = subint(dump, offset, int)
for n = 1, count do
offset = offset + subint(dump, offset, size) + size + int * 2
end
count, offset = subint(dump, offset, int)
for n = 1, count do
offset = offset + subint(dump, offset, size) + size
end
stripped = stripped .. string.rep("\0", int * 3)
return stripped, offset
end
return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
end
-- Removes whitespace from beginning and end of a string -- Removes whitespace from beginning and end of a string
function trim(str) function trim(str)
local s = str:gsub("^%s*(.-)%s*$", "%1") local s = str:gsub("^%s*(.-)%s*$", "%1")
return s return s
end end
-- Updates given table with new values -- Updates given table with new values
function update(t, updates) function update(t, updates)
for k, v in pairs(updates) do for k, v in pairs(updates) do

View file

@ -25,10 +25,9 @@ limitations under the License.
]]-- ]]--
module("luci.config", package.seeall) local uci = require("luci.model.uci")
require("luci.model.uci") local util = require("luci.util")
require("luci.util") module("luci.config")
require("luci.sys")
-- Warning! This is only for fallback and compatibility purporses! -- -- Warning! This is only for fallback and compatibility purporses! --
main = {} main = {}
@ -42,7 +41,7 @@ main.lang = "de"
-- Now overwrite with UCI values -- Now overwrite with UCI values
local ucidata = luci.model.uci.sections("luci") local ucidata = uci.sections("luci")
if ucidata then if ucidata then
luci.util.update(luci.config, ucidata) util.update(_M, ucidata)
end end

View file

@ -177,7 +177,7 @@ end
-- Calls the index function of all available controllers -- Calls the index function of all available controllers
function createindex_plain(path, suffix) function createindex_plain(path, suffix)
local cachetime = nil local cache = nil
local controllers = luci.util.combine( local controllers = luci.util.combine(
luci.fs.glob(path .. "*" .. suffix) or {}, luci.fs.glob(path .. "*" .. suffix) or {},
@ -185,32 +185,38 @@ function createindex_plain(path, suffix)
) )
if indexcache then if indexcache then
cachetime = luci.fs.mtime(indexcache) cache = luci.fs.mtime(indexcache)
if not cachetime then if not cache then
luci.fs.mkdir(indexcache) luci.fs.mkdir(indexcache)
luci.fs.chmod(indexcache, "a=,u=rwx") luci.fs.chmod(indexcache, "a=,u=rwx")
cache = luci.fs.mtime(indexcache)
end end
end end
if not cachetime then for i,c in ipairs(controllers) do
for i,c in ipairs(controllers) do local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".") local cachefile = indexcache .. "/" .. module
stat, mod = pcall(require, c) local stime
local ctime
if cache then
stime = luci.fs.mtime(c) or 0
ctime = luci.fs.mtime(cachefile) or 0
end
if not cache or stime > ctime then
stat, mod = pcall(require, module)
if stat and mod and type(mod.index) == "function" then if stat and mod and type(mod.index) == "function" then
index[c] = mod.index index[module] = mod.index
if indexcache then if cache then
luci.fs.writefile(indexcache .. "/" .. c, string.dump(mod.index)) luci.fs.writefile(cachefile, luci.util.dump(mod.index))
end end
end end
end else
else index[module] = loadfile(cachefile)
for i,c in ipairs(luci.fs.dir(indexcache)) do
if c:sub(1) ~= "." then
index[c] = loadfile(indexcache .. "/" .. c)
end
end end
end end
end end
@ -226,7 +232,7 @@ function createtree()
-- Load default translation -- Load default translation
luci.i18n.loadc("default") luci.i18n.loadc("default")
local scope = _G local scope = luci.util.clone(_G)
for k,v in pairs(_M) do for k,v in pairs(_M) do
if type(v) == "function" then if type(v) == "function" then
scope[k] = v scope[k] = v

View file

@ -39,7 +39,7 @@ end
-- Loads a translation and copies its data into the global translation table -- Loads a translation and copies its data into the global translation table
function load(file, force) function load(file, force)
if force or not loaded[file] then if force or not loaded[file] then
local f = loadfile(i18ndir .. file) local f = loadfile(i18ndir..file..".lua") or loadfile(i18ndir..file)
if f then if f then
setfenv(f, table) setfenv(f, table)
f() f()
@ -54,8 +54,8 @@ function load(file, force)
end end
-- Same as load but autocompletes the filename with .LANG from config.lang -- Same as load but autocompletes the filename with .LANG from config.lang
function loadc(file) function loadc(file, force)
return load(file .. "." .. require("luci.config").main.lang) return load(file .. "." .. require("luci.config").main.lang, force)
end end
-- Returns the i18n-value defined by "key" or if there is no such: "default" -- Returns the i18n-value defined by "key" or if there is no such: "default"

View file

@ -30,22 +30,17 @@ require("luci.util")
require("luci.fs") require("luci.fs")
require("luci.http") require("luci.http")
viewdir = luci.sys.libpath() .. "/view/" luci.config.template = luci.config.template or {}
viewdir = luci.config.template.viewdir or luci.sys.libpath() .. "/view"
compiledir = luci.config.template.compiledir or luci.sys.libpath() .. "/view"
-- Compile modes: -- Compile modes:
-- none: Never compile, only use precompiled data from files -- none: Never compile, only use precompiled data from files
-- memory: Always compile, do not save compiled files, ignore precompiled -- memory: Always compile, do not save compiled files, ignore precompiled
-- file: Compile on demand, save compiled files, update precompiled -- file: Compile on demand, save compiled files, update precompiled
compiler_mode = "memory" compiler_mode = luci.config.template.compiler_mode or "memory"
-- This applies to compiler modes "always" and "smart"
--
-- Produce compiled lua code rather than lua sourcecode
-- WARNING: Increases template size heavily!!!
-- This produces the same bytecode as luac but does not have a strip option
compiler_enable_bytecode = false
-- Define the namespace for template modules -- Define the namespace for template modules
@ -107,12 +102,7 @@ function compile(template)
template = template:gsub("<%%"..tostring(k).."%%>", re) template = template:gsub("<%%"..tostring(k).."%%>", re)
end end
if compiler_enable_bytecode then return loadstring(template)
tf = loadstring(template)
template = string.dump(tf)
end
return template
end end
-- Oldstyle render shortcut -- Oldstyle render shortcut
@ -156,8 +146,8 @@ function Template.__init__(self, name)
end end
-- Compile and build -- Compile and build
local sourcefile = viewdir .. name .. ".htm" local sourcefile = viewdir .. "/" .. name .. ".htm"
local compiledfile = viewdir .. name .. ".lua" local compiledfile = compiledir .. "/" .. name .. ".lua"
local err local err
if compiler_mode == "file" then if compiler_mode == "file" then
@ -171,9 +161,15 @@ function Template.__init__(self, name)
source, err = luci.fs.readfile(sourcefile) source, err = luci.fs.readfile(sourcefile)
if source then if source then
local compiled = compile(source) local compiled, err = compile(source)
luci.fs.writefile(compiledfile, compiled)
self.template, err = loadstring(compiled) local compiledfile_dir = luci.fs.dirname(compiledfile)
if not luci.fs.mtime(compiledfile_dir) then
luci.fs.mkdir(compiledfile_dir)
end
luci.fs.writefile(compiledfile, luci.util.dump(compiled))
self.template = compiled
end end
else else
self.template, err = loadfile(compiledfile) self.template, err = loadfile(compiledfile)
@ -186,7 +182,7 @@ function Template.__init__(self, name)
local source local source
source, err = luci.fs.readfile(sourcefile) source, err = luci.fs.readfile(sourcefile)
if source then if source then
self.template, err = loadstring(compile(source)) self.template, err = compile(source)
end end
end end

View file

@ -35,6 +35,10 @@ config internal languages
option de "Deutsch" option de "Deutsch"
option en "English" option en "English"
config internal template
option compiler_mode file
option compiledir "/tmp/.lucitplcache"
config internal themes config internal themes
option OpenWRT "/luci-static/openwrt.org" option OpenWRT "/luci-static/openwrt.org"
option Fledermaus "/luci-static/fledermaus" option Fledermaus "/luci-static/fledermaus"

View file

@ -1,18 +1,21 @@
module("luci.controller.admin.wifi", package.seeall) module("luci.controller.admin.wifi", package.seeall)
function index() function index()
luci.i18n.loadc("admin-core")
local i18n = luci.i18n.translate
local page = node("admin", "wifi") local page = node("admin", "wifi")
page.target = template("admin_wifi/index") page.target = template("admin_wifi/index")
page.title = "Drahtlos" page.title = i18n("wifi", "Drahtlos")
page.order = 60 page.order = 60
local page = node("admin", "wifi", "devices") local page = node("admin", "wifi", "devices")
page.target = cbi("admin_wifi/devices") page.target = cbi("admin_wifi/devices")
page.title = "Geräte" page.title = i18n("devices", "Geräte")
page.order = 10 page.order = 10
local page = node("admin", "wifi", "networks") local page = node("admin", "wifi", "networks")
page.target = cbi("admin_wifi/networks") page.target = cbi("admin_wifi/networks")
page.title = "Netze" page.title = i18n("networks", "Netze")
page.order = 20 page.order = 20
end end

View file

@ -1,15 +1,15 @@
-- ToDo: Translate, Add descriptions and help texts -- ToDo: Translate, Add descriptions and help texts
m = Map("wireless", translate("devices", "Geräte"), translate("a_w_devices1",
m = Map("wireless", "Geräte", [[An dieser Stelle können eingebaute WLAN-Geräte konfiguriert werden.]]) "An dieser Stelle können eingebaute WLAN-Geräte konfiguriert werden."))
s = m:section(TypedSection, "wifi-device") s = m:section(TypedSection, "wifi-device")
--s.addremove = true --s.addremove = true
en = s:option(Flag, "disabled", "Aktivieren") en = s:option(Flag, "disabled", translate("enable", "Aktivieren"))
en.enabled = "0" en.enabled = "0"
en.disabled = "1" en.disabled = "1"
t = s:option(ListValue, "type", "Typ") t = s:option(ListValue, "type", translate("type", "Typ"))
t:value("broadcom") t:value("broadcom")
t:value("atheros") t:value("atheros")
t:value("mac80211") t:value("mac80211")
@ -22,7 +22,7 @@ for driver in luci.sys.execl(c)[1]:gmatch("[^ ]+") do
end end
]]-- ]]--
mode = s:option(ListValue, "mode", "Modus") mode = s:option(ListValue, "mode", translate("mode", "Modus"))
mode:value("", "standard") mode:value("", "standard")
mode:value("11b", "802.11b") mode:value("11b", "802.11b")
mode:value("11g", "802.11g") mode:value("11g", "802.11g")
@ -30,22 +30,22 @@ mode:value("11a", "802.11a")
mode:value("11bg", "802.11b+g") mode:value("11bg", "802.11b+g")
mode.rmempty = true mode.rmempty = true
s:option(Value, "channel", "Funkkanal") s:option(Value, "channel", translate("a_w_channel", "Funkkanal"))
s:option(Value, "txantenna", "Sendeantenne").rmempty = true s:option(Value, "txantenna", translate("a_w_txantenna", "Sendeantenne")).rmempty = true
s:option(Value, "rxantenna", "Empfangsantenne").rmempty = true s:option(Value, "rxantenna", translate("a_w_rxantenna", "Empfangsantenne")).rmempty = true
s:option(Value, "distance", "Distanz", s:option(Value, "distance", translate("distance", "Distanz"),
"Distanz zum am weitesten entfernten Funkpartner (m)").rmempty = true translate("a_w_distance1", "Distanz zum am weitesten entfernten Funkpartner (m)")).rmempty = true
s:option(Value, "diversity", "Diversität"):depends("type", "atheros") s:option(Value, "diversity", translate("a_w_diversity", "Diversität")):depends("type", "atheros")
country = s:option(Value, "country", "Ländercode") country = s:option(Value, "country", translate("a_w_countrycode", "Ländercode"))
country.optional = true country.optional = true
country:depends("type", "broadcom") country:depends("type", "broadcom")
maxassoc = s:option(Value, "maxassoc", "Verbindungslimit") maxassoc = s:option(Value, "maxassoc", translate("a_w_connlimit", "Verbindungslimit"))
maxassoc:depends("type", "broadcom") maxassoc:depends("type", "broadcom")
maxassoc.optional = true maxassoc.optional = true

View file

@ -1,16 +1,16 @@
-- ToDo: Translate, Add descriptions and help texts -- ToDo: Translate, Add descriptions and help texts
m = Map("wireless", "Netze", [[Pro WLAN-Gerät können mehrere Netze bereitgestellt werden. m = Map("wireless", translate("networks", "Netze"), translate("a_w_networks1", [[Pro WLAN-Gerät können mehrere Netze bereitgestellt werden.
Es sollte beachtet werden, dass es hardware- / treiberspezifische Einschränkungen gibt. Es sollte beachtet werden, dass es hardware- / treiberspezifische Einschränkungen gibt.
So kann pro WLAN-Gerät in der Regel entweder 1 Ad-Hoc-Zugang ODER bis zu 3 Access-Point und 1 Client-Zugang So kann pro WLAN-Gerät in der Regel entweder 1 Ad-Hoc-Zugang ODER bis zu 3 Access-Point und 1 Client-Zugang
gleichzeitig erstellt werden.]]) gleichzeitig erstellt werden.]]))
s = m:section(TypedSection, "wifi-iface") s = m:section(TypedSection, "wifi-iface")
s.addremove = true s.addremove = true
s.anonymous = true s.anonymous = true
s:option(Value, "ssid", "Netzkennung (ESSID)").maxlength = 32 s:option(Value, "ssid", translate("a_w_netid", "Netzkennung (ESSID)")).maxlength = 32
device = s:option(ListValue, "device", "Gerät") device = s:option(ListValue, "device", translate("device", "Gerät"))
local d = luci.model.uci.sections("wireless") local d = luci.model.uci.sections("wireless")
if d then if d then
for k, v in pairs(d) do for k, v in pairs(d) do
@ -20,7 +20,7 @@ if d then
end end
end end
network = s:option(ListValue, "network", "Netzwerk", "WLAN-Netz zu Netzwerk hinzufügen") network = s:option(ListValue, "network", translate("network", "Netzwerk"), translate("a_w_network1", "WLAN-Netz zu Netzwerk hinzufügen"))
network:value("") network:value("")
for k, v in pairs(luci.model.uci.sections("network")) do for k, v in pairs(luci.model.uci.sections("network")) do
if v[".type"] == "interface" and k ~= "loopback" then if v[".type"] == "interface" and k ~= "loopback" then
@ -28,7 +28,7 @@ for k, v in pairs(luci.model.uci.sections("network")) do
end end
end end
mode = s:option(ListValue, "mode", "Modus") mode = s:option(ListValue, "mode", translate("mode", "Modus"))
mode:value("ap", "Access Point") mode:value("ap", "Access Point")
mode:value("adhoc", "Ad-Hoc") mode:value("adhoc", "Ad-Hoc")
mode:value("sta", "Client") mode:value("sta", "Client")
@ -36,13 +36,13 @@ mode:value("wds", "WDS")
s:option(Value, "bssid", "BSSID").optional = true s:option(Value, "bssid", "BSSID").optional = true
s:option(Value, "txpower", "Sendeleistung", "dbm").rmempty = true s:option(Value, "txpower", translate("a_w_txpwr", "Sendeleistung"), "dbm").rmempty = true
s:option(Flag, "frameburst", "Broadcom-Frameburst").optional = true s:option(Flag, "frameburst", translate("a_w_brcmburst", "Broadcom-Frameburst")).optional = true
s:option(Flag, "bursting", "Atheros-Frameburst").optional = true s:option(Flag, "bursting", translate("a_w_athburst", "Atheros-Frameburst")).optional = true
encr = s:option(ListValue, "encryption", "Verschlüsselung") encr = s:option(ListValue, "encryption", translate("encryption", "Verschlüsselung"))
encr:value("none", "keine") encr:value("none", "keine")
encr:value("wep", "WEP") encr:value("wep", "WEP")
encr:value("psk", "WPA-PSK") encr:value("psk", "WPA-PSK")
@ -50,7 +50,7 @@ encr:value("wpa", "WPA-Radius")
encr:value("psk2", "WPA2-PSK") encr:value("psk2", "WPA2-PSK")
encr:value("wpa2", "WPA2-Radius") encr:value("wpa2", "WPA2-Radius")
key = s:option(Value, "key", "Schlüssel") key = s:option(Value, "key", translate("key", "Schlüssel"))
key:depends("encryption", "wep") key:depends("encryption", "wep")
key:depends("encryption", "psk") key:depends("encryption", "psk")
key:depends("encryption", "wpa") key:depends("encryption", "wpa")
@ -58,19 +58,19 @@ key:depends("encryption", "psk2")
key:depends("encryption", "wpa2") key:depends("encryption", "wpa2")
key.rmempty = true key.rmempty = true
server = s:option(Value, "server", "Radius-Server") server = s:option(Value, "server", translate("a_w_radiussrv", "Radius-Server"))
server:depends("encryption", "wpa") server:depends("encryption", "wpa")
server:depends("encryption", "wpa2") server:depends("encryption", "wpa2")
server.rmempty = true server.rmempty = true
port = s:option(Value, "port", "Radius-Port") port = s:option(Value, "port", translate("a_w_radiusport", "Radius-Port"))
port:depends("encryption", "wpa") port:depends("encryption", "wpa")
port:depends("encryption", "wpa2") port:depends("encryption", "wpa2")
port.rmempty = true port.rmempty = true
s:option(Flag, "isolate", "AP-Isolation", "Unterbindet Client-Client-Verkehr").optional = true s:option(Flag, "isolate", translate("a_w_apisolation", "AP-Isolation"), translate("a_w_apisolation1", "Unterbindet Client-Client-Verkehr")).optional = true
s:option(Flag, "hidden", "ESSID verstecken").optional = true s:option(Flag, "hidden", translate("a_w_hideessid", "ESSID verstecken")).optional = true

View file

@ -1,9 +1,9 @@
<%+header%> <%+header%>
<h1><%:wifi Drahtlos%></h1> <h1><%:wifi Drahtlos%></h1>
<p><%:wifi1 Hier finden sich Konfiugrationsmöglichkeiten für Drahtlos-Netzwerke nach dem WLAN-Standard.%></p> <p><%:a_w_wifi1 Hier finden sich Konfiugrationsmöglichkeiten für Drahtlos-Netzwerke nach dem WLAN-Standard.%></p>
<p><%:wifi2 802.11b/g/a/n-Geräte können so einfach in das bestehende physische Netzwerk integriert werden. <p><%:a_w_wifi2 802.11b/g/a/n-Geräte können so einfach in das bestehende physische Netzwerk integriert werden.
Die Unterstützung von virtuellen Adaptern ermöglicht auch den Einsatz als Wireless-Repeater oder von Die Unterstützung von virtuellen Adaptern ermöglicht auch den Einsatz als Wireless-Repeater oder von
mehreren Netzwerken gleichzeitig auf einem Gerät.%></p> mehreren Netzwerken gleichzeitig auf einem Gerät.%></p>
<p><%:wifi3 Es werden Managed, Client, Ad-Hoc und WDS-Modus unterstützt sowie WPA und WPA2-Verschlüsselung zur gesicherten <p><%:a_w_wifi3 Es werden Managed, Client, Ad-Hoc und WDS-Modus unterstützt sowie WPA und WPA2-Verschlüsselung zur gesicherten
Kommunikation.%></p> Kommunikation.%></p>
<%+footer%> <%+footer%>