Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions board/common/qemu/Config.in.in
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,19 @@ config QEMU_NET_PORTS
int "Number of Rocker switch ports"
depends on QEMU_NET_ROCKER
default 10

comment "Wireless"

config QEMU_WIFI_RADIOS
int "Number of virtual WiFi radios (mac80211_hwsim)"
default 0
help
Number of simulated 802.11 radios to create with mac80211_hwsim.
The count is passed to the guest via the opt/wifi fw_cfg and loaded
by the 00-hwsim boot script.

0 (default) means no radios, so nothing wireless clutters the guest.
Set a small number (e.g. 2) for local AP/station experiments.

Note: 'make run' is a single guest with no inter-guest wireless
medium, so these radios only reach each other within this one guest.
11 changes: 11 additions & 0 deletions board/common/qemu/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ EOF
echo -n "-fw_cfg name=opt/vpd,file=$vpd_file"
}

wifi_args()
{
# Number of mac80211_hwsim radios for the guest, read at boot by 00-hwsim
# (fw_cfg opt/wifi). Default 0 -> no radios, no wifi clutter.
radios=${CONFIG_QEMU_WIFI_RADIOS:-0}
wifi="${qdir}/wifi"
echo "$radios" > "$wifi"
echo -n "-fw_cfg name=opt/wifi,file=$wifi "
}

wdt_args()
{
echo -n "-device i6300esb "
Expand Down Expand Up @@ -387,6 +397,7 @@ run_qemu()
$(usb_args) \
$(host_args) \
$(net_args) \
$(wifi_args) \
$(wdt_args) \
$(rtc_args) \
$(vpd_args) \
Expand Down
11 changes: 9 additions & 2 deletions board/common/rootfs/usr/libexec/infix/init.d/00-probe
Original file line number Diff line number Diff line change
Expand Up @@ -654,10 +654,17 @@ def main():
err = 1

os.umask(0o337)
# Write atomically: confd reads system.json as soon as ixinit signals
# done, so a truncated in-place write could be read half-written. Write
# to a temp file in the same dir, then rename (atomic on the same fs).
# pylint: disable=invalid-name
with open(SYSTEM_JSON, "w", encoding="ascii") as f:
tmp = SYSTEM_JSON + ".tmp"
with open(tmp, "w", encoding="ascii") as f:
json.dump(out, f)
shutil.chown(SYSTEM_JSON, user="root", group="wheel")
f.flush()
os.fsync(f.fileno())
shutil.chown(tmp, user="root", group="wheel")
os.replace(tmp, SYSTEM_JSON)
return err


Expand Down
7 changes: 7 additions & 0 deletions board/common/rootfs/usr/libexec/infix/init.d/20-nameif
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,12 @@ for iface in $ports; do
ip link set "$iface" group port
done

# The mac80211_hwsim monitor tap (hwsim test images only) is a debug
# observer, not a user-facing interface. It cannot be deleted (no rtnl
# link ops), so classify it as internal like the DSA CPU interfaces.
if [ -d /sys/class/net/hwsim0 ]; then
ip link set dev hwsim0 group internal
fi

# At least loopback in iface group
ip link set lo group iface
34 changes: 21 additions & 13 deletions board/common/rootfs/usr/libexec/infix/iw.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,25 @@ def parse_phy_info(phy_name):
if current_band and current_band.get('frequencies'):
result['bands'].append(current_band)

# Determine band names and assign band numbers
# Keep only the bands Infix supports (2.4/5/6 GHz), naming them by their
# first frequency. Hardware may expose others (e.g. S1G on hwsim) that we
# neither configure nor report.
bands = []
for band in result['bands']:
if band['frequencies']:
freq = band['frequencies'][0]
if 2400 <= freq <= 2500:
band['name'] = '2.4GHz'
band['band'] = 1
elif 5150 <= freq <= 5900:
band['name'] = '5GHz'
band['band'] = 2
elif 5955 <= freq <= 7115:
band['name'] = '6GHz'
band['band'] = 3
freq = band['frequencies'][0]
if 2400 <= freq <= 2500:
band['name'] = '2.4GHz'
band['band'] = 1
elif 5150 <= freq <= 5900:
band['name'] = '5GHz'
band['band'] = 2
elif 5955 <= freq <= 7115:
band['name'] = '6GHz'
band['band'] = 3
else:
continue
bands.append(band)
result['bands'] = bands

# Set max TX power
if max_power is not None:
Expand All @@ -207,7 +213,9 @@ def parse_phy_info(phy_name):

# Map driver to manufacturer
driver_lower = driver_name.lower()
if 'mt' in driver_lower or 'mediatek' in driver_lower:
if 'hwsim' in driver_lower:
result['manufacturer'] = 'Virtual (hwsim)'
elif 'mt' in driver_lower or 'mediatek' in driver_lower:
result['manufacturer'] = 'MediaTek Inc.'
elif 'rtw' in driver_lower or 'realtek' in driver_lower:
result['manufacturer'] = 'Realtek Semiconductor Corp.'
Expand Down
1 change: 1 addition & 0 deletions configs/x86_64_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ INFIX_DOC="https://www.kernelkit.org/infix/"
INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
BR2_PACKAGE_FEATURE_GPS=y
BR2_PACKAGE_FEATURE_WIFI=y
BR2_PACKAGE_FEATURE_WIFI_HWSIM=y
BR2_PACKAGE_FEATURE_WIFI_MEDIATEK=y
BR2_PACKAGE_FEATURE_WIFI_QUALCOMM=y
BR2_PACKAGE_FEATURE_WIFI_REALTEK=y
Expand Down
30 changes: 30 additions & 0 deletions doc/test-arch.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,36 @@ image. The device will boot into test mode on first power-on.
> unless `startup_override()` is called first (or the marker is removed
> and a normal startup config is saved).

Simulated Wi-Fi
---------------

The virtual topology has no real radios, so Wi-Fi tests run against
`mac80211_hwsim` devices and the "air" between guests is faked. A small
relay, `wifimedium` (`package/feature-wifi/wifimedium`), takes every frame
a radio transmits, forwards it to the other guests, and injects what
arrives back into the local radios.

Each radio (`radio0`, `radio1`, ...) is paired with a carrier NIC of the
same name, and that carrier joins the multicast group (a *cell*) the
topology wires it to. Two radios hear each other only when their carriers
share a cell. Radios on the same cell that tune to different channels are
still kept apart, as on real hardware.

By convention the two general cells carry `radio0` and `radio1` across all
DUTs, so most tests put communicating radios on the same index: a station
joins an AP on the same `radioN`. The mesh-roaming test, for instance,
uses `radio1` for both the gateway APs and the client. That is only a
convention, though. A cell can carry any set of radios, including two of
the *same* DUT. The band-steering test relies on that: it wires one DUT's
`radio2` (2.4GHz) and `radio3` (5GHz) onto a dedicated cell so a single
client radio hears the same SSID on both bands.

How many radios each DUT gets depends on the topology. The number of
`radioN` ports wired to a node is passed in via the `opt/wifi` fw_cfg and
loaded at boot by the `00-hwsim` script, so a node with no wifi links has
no radios at all. The wiring for the standard suite lives in
`test/virt/quad`.

[9PM]: https://github.com/rical/9pm
[Qeneth]: https://github.com/wkz/qeneth
[TAP]: https://testanything.org/
7 changes: 7 additions & 0 deletions doc/wifi.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ in-octets : 148388
out-octets : 24555
mode : station
ssid : MyNetwork
bssid : b4:fb:e4:17:b6:a7
signal : -45 dBm (good)
rx bitrate : 72.2 Mbps
tx bitrate : 86.6 Mbps
Expand All @@ -319,6 +320,11 @@ In the CLI, signal strength is reported as: excellent, good, fair or bad.
For precise signal strength values in dBm, use NETCONF or RESTCONF to access
the `signal-strength` leaf in the operational datastore.

When connected, `bssid` shows the MAC address of the access point the
station is associated to. It appears only while connected. When several
access points share one SSID (a roaming network), the `bssid` is what
tells them apart, and it changes as the station roams between them.

## Passphrase Requirements

To ensure your connection is secure and compatible with all network
Expand Down Expand Up @@ -378,6 +384,7 @@ operational status : up
physical address : f0:09:0d:36:5f:86
mode : station
ssid : MyHomeNetwork
bssid : b4:fb:e4:17:b6:a7
signal : -52 dBm (good)
</code></pre>

Expand Down
47 changes: 47 additions & 0 deletions package/feature-wifi/00-hwsim
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh
# Load mac80211_hwsim virtual radios sized to this DUT's topology, then wait
# for udev to rename the phys (phyN -> radioN) before returning.
#
# The radio count comes from a dedicated fw_cfg file (opt/wifi) that qeneth
# writes per node from the number of radioN carrier ports the topology wired
# to it. A node with no wifi links gets 0 (or no file), so hwsim is not loaded
# at all -- no phys, nothing clutters its interface view.
#
# Runs early in ixinit, before 00-probe, so the system probe records the radios
# by their final radioN names. ixinit is past the <pid/syslogd> / udev-settle
# barrier, so 60-rename-wifi-phy.rules (phyN -> radioN) and
# 70-remove-virtual-wifi-interfaces.rules (drop the stray wlanN) are loaded and
# fire on the phy add events. On success it sets the <usr/hwsim> condition so
# wifimedium starts only where radios exist. Installed only with
# BR2_PACKAGE_FEATURE_WIFI_HWSIM.

wifi=/sys/firmware/qemu_fw_cfg/by_name/opt/wifi/raw
ieee=/sys/class/ieee80211

# Number of radios to create, from the topology (via fw_cfg).
radios=0
[ -r "$wifi" ] && radios=$(cat "$wifi" 2>/dev/null)

# No radios wanted -> don't load hwsim, keep the view wifi-free.
[ "$radios" -gt 0 ] 2>/dev/null || exit 0

modprobe mac80211_hwsim radios="$radios" || exit 0

# Wait (bounded ~10s) for the phyN -> radioN rename to finish: the class has
# entries and none are still named phyN. The rename is a udev RUN, so it lags
# modprobe's return. On timeout the radios never settled -- fail (and log) so
# it is visible why 00-probe may record stale phyN names.
TIMEOUT=100
i=0
while [ "$i" -lt "$TIMEOUT" ]; do
names=$(ls "$ieee" 2>/dev/null)
if [ -n "$names" ] && ! echo "$names" | grep -q '^phy'; then
initctl -bq cond set hwsim
exit 0
fi
i=$((i + 1))
sleep 0.1
done

logger -p user.err -t 00-hwsim "timed out waiting for hwsim phys to be renamed radioN"
exit 1
12 changes: 12 additions & 0 deletions package/feature-wifi/Config.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ config BR2_PACKAGE_FEATURE_WIFI
help
Enables WiFi in Infix. Enables all requried applications.

config BR2_PACKAGE_FEATURE_WIFI_HWSIM
bool "Virtual Wi-Fi radios (mac80211_hwsim)"
depends on BR2_PACKAGE_FEATURE_WIFI
help
Build the mac80211_hwsim virtual radio driver (as a module) for the
QEMU test target, to exercise AP, station, and mesh modes without
physical WiFi adapters. The module is autoloaded at boot (see the
x86_64 /etc/modules-load.d) and frames are bridged between guests by
the wifimedium relay.

Leave disabled on real hardware; it only provides simulated radios.

config BR2_PACKAGE_FEATURE_WIFI_MEDIATEK
bool "Mediatek WiFi Devices"
depends on BR2_PACKAGE_FEATURE_WIFI
Expand Down
24 changes: 24 additions & 0 deletions package/feature-wifi/feature-wifi.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ define FEATURE_WIFI_LINUX_CONFIG_FIXUPS
$(call KCONFIG_SET_OPT,CONFIG_CFG80211,m)
$(call KCONFIG_ENABLE_OPT,CONFIG_MAC80211_MESH)

# Virtual radio for automated testing (mac80211_hwsim), built as a
# module and gated behind BR2_PACKAGE_FEATURE_WIFI_HWSIM so it is not
# built on real boards. When selected, the module is loaded early in
# ixinit by 00-hwsim -- sized to the DUT's topology, so radio-less nodes
# load nothing -- and bridged between QEMU guests by the wifimedium relay
# (both installed by FEATURE_WIFI_INSTALL_HWSIM below).
$(if $(BR2_PACKAGE_FEATURE_WIFI_HWSIM),
$(call KCONFIG_SET_OPT,CONFIG_MAC80211_HWSIM,m))

$(if $(filter y,$(BR2_PACKAGE_FEATURE_WIFI_MEDIATEK)),
$(call KCONFIG_ENABLE_OPT,CONFIG_MT7601U)
$(call KCONFIG_ENABLE_OPT,CONFIG_MT76x0U)
Expand Down Expand Up @@ -76,5 +85,20 @@ define FEATURE_WIFI_INSTALL_IN_ROMFS
endef
FEATURE_WIFI_POST_INSTALL_TARGET_HOOKS += FEATURE_WIFI_INSTALL_IN_ROMFS

# Virtual-radio (mac80211_hwsim) runtime, installed only when the option is
# selected: the 00-hwsim ixinit script loads the module sized to the opt/wifi
# fw_cfg qeneth writes per node (no radios -> not loaded), and the wifimedium
# relay + its Finit service bridge hwsim frames between QEMU guests so they can
# associate "wirelessly".
ifeq ($(BR2_PACKAGE_FEATURE_WIFI_HWSIM),y)
define FEATURE_WIFI_INSTALL_HWSIM
$(INSTALL) -D -m 0755 $(FEATURE_WIFI_PKGDIR)/00-hwsim $(TARGET_DIR)/usr/libexec/infix/init.d/00-hwsim
$(INSTALL) -D -m 0755 $(FEATURE_WIFI_PKGDIR)/wifimedium $(TARGET_DIR)/usr/libexec/infix/wifimedium
$(INSTALL) -D -m 0644 $(FEATURE_WIFI_PKGDIR)/wifimedium.conf $(TARGET_DIR)/etc/finit.d/available/wifimedium.conf
ln -sf ../available/wifimedium.conf $(TARGET_DIR)/etc/finit.d/enabled/wifimedium.conf
endef
FEATURE_WIFI_POST_INSTALL_TARGET_HOOKS += FEATURE_WIFI_INSTALL_HWSIM
endif


$(eval $(generic-package))
Loading
Loading