Skip to content
Merged

Misc #1366

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
149 changes: 144 additions & 5 deletions board/common/rootfs/usr/libexec/infix/iw.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,73 @@ def parse_interface_info(ifname):
return result


def parse_stations(ifname):
"""
Parse 'iw dev <name> station dump' output
Returns: list of connected stations with stats
"""
output = run_iw('dev', ifname, 'station', 'dump')
if not output:
return []

stations = []
current = None

for line in output.splitlines():
stripped = line.strip()

# New station entry: "Station aa:bb:cc:dd:ee:ff (on wifiX)"
if stripped.startswith('Station '):
if current:
stations.append(current)
parts = stripped.split()
if len(parts) >= 2:
current = {'mac-address': parts[1].lower()}
else:
current = None
continue

if not current or ':' not in stripped:
continue

key, _, value = stripped.partition(':')
key = key.strip()
value = value.strip()

try:
if key == 'signal':
# Format: "-42 dBm" or "-42 [-44, -45] dBm"
current['rssi'] = int(value.split()[0])
elif key == 'connected time':
# Format: "123 seconds"
current['connected-time'] = int(value.split()[0])
elif key == 'rx bytes':
current['rx-bytes'] = value # counter64: string-encoded
elif key == 'tx bytes':
current['tx-bytes'] = value # counter64: string-encoded
elif key == 'rx packets':
current['rx-packets'] = value # counter64: string-encoded
elif key == 'tx packets':
current['tx-packets'] = value # counter64: string-encoded
elif key == 'tx bitrate':
# Format: "866.7 MBit/s ..." - convert to 100kbit/s units
speed_mbps = float(value.split()[0])
current['tx-speed'] = int(speed_mbps * 10)
elif key == 'rx bitrate':
speed_mbps = float(value.split()[0])
current['rx-speed'] = int(speed_mbps * 10)
elif key == 'inactive time':
# Format: "1234 ms"
current['inactive-time'] = int(value.split()[0])
except (ValueError, IndexError):
continue

if current:
stations.append(current)

return stations


def parse_survey(ifname):
"""
Parse 'iw dev <name> survey dump' output
Expand Down Expand Up @@ -396,6 +463,66 @@ def parse_dev():
return result


def parse_link(ifname):
"""
Parse 'iw dev <name> link' output for station mode
Returns: {connected, bssid, ssid, frequency, signal, tx_bitrate, rx_bitrate}
"""
output = run_iw('dev', ifname, 'link')
if not output:
return {'connected': False}

if 'Not connected' in output:
return {'connected': False}

result = {'connected': True}

for line in output.splitlines():
stripped = line.strip()

# Connected to aa:bb:cc:dd:ee:ff
if stripped.startswith('Connected to '):
parts = stripped.split()
if len(parts) >= 3:
result['bssid'] = parts[2].lower()

# SSID: NetworkName
elif stripped.startswith('SSID: '):
result['ssid'] = stripped[6:]

# freq: 5180
elif stripped.startswith('freq: '):
try:
result['frequency'] = int(stripped[6:])
except ValueError:
pass

# signal: -42 dBm
elif stripped.startswith('signal: '):
try:
result['rssi'] = int(stripped.split()[1])
except (ValueError, IndexError):
pass

# tx bitrate: 866.7 MBit/s ...
elif stripped.startswith('tx bitrate: '):
try:
speed = float(stripped.split()[2])
result['tx-speed'] = int(speed * 10) # 100kbit/s units
except (ValueError, IndexError):
pass

# rx bitrate: 780.0 MBit/s ...
elif stripped.startswith('rx bitrate: '):
try:
speed = float(stripped.split()[2])
result['rx-speed'] = int(speed * 10)
except (ValueError, IndexError):
pass

return result


def main():
if len(sys.argv) < 2:
print(json.dumps({
Expand All @@ -404,14 +531,17 @@ def main():
'list': 'List all PHY devices',
'dev': 'List all interfaces grouped by PHY',
'info': 'Get PHY or interface information (requires device)',
'survey': 'Get channel survey data (requires interface name)'
'survey': 'Get channel survey data (requires interface)',
'station': 'Get connected stations in AP mode (requires interface)',
'link': 'Get link info in station mode (requires interface)'
},
'examples': [
'iw.py list',
'iw.py dev',
'iw.py info radio0',
'iw.py info phy4',
'iw.py info wlan0',
'iw.py station wifi0',
'iw.py link wlan0',
'iw.py survey wlan0'
]
}, indent=2))
Expand All @@ -434,12 +564,21 @@ def main():
data = parse_phy_info(device)
else:
data = parse_interface_info(device)
elif command == 'station':
if len(sys.argv) < 3:
data = {'error': 'station command requires interface argument'}
else:
data = parse_stations(sys.argv[2])
elif command == 'link':
if len(sys.argv) < 3:
data = {'error': 'link command requires interface argument'}
else:
data = parse_link(sys.argv[2])
elif command == 'survey':
if len(sys.argv) < 3:
data = {'error': 'survey command requires device argument'}
data = {'error': 'survey command requires interface argument'}
else:
device = sys.argv[2]
data = parse_survey(device)
data = parse_survey(sys.argv[2])
else:
data = {'error': f'Unknown command: {command}'}

Expand Down
29 changes: 5 additions & 24 deletions doc/wifi.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,46 +84,27 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> leave

**Key radio parameters:**
- `country-code`: Two-letter ISO 3166-1 code - determines allowed channels and maximum power. Examples: US, DE, GB, SE, FR, JP. **Must match your physical location for legal compliance.**
- `band`: 2.4GHz, 5GHz, or 6GHz (required for AP mode). Band selection automatically enables appropriate WiFi standards (2.4GHz: 802.11n, 5GHz: 802.11n/ac, 6GHz: 802.11n/ac/ax)
- `band`: 2.4GHz, 5GHz, or 6GHz (required for AP mode). Automatically enables appropriate WiFi standards (2.4GHz: 802.11n/ax, 5GHz: 802.11n/ac/ax, 6GHz: 802.11ax)
- `channel`: Channel number (1-196) or "auto" (required for AP mode). When set to "auto", defaults to channel 6 for 2.4GHz, channel 36 for 5GHz, or channel 109 for 6GHz
- `enable-80211ax`: Boolean (default: false). Opt-in to enable 802.11ax (WiFi 6) on 2.4GHz and 5GHz bands. The 6GHz band always uses 802.11ax regardless of this setting

> [!NOTE]
> TX power and channel width are automatically determined by the driver based on regulatory constraints, PHY mode, and hardware capabilities.
### WiFi 6 (802.11ax) Support

WiFi 6 (802.11ax) provides improved performance in congested environments through
features like OFDMA, Target Wake Time, and BSS Coloring. By default, WiFi 6 is
only enabled on the 6GHz band (WiFi 6E requirement).
WiFi 6 (802.11ax) is always enabled in AP mode on all bands, providing improved
performance through features like OFDMA, BSS Coloring, and beamforming.

To enable WiFi 6 on 2.4GHz or 5GHz bands:

```
admin@example:/> configure
admin@example:/config/> edit hardware component radio0 wifi-radio
admin@example:/config/hardware/component/radio0/wifi-radio/> set country-code DE
admin@example:/config/hardware/component/radio0/wifi-radio/> set band 5GHz
admin@example:/config/hardware/component/radio0/wifi-radio/> set channel 36
admin@example:/config/hardware/component/radio0/wifi-radio/> set enable-80211ax true
admin@example:/config/hardware/component/radio0/wifi-radio/> leave
```

**WiFi 6 Benefits:**
**WiFi 6 Features (always enabled):**
- **OFDMA**: Better multi-user efficiency in dense environments
- **Target Wake Time**: Improved battery life for client devices
- **1024-QAM**: Higher throughput with strong signal conditions
- **BSS Coloring**: Reduced interference from neighboring networks
- **Beamforming**: Improved signal quality and range

**Requirements:**
- Hardware must support 802.11ax
- Client devices must support WiFi 6 for full benefits
- Older WiFi 5/4 clients can still connect but won't use WiFi 6 features

> [!NOTE]
> The 6GHz band always uses WiFi 6 (802.11ax) regardless of the `enable-80211ax`
> setting, as WiFi 6E requires 802.11ax support.
## Discovering Available Networks (Scanning)

Before connecting to a WiFi network, you need to discover which networks
Expand Down
2 changes: 1 addition & 1 deletion src/bin/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ static int resolve_dst(const char **dst, const struct infix_ds **ds, char **path
static int copy(const char *src, const char *dst)
{
char *srcpath = NULL, *dstpath = NULL;
const struct infix_ds *srcds, *dstds;
const struct infix_ds *srcds = NULL, *dstds = NULL;
bool rmsrc = false;
mode_t oldmask;
int err = 1;
Expand Down
Loading