Skip to content

Commit cc88e1e

Browse files
Merge pull request #41 from edgeengineer/edg-322-pi-5-wifi-not-working-with-connman-device-shows-as
Fix Raspberry Pi 5 WiFi Support
2 parents 5fce20a + 949a0eb commit cc88e1e

14 files changed

Lines changed: 546 additions & 0 deletions

docs/WIFI_CONFIGURATION.md

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
# WiFi Configuration in EdgeOS
2+
3+
## Overview
4+
5+
EdgeOS includes WiFi support via ConnMan and wpa_supplicant. WiFi is disabled by default for power management and must be explicitly enabled by the user.
6+
7+
## Architecture
8+
9+
- **ConnMan**: Network manager that provides the WiFi API
10+
- **wpa_supplicant**: Handles WiFi authentication and connection
11+
- **wireless-regdb**: Provides regulatory domain database
12+
- **Udev**: Automatically brings up WiFi interfaces when detected
13+
14+
## Regulatory Domain (Country Code)
15+
16+
### Why It's Required
17+
18+
The Broadcom WiFi driver (brcmfmac) on Raspberry Pi **keeps WiFi soft-blocked** until a valid regulatory domain (country code) is set. This is a regulatory compliance requirement to ensure the device only transmits on frequencies legal in the user's country.
19+
20+
The kernel parameter `ieee80211_regdom` sets the **global default**, but the WiFi PHY device won't actually adopt it until **userspace sends a regulatory hint**. Until then, the PHY stays in the "world domain" (country 99) and remains soft-blocked.
21+
22+
### Default Configuration
23+
24+
EdgeOS sets the regulatory domain via a **udev rule** that runs when the WiFi PHY device appears:
25+
26+
**Udev Rule**: `/etc/udev/rules.d/85-wifi-regdomain.rules`
27+
```
28+
ACTION=="add", SUBSYSTEM=="ieee80211", KERNEL=="phy[0-9]*", \
29+
IMPORT{program}="/bin/sh -c 'c=$(cat /etc/regdomain 2>/dev/null || echo US); printf \"COUNTRY=%%s\\n\" \"$c\"'", \
30+
RUN+="/usr/sbin/iw reg set $env{COUNTRY}"
31+
```
32+
33+
**Note**:
34+
- Uses `printf` instead of `echo` to output KEY=VALUE format that udev expects
35+
- `%%s` escapes the `%` for udev (udev uses `%` for its own substitutions) so it passes literal `%s` to the shell
36+
- You may see a harmless warning about "invalid substitution type" in logs - udev complains about shell syntax it doesn't understand, but still executes correctly
37+
38+
**Country Code File**: `/etc/regdomain`
39+
```
40+
US
41+
```
42+
43+
**How it works:**
44+
- Triggers when WiFi PHY device (phy0) appears during boot
45+
- Reads country code from `/etc/regdomain` (defaults to US if file missing)
46+
- Runs `iw reg set <COUNTRY>` to send regulatory hint to kernel
47+
- ConnMan manages rfkill state (WiFi is disabled by default, enabled by the user)
48+
- Simple and reliable - no systemd service complexity
49+
50+
**Fallback**: `/etc/modprobe.d/cfg80211.conf`
51+
```
52+
options cfg80211 ieee80211_regdom=US
53+
```
54+
55+
This sets the global default, but is not sufficient by itself - the PHY-specific regulatory hint via `iw reg set` is required.
56+
57+
### Provisioning Integration
58+
59+
**The provisioning flow MUST set the correct country code** for the user's location. To do this:
60+
61+
1. **During WiFi setup**, prompt the user for their country code (or derive from timezone/location)
62+
2. **Update the regulatory domain file**:
63+
64+
```bash
65+
# Set the country code (e.g., GB, DE, JP, etc.)
66+
echo GB > /etc/regdomain
67+
```
68+
69+
3. **Apply immediately without reboot**:
70+
71+
```bash
72+
# Set regulatory domain immediately
73+
iw reg set GB
74+
75+
# Enable WiFi (ConnMan will unblock rfkill automatically)
76+
connmanctl enable wifi
77+
```
78+
79+
**Alternative: Trigger udev to re-apply**:
80+
```bash
81+
# Trigger udev to re-read /etc/regdomain and apply
82+
udevadm trigger -s ieee80211
83+
84+
# Verify it was applied
85+
iw reg get
86+
```
87+
88+
**Why this is safe:**
89+
- Writes to a data file (`/etc/regdomain`), not editing udev rule syntax
90+
- Can't break the udev rule with a typo
91+
- Udev rule reads this file on every boot and applies it automatically
92+
93+
**Note**: `iw reg set` does NOT persist across reboots - it only sets the regulatory domain at runtime in kernel memory. The udev rule re-reads `/etc/regdomain` and reapplies it on every boot when the WiFi PHY appears.
94+
95+
### Valid Country Codes
96+
97+
Country codes follow ISO 3166-1 alpha-2 standard:
98+
- `US` - United States
99+
- `GB` - United Kingdom
100+
- `DE` - Germany
101+
- `JP` - Japan
102+
- `CN` - China
103+
- `AU` - Australia
104+
- etc.
105+
106+
See `/lib/firmware/regulatory.db` or wireless-regdb documentation for complete list.
107+
108+
## WiFi Power Management
109+
110+
### Default State and Persistence
111+
112+
WiFi is **disabled by default** on first boot for power savings. This is controlled by `/etc/connman/main.conf` which excludes WiFi from `PreferredTechnologies`.
113+
114+
**After first boot:**
115+
- User enables WiFi → ConnMan saves `Enable=true` to `/var/lib/connman/settings`
116+
- WiFi stays enabled across reboots (persisted state)
117+
- User disables WiFi → ConnMan saves `Enable=false`
118+
- WiFi stays disabled across reboots (persisted state)
119+
120+
ConnMan naturally persists the user's WiFi preference.
121+
122+
### User Control
123+
124+
Users manage WiFi via ConnMan commands:
125+
126+
```bash
127+
# Enable WiFi (required before scanning/connecting)
128+
connmanctl enable wifi
129+
130+
# Disable WiFi (power saving)
131+
connmanctl disable wifi
132+
133+
# Scan for networks
134+
connmanctl scan wifi
135+
136+
# List available networks
137+
connmanctl services
138+
```
139+
140+
## Technical Details
141+
142+
### Why `ip link set wlan0 up` is Required
143+
144+
wpa_supplicant only registers interfaces that are **UP** with D-Bus/ConnMan. A udev rule automatically brings up WiFi interfaces when detected:
145+
146+
**File**: `/etc/udev/rules.d/80-wireless.rules`
147+
```
148+
SUBSYSTEM=="net", ACTION=="add", KERNEL=="wlan*", RUN+="/sbin/ip link set %k up"
149+
```
150+
151+
### How WiFi Initialization Works
152+
153+
#### Boot Sequence
154+
1. **Module Load**: cfg80211 module loads with `ieee80211_regdom=US` parameter (sets global default)
155+
2. **Driver Init**: brcmfmac driver initializes - WiFi PHY appears as phy0 in "world domain" (country 99), registers with rfkill (default: unblocked)
156+
3. **Udev Event**: ieee80211 subsystem fires ADD event for phy0
157+
4. **Udev Rule Triggers**: `85-wifi-regdomain.rules` matches the phy0 device
158+
5. **Regulatory Hint**: Rule runs `/usr/sbin/iw reg set US` which sends nl80211 regulatory hint to kernel
159+
6. **Domain Applied**: Kernel cfg80211 applies US regulatory domain to phy0
160+
7. **wlan0 appears**: Interface appears, rfkill device registered
161+
8. **systemd-rfkill restores state**: Reads `/var/lib/systemd/rfkill/platform-*:wlan` and restores saved rfkill block/unblock state
162+
9. **Migration service** (first boot only): Reads ConnMan settings, sets initial rfkill state
163+
10. **Udev brings up wlan0**: `80-wireless.rules` runs `ip link set wlan0 up` so wpa_supplicant can register it
164+
11. **ConnMan Starts**: Reads hardware rfkill state, synchronizes with persisted settings
165+
12. **WiFi Nudge** (if WiFi enabled): Toggles WiFi to ensure wpa_supplicant registers wlan0 with ConnMan
166+
13. **WiFi Ready**: Enabled or disabled based on persisted state, fully operational
167+
168+
#### When User Enables/Disables WiFi
169+
1. **User Command**: `connmanctl enable wifi` or `connmanctl disable wifi`
170+
2. **ConnMan Action**: Automatically manages rfkill (unblock/block)
171+
3. **systemd-rfkill saves state**: Monitors `/dev/rfkill` and persists block/unblock state to `/var/lib/systemd/rfkill/platform-*:wlan`
172+
4. **ConnMan synchronizes**: Updates `Enable=true/false` in `/var/lib/connman/settings` to match rfkill state
173+
5. **Next Boot**: systemd-rfkill restores rfkill state → ConnMan reads it → WiFi state persisted
174+
175+
### WiFi State Persistence
176+
177+
**systemd-rfkill**: Persists WiFi enable/disable state across reboots
178+
179+
EdgeOS uses the standard `systemd-rfkill` service to persist WiFi radio state:
180+
- When user enables WiFi → ConnMan unblocks rfkill → systemd-rfkill saves state to `/var/lib/systemd/rfkill/platform-*:wlan`
181+
- When user disables WiFi → ConnMan blocks rfkill → systemd-rfkill saves state
182+
- On boot → systemd-rfkill restores the saved rfkill state before ConnMan starts
183+
- ConnMan reads the hardware rfkill state and synchronizes its settings
184+
185+
**Migration Service**: `wifi-rfkill-migrate.service`
186+
187+
One-time service that runs on first boot to migrate existing ConnMan WiFi policy into rfkill state:
188+
- Reads `[WiFi] Enable=true/false` from `/var/lib/connman/settings`
189+
- Sets initial rfkill state accordingly
190+
- Creates marker file `/var/lib/systemd/wifi-rfkill-migrated` to prevent re-running
191+
- Runs before ConnMan to ensure correct initial state
192+
193+
**WiFi Registration Nudge**: `wifi-nudge.service`
194+
195+
Ensures wpa_supplicant properly registers wlan0 with ConnMan on boot when WiFi is enabled:
196+
- Runs after ConnMan starts
197+
- If WiFi is enabled, performs a disable/enable cycle to ensure wpa_supplicant registration
198+
- Harmless if WiFi is disabled (skips the toggle)
199+
- Fixes race where wpa_supplicant doesn't register wlan0 before ConnMan checks
200+
201+
### Key Insights
202+
203+
**PHY-aware timing**: udev rule runs when PHY actually exists, not "early" before it appears
204+
205+
**Standard persistence**: Uses systemd-rfkill (standard Linux component) instead of custom solution
206+
207+
**Clean separation**: udev owns regulatory domain, systemd-rfkill owns radio state persistence, ConnMan owns network management
208+
209+
**No USB gadget interference**: ConnMan starts immediately for USB gadget networking; WiFi services run in parallel
210+
211+
**Robust state handling**: rfkill is the single source of truth; ConnMan synchronizes to match hardware state
212+
213+
### Previous Failed Approaches
214+
215+
**Kernel parameter `rfkill.default_state=1`**: Doesn't work - brcmfmac driver ignores it
216+
217+
**Relying solely on `ieee80211_regdom` modprobe parameter**: Sets global default but WiFi PHY won't adopt it without userspace regulatory hint
218+
219+
**Running `iw reg set` too early**: PHY doesn't exist yet, hint goes nowhere
220+
221+
**wpa_supplicant country= with ConnMan**: wpa_supplicant runs in `-u` mode (D-Bus), won't read config file until ConnMan tells it to manage an interface (but WiFi is blocked so it can't)
222+
223+
**Complex systemd services**: Early services can interfere with USB gadget and other boot processes
224+
225+
## Edge Cases and Considerations
226+
227+
### Multiple WiFi PHYs (USB Dongles)
228+
229+
The udev rule runs **per-PHY** automatically. If you plug in a USB WiFi dongle, it will get its own phy1 device and the regulatory domain will be set for it as well. This is the correct behavior.
230+
231+
### Country Code Provisioning
232+
233+
The recommended approach for setting the country code during provisioning:
234+
235+
```bash
236+
# Update the country code file
237+
echo GB > /etc/regdomain
238+
239+
# Trigger udev to re-apply immediately
240+
udevadm trigger -s ieee80211
241+
242+
# Verify it was applied
243+
iw reg get
244+
```
245+
246+
This avoids manual `iw reg set` commands and ensures the setting persists across reboots.
247+
248+
### systemd-rfkill State Files
249+
250+
EdgeOS uses systemd-rfkill to persist WiFi radio state across reboots. State files are stored in `/var/lib/systemd/rfkill/`:
251+
- `platform-*:wlan` - WiFi rfkill state (0 = unblocked, 1 = blocked)
252+
- `platform-*:bluetooth` - Bluetooth rfkill state
253+
254+
**Do not manually edit these files.** They are managed automatically by systemd-rfkill when ConnMan toggles the radio state.
255+
256+
### Alternative Network Managers
257+
258+
If you ever switch from ConnMan to standalone wpa_supplicant (with `wpa_supplicant@wlan0.service`), you'll need to:
259+
260+
1. Add `country=XX` to `/etc/wpa_supplicant/wpa_supplicant-wlan0.conf`
261+
2. Disable ConnMan's WiFi management
262+
263+
With ConnMan's `-u` D-Bus mode, the wpa_supplicant config file is ignored.
264+
265+
### Broadcom Firmware Requirements (Raspberry Pi)
266+
267+
Broadcom WiFi (brcmfmac) requires matching firmware files:
268+
269+
- Main firmware: `/lib/firmware/brcm/brcmfmac43455-sdio.bin` (or similar for your chip)
270+
- CLM blob: `/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob` (contains per-country channel/power limits)
271+
272+
If the CLM blob is missing, WiFi may fail to initialize or have limited channel access. The `wireless-regdb` package provides the general regulatory database, but Broadcom needs its own CLM blob.
273+
274+
## Troubleshooting
275+
276+
### WiFi is soft-blocked (expected behavior)
277+
278+
WiFi is **soft-blocked by default** for power saving. This is normal! To enable WiFi:
279+
280+
```bash
281+
connmanctl enable wifi
282+
```
283+
284+
Check the regulatory domain:
285+
```bash
286+
iw reg get
287+
```
288+
289+
Should show:
290+
```
291+
global
292+
country US: DFS-FCC
293+
```
294+
295+
If it shows `country 00` or `country 99`, the regulatory domain is not set. Check if the udev rule executed:
296+
297+
```bash
298+
# Test the udev rule manually
299+
udevadm test --action=add /sys/class/ieee80211/phy0
300+
301+
# Check boot logs for udev rule execution
302+
journalctl -b | grep -E 'ieee80211|iw reg'
303+
```
304+
305+
### WiFi scan returns "Not implemented"
306+
307+
ConnMan is not built with WiFi support. Check:
308+
```bash
309+
connmanctl technologies
310+
```
311+
312+
Should list `/net/connman/technology/wifi` - if missing, `wifi` is not in DISTRO_FEATURES.
313+
314+
### wlan0 interface doesn't appear
315+
316+
Check if wireless firmware is loaded:
317+
```bash
318+
dmesg | grep brcmfmac
319+
```
320+
321+
Should see firmware loading successfully.
322+
323+
### WiFi scanning works but can't connect
324+
325+
Check wpa_supplicant is running with D-Bus support:
326+
```bash
327+
ps aux | grep wpa_supplicant
328+
```
329+
330+
Should see `-u` flag (D-Bus mode).
331+
332+
### Missing regulatory database or firmware
333+
334+
For Raspberry Pi Broadcom WiFi:
335+
336+
```bash
337+
# Check regulatory database
338+
ls -la /lib/firmware/regulatory.db
339+
340+
# Check Broadcom firmware and CLM blob
341+
ls -la /lib/firmware/brcm/ | grep -E 'brcmfmac.*\.(bin|txt|clm_blob)'
342+
343+
# Check kernel messages for firmware loading
344+
dmesg | grep -i firmware
345+
```
346+
347+
Required packages: `wireless-regdb`, `linux-firmware-bcm43455` (or equivalent for your hardware).
348+
349+
## Build Configuration
350+
351+
WiFi support is controlled by these Yocto variables:
352+
353+
**In `conf/distro/edgeos.conf`**:
354+
```bitbake
355+
DISTRO_FEATURES:append = " wifi"
356+
```
357+
358+
**In `recipes-connectivity/connman/connman_%.bbappend`**:
359+
```bitbake
360+
PACKAGECONFIG:append = " wifi"
361+
```
362+
363+
## Recipe Files
364+
365+
- `meta-edgeos/recipes-connectivity/connman/connman_%.bbappend` - ConnMan WiFi configuration, udev rules, and regulatory domain setup
366+
- `meta-edgeos/recipes-core/packagegroups/packagegroup-edgeos-wifi.bb` - WiFi package group
367+
368+
## References
369+
370+
- [ConnMan Documentation](https://git.kernel.org/pub/scm/network/connman/connman.git/about/)
371+
- [wpa_supplicant D-Bus API](https://w1.fi/wpa_supplicant/devel/dbus.html)
372+
- [Linux Wireless Regulatory Documentation](https://wireless.wiki.kernel.org/en/developers/regulatory)
373+
- [systemd-rfkill](https://www.freedesktop.org/software/systemd/man/systemd-rfkill.service.html)

meta-edgeos/conf/distro/edgeos.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ SDK_VERSION = "${@d.getVar('DISTRO_VERSION')}"
1717
# remove distro unnecessary feature(s)
1818
DISTRO_FEATURES:remove = "x11 wayland opengl opengles egl sysvinit ptest 3g"
1919

20+
# Add wifi support for connman
21+
DISTRO_FEATURES:append = " wifi"
22+
2023
# Add virtualization support when container runtime is enabled
2124
DISTRO_FEATURES:append = "${@' virtualization' if d.getVar('EDGEOS_CONTAINER_RUNTIME') == '1' else ''}"

meta-edgeos/conf/local.conf.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ ENABLE_DWC2_PERIPHERAL = "1"
3535
RASPBERRYPI_CAMERA_V2 = "1"
3636
RASPBERRYPI_CAMERA_V3 = "1"
3737

38+
# Kernel version - use 6.12 for latest improvements
39+
PREFERRED_VERSION_linux-raspberrypi = "6.12.%"
40+
3841
# EdgeOS feature flags
3942
EDGEOS_DEBUG = "1"
4043
EDGEOS_DEBUG_UART = "1"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Bring up wireless interfaces so wpa_supplicant can register them
2+
# wpa_supplicant in D-Bus mode only registers interfaces that are UP
3+
SUBSYSTEM=="net", ACTION=="add", KERNEL=="wlan*", RUN+="/sbin/ip link set %k up"

0 commit comments

Comments
 (0)