feat: M3 complete — WiFi, BT keyboard and mouse
Bluetooth (RTL8723D USB combo chip): - GPIO pin 357 (active low) enables WiFi/BT hardware via x6200_gpio_set() - bluez5_utils 5.64 (downgraded from 5.79 — HID input plugin broken in 5.79) - rtl8723d_config.bin added to overlay (missing from linux-firmware package) - S45wifi-bt: GPIO enable + modprobe btusb/uhid/hidp at boot - S85bt-keyboard: auto-connect loop with scan+connect every 20s WiFi (RTL8723DU): - out-of-tree lwfinger/rtw88 driver (RTW88_8723DU not in kernel 6.1 mainline) - linux-firmware RTL_RTW88 for rtw88/rtw8723d_fw.bin - regulatory.db for cfg80211 - wpa_supplicant with multi-network config in /etc/wpa_supplicant.conf - S46wifi: wpa_supplicant + udhcpc at boot Key findings: - RTL8723D USB WiFi (0bda:d723) requires out-of-tree rtw88 on kernel 6.1 - BT and WiFi share same USB device, both need GPIO 357 = 0 to power on - bluez5 5.79 HID input plugin not linked into bluetoothd (build system bug)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ build/
|
|||||||
# Local overrides
|
# Local overrides
|
||||||
local.mk
|
local.mk
|
||||||
.env
|
.env
|
||||||
|
wpa_supplicant.conf
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -41,12 +41,15 @@ The product name **Virtuoso** is reserved for when (if!) Mestre actually deliver
|
|||||||
|
|
||||||
| # | Milestone | Status |
|
| # | Milestone | Status |
|
||||||
|---|----------------------------------------------------------------------|--------|
|
|---|----------------------------------------------------------------------|--------|
|
||||||
| 1 | Boot a recent Buildroot LTS on the X6200 (serial console) | 🟡 In progress |
|
| 1 | Boot a recent Buildroot LTS on the X6200 (serial console) | ✅ 2026-05-03 |
|
||||||
| 2 | Forward-port LCD panel driver — full display output | ⚪ Planned |
|
| 2 | Working audio stack | ✅ 2026-05-06 |
|
||||||
| 3 | piHPSDR running on X6200 with working audio | ⚪ Planned |
|
| 3 | WiFi and Bluetooth keyboard and mouse | ✅ 2026-05-09 |
|
||||||
| 4 | `mestre` service: evdev → ALSA virtual MIDI bridge | ⚪ Planned |
|
| 4 | Forward-port LCD panel driver — full display output | ✅ 2026-05-03 |
|
||||||
| 5 | End-to-end: X6200 controlling Hermes-Lite 2 as a control head | ⚪ Planned |
|
| 5 | piHPSDR running on X6200, controlled by mouse and keyboard | ⚪ Planned |
|
||||||
| 6 | First-boot wizard, settings persistence, polished UX | ⚪ Planned |
|
| 6 | piHPSDR audio — HL2 audio via SoC speaker | ⚪ Planned |
|
||||||
|
| 7 | `mestre` service: evdev → ALSA virtual MIDI bridge | ⚪ Planned |
|
||||||
|
| 8 | End-to-end: X6200 controlling Hermes-Lite 2 as a control head | ⚪ Planned |
|
||||||
|
| 9 | First-boot wizard, settings persistence, polished UX | ⚪ Planned |
|
||||||
| ∞ | Earn the right to call it Virtuoso | ⚪ TBD |
|
| ∞ | Earn the right to call it Virtuoso | ⚪ TBD |
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
@@ -66,7 +69,7 @@ The architecture is deliberately patch-free: piHPSDR is used as-is, and the MIDI
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
> ⚠️ **Pre-1.0**: build instructions are aspirational while milestone 1 is in progress.
|
> ⚠️ **Pre-1.0**: actively developed. Core build works; some milestones still in progress.
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
|
|||||||
255
audio-architecture.md
Normal file
255
audio-architecture.md
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
# X6200 Audio Architecture
|
||||||
|
|
||||||
|
A detailed technical account of how audio works on the Xiegu X6200,
|
||||||
|
reverse-engineered during Mestre development (May 2026).
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The X6200 has two distinct audio sources and one audio output path.
|
||||||
|
Understanding the distinction between them is essential for any software
|
||||||
|
that wants to produce sound on this radio.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ X6200 Hardware │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ RF Front │ │ Allwinner│ │
|
||||||
|
│ │ End │ │ R16 │ (SoC / Application CPU) │
|
||||||
|
│ │ + FPGA │ │ │ │
|
||||||
|
│ └────┬─────┘ └────┬─────┘ │
|
||||||
|
│ │ RX audio │ DAC audio │
|
||||||
|
│ │ (analog) │ (digital → analog via codec) │
|
||||||
|
│ └──────┬────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────▼─────┐ │
|
||||||
|
│ │ Audio │ ← voice_rec bit selects source │
|
||||||
|
│ │ MUX │ (controlled via I2C to MCU) │
|
||||||
|
│ └────┬─────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────▼─────┐ │
|
||||||
|
│ │ PA │ ← Power Amplifier │
|
||||||
|
│ │ (Class D)│ enabled by rxvol_set > 0 │
|
||||||
|
│ └────┬─────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌────▼─────┐ │
|
||||||
|
│ │ Speaker │ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Two Audio Sources
|
||||||
|
|
||||||
|
### Source 1: RX-Radio Audio (default)
|
||||||
|
|
||||||
|
The RF front-end and FPGA process the received RF signal and produce
|
||||||
|
an analog audio output — the classic "radio receiver sound". This is
|
||||||
|
the default audio source at power-on. It is completely independent of
|
||||||
|
the SoC and ALSA; it bypasses the codec entirely and is fed directly
|
||||||
|
into the audio MUX.
|
||||||
|
|
||||||
|
This is why you hear receiver noise immediately when the X6200 boots,
|
||||||
|
even before Linux has fully started.
|
||||||
|
|
||||||
|
### Source 2: SoC DAC Audio
|
||||||
|
|
||||||
|
The Allwinner R16 SoC contains an integrated audio codec
|
||||||
|
(`sun8i-a33-audio`) which can produce digital audio, converted to
|
||||||
|
analog via the DAC. This is the source used by software running on
|
||||||
|
the SoC — piHPSDR, aplay, PulseAudio, etc.
|
||||||
|
|
||||||
|
This source goes through the standard Linux ALSA stack:
|
||||||
|
|
||||||
|
```
|
||||||
|
Application (piHPSDR, aplay)
|
||||||
|
→ PulseAudio (mixing, resampling)
|
||||||
|
→ ALSA (hw:0,0 / pcmC0D0p)
|
||||||
|
→ sun8i-a33-audio codec driver
|
||||||
|
→ DAC (analog output)
|
||||||
|
→ Audio MUX
|
||||||
|
→ PA
|
||||||
|
→ Speaker
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Audio MUX — The Critical Control Point
|
||||||
|
|
||||||
|
A hardware multiplexer inside the MCU/FPGA selects which source
|
||||||
|
reaches the power amplifier and speaker. This MUX is controlled
|
||||||
|
via I2C, **not** via ALSA or any Linux kernel interface.
|
||||||
|
|
||||||
|
### I2C Protocol
|
||||||
|
|
||||||
|
The X6200 MCU sits on I2C bus 0, address `0x72`. All hardware control
|
||||||
|
uses the X6200Control library protocol: a 6-byte write transaction
|
||||||
|
consisting of a 2-byte register address followed by a 4-byte value.
|
||||||
|
|
||||||
|
The audio MUX is controlled by the `voice_rec` bit in the
|
||||||
|
`sple_atue_trx` register (command index 12, address `0x0030`):
|
||||||
|
|
||||||
|
| voice_rec bit | Audio source heard in speaker |
|
||||||
|
|---------------|-------------------------------|
|
||||||
|
| 0 (default) | RX-radio (RF receiver) |
|
||||||
|
| 1 | SoC DAC (software audio) |
|
||||||
|
|
||||||
|
**To switch to SoC DAC audio:**
|
||||||
|
```bash
|
||||||
|
i2ctransfer -y 0 w6@0x72 0x00 0x30 0x08 0x00 0x00 0x00
|
||||||
|
```
|
||||||
|
|
||||||
|
**To switch back to RX-radio audio:**
|
||||||
|
```bash
|
||||||
|
i2ctransfer -y 0 w6@0x72 0x00 0x30 0x00 0x00 0x00 0x00
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Power Amplifier — The Other Critical Control Point
|
||||||
|
|
||||||
|
The speaker PA (power amplifier) is not always on. It is enabled
|
||||||
|
as a side effect of setting the speaker volume via `x6200_control_rxvol_set()`.
|
||||||
|
When `rxvol = 0` (the default after `x6200_control_init()`), the PA
|
||||||
|
is effectively off and nothing is heard regardless of the MUX position
|
||||||
|
or ALSA configuration.
|
||||||
|
|
||||||
|
This is why setting the correct mixer levels alone is not sufficient —
|
||||||
|
the MCU must be told to activate the PA via the I2C control protocol.
|
||||||
|
|
||||||
|
## The ALSA Codec — Clock Dependency
|
||||||
|
|
||||||
|
The `sun8i-a33-audio` codec requires its master clock (`pll-audio`,
|
||||||
|
22.5792 MHz) to be running for DAC output to work. The kernel's
|
||||||
|
clock framework only enables this clock when a consumer holds an
|
||||||
|
active reference — i.e., when an application has the PCM device open.
|
||||||
|
|
||||||
|
**This is the most subtle requirement:** if no process holds
|
||||||
|
`/dev/snd/pcmC0D0p` open, the audio clocks are disabled and the
|
||||||
|
codec produces no output, even if the ALSA mixer is correctly
|
||||||
|
configured and voice_rec is set to DAC mode.
|
||||||
|
|
||||||
|
PulseAudio, running in system mode, holds the PCM device open
|
||||||
|
continuously. This is what keeps the clocks running at all times
|
||||||
|
and makes reliable audio possible.
|
||||||
|
|
||||||
|
```
|
||||||
|
PulseAudio running → pcmC0D0p open → pll-audio enabled → codec active
|
||||||
|
```
|
||||||
|
|
||||||
|
Without PulseAudio (or equivalent), every `aplay` invocation would
|
||||||
|
need to wait for the codec to stabilise after the clock starts —
|
||||||
|
and the X6200 MCU may reset the MUX during this window.
|
||||||
|
|
||||||
|
## The ALSA Mixer — Required Configuration
|
||||||
|
|
||||||
|
The codec's internal mixer must be configured correctly for audio
|
||||||
|
to flow from the DAC to the analog output. The critical controls are:
|
||||||
|
|
||||||
|
| numid | Name | Required value |
|
||||||
|
|-------|-----------------------------------|----------------|
|
||||||
|
| 2 | AIF1 DA0 Playback Volume | 160, 160 |
|
||||||
|
| 7 | Headphone Playback Volume | 58 |
|
||||||
|
| 8 | Headphone Playback Switch | on, on |
|
||||||
|
| 20 | AIF1 Data Digital ADC Cap. Switch | on, on |
|
||||||
|
| 26 | AIF1 DA0 Stereo Playback Route | 3, 3 (Mix Mono)|
|
||||||
|
| 28 | AIF1 Slot 0 Digital DAC PB Switch | on, on |
|
||||||
|
| 31 | DAC Playback Switch | off, off |
|
||||||
|
|
||||||
|
Note that `DAC Playback Switch` (numid=31) must be **off** — this is
|
||||||
|
counterintuitive but correct. The signal path goes through AIF1,
|
||||||
|
not the direct DAC path.
|
||||||
|
|
||||||
|
These values are saved in `/var/lib/alsa/asound.state` and restored
|
||||||
|
at boot via `alsactl restore` (init script `S50alsa`).
|
||||||
|
|
||||||
|
## Complete Initialisation Sequence
|
||||||
|
|
||||||
|
The following sequence must be performed in this exact order at boot:
|
||||||
|
|
||||||
|
### Step 1: ALSA mixer state (S50alsa)
|
||||||
|
```bash
|
||||||
|
alsactl restore
|
||||||
|
```
|
||||||
|
Restores the correct mixer levels from `/var/lib/alsa/asound.state`.
|
||||||
|
|
||||||
|
### Step 2: PulseAudio (S50pulseaudio)
|
||||||
|
```bash
|
||||||
|
pulseaudio --system --daemonize --disallow-exit --exit-idle-time=-1
|
||||||
|
```
|
||||||
|
Starts PulseAudio in system mode. This opens `pcmC0D0p` and keeps
|
||||||
|
the ALSA clocks running continuously.
|
||||||
|
|
||||||
|
### Step 3: X6200 hardware init (S70mestre-audio)
|
||||||
|
```python
|
||||||
|
lib.x6200_control_init() # Establishes I2C state, sends all_cmd
|
||||||
|
lib.x6200_control_spmode_set(False) # Speaker mode (not headphone)
|
||||||
|
lib.x6200_control_rxvol_set(50) # Enables PA, sets volume (0-100)
|
||||||
|
```
|
||||||
|
`x6200_control_init()` sends a full command packet to the MCU via I2C,
|
||||||
|
establishing all register values. **It sets rxvol=0** (PA off) as
|
||||||
|
its default. `rxvol_set(50)` must follow immediately to enable the PA.
|
||||||
|
|
||||||
|
### Step 4: Switch audio MUX to SoC DAC (S70mestre-audio)
|
||||||
|
```bash
|
||||||
|
i2ctransfer -y 0 w6@0x72 0x00 0x30 0x08 0x00 0x00 0x00
|
||||||
|
```
|
||||||
|
Sets the `voice_rec` bit, switching the speaker from RX-radio audio
|
||||||
|
to SoC DAC audio. From this point, any audio played via PulseAudio
|
||||||
|
or directly to `hw:0,0` (44100 Hz, stereo, S16_LE) will be heard
|
||||||
|
in the speaker.
|
||||||
|
|
||||||
|
## Mestre's Implementation
|
||||||
|
|
||||||
|
Mestre's init scripts implement this sequence:
|
||||||
|
|
||||||
|
```
|
||||||
|
S50alsa → alsactl restore
|
||||||
|
S50pulseaudio → pulseaudio --system
|
||||||
|
S70mestre-audio → x6200_control_init + rxvol_set + voice_rec switch
|
||||||
|
S96keepalive → I2C PMU keepalive (prevents automatic power-off)
|
||||||
|
```
|
||||||
|
|
||||||
|
The audio MUX switch command (`voice_rec=on`) is permanent until the
|
||||||
|
next `x6200_control_init()` call — it does not need to be repeated.
|
||||||
|
However, the PMU keepalive (separate from audio) must run continuously
|
||||||
|
or the radio will power off after approximately 180 seconds.
|
||||||
|
|
||||||
|
## Volume Control
|
||||||
|
|
||||||
|
Speaker volume is controlled via `x6200_control_rxvol_set(vol)` where
|
||||||
|
`vol` is 0-100. This sends an I2C command to the MCU which adjusts
|
||||||
|
an analog gain stage. **It is not reflected in the ALSA mixer** and
|
||||||
|
cannot be read or set via `amixer`.
|
||||||
|
|
||||||
|
The ALSA mixer `Headphone Playback Volume` (numid=7) and
|
||||||
|
`AIF1 DA0 Playback Volume` (numid=2) affect the digital gain
|
||||||
|
in the codec but are set to fixed values at boot and not used
|
||||||
|
for runtime volume control in Mestre.
|
||||||
|
|
||||||
|
## Known Quirks
|
||||||
|
|
||||||
|
**rxvol_set defaults to 0 after init.** Every call to
|
||||||
|
`x6200_control_init()` resets the speaker volume to 0 (PA off).
|
||||||
|
Always call `rxvol_set(N)` immediately after `init()`.
|
||||||
|
|
||||||
|
**voice_rec is reset by x6200_control_init().** If `init()` is called
|
||||||
|
again (e.g., after a crash), the MUX reverts to RX-radio mode and
|
||||||
|
the full init sequence must be repeated.
|
||||||
|
|
||||||
|
**PulseAudio must start before mestre-audio.** If PulseAudio is not
|
||||||
|
running when the audio MUX is switched, the codec clocks may not be
|
||||||
|
active and the switch may have no audible effect.
|
||||||
|
|
||||||
|
**44100 Hz stereo S16_LE** is the correct format for direct hardware
|
||||||
|
playback (`hw:0,0`). PulseAudio handles resampling from other rates.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [gdyuldin/X6200Control](https://github.com/gdyuldin/X6200Control) —
|
||||||
|
I2C control library source, including the `sple_atue_trx` register
|
||||||
|
layout and `voice_rec` bit definition.
|
||||||
|
- [gdyuldin/x6200_gui](https://github.com/gdyuldin/x6200_gui) —
|
||||||
|
Reference implementation of the full audio init sequence
|
||||||
|
(`audio_play_en()`, `audio_init()`, `radio_init()`).
|
||||||
|
- [AetherRadio/X6100Control](https://github.com/AetherRadio/X6100Control) —
|
||||||
|
X6100 control library (protocol-compatible with X6200), used to
|
||||||
|
understand the register map before X6200Control source was available.
|
||||||
|
- Allwinner R16 / sun8i-a33 audio driver: `sound/soc/sunxi/` in the
|
||||||
|
Linux kernel tree. The codec is `sun8i-codec` with an AIF1 I2S
|
||||||
|
interface to the SoC's audio PLL.
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
menu "Mestre"
|
menu "Mestre"
|
||||||
|
|
||||||
source "$BR2_EXTERNAL_MESTRE_PATH/package/x6200-control/Config.in"
|
source "$BR2_EXTERNAL_MESTRE_PATH/package/x6200-control/Config.in"
|
||||||
|
source "$BR2_EXTERNAL_MESTRE_PATH/package/rtw88/Config.in"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -35,3 +35,8 @@ CONFIG_SND_DUMMY=m
|
|||||||
# Expose kernel config via /proc/config.gz (useful for debugging)
|
# Expose kernel config via /proc/config.gz (useful for debugging)
|
||||||
CONFIG_IKCONFIG=y
|
CONFIG_IKCONFIG=y
|
||||||
CONFIG_IKCONFIG_PROC=y
|
CONFIG_IKCONFIG_PROC=y
|
||||||
|
|
||||||
|
# HID via userspace (required for Bluetooth HID devices)
|
||||||
|
CONFIG_UHID=y
|
||||||
|
CONFIG_INPUT_UINPUT=y
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,13 @@ CONFIG_NET_IPIP=y
|
|||||||
# CONFIG_INET_DIAG is not set
|
# CONFIG_INET_DIAG is not set
|
||||||
# CONFIG_IPV6 is not set
|
# CONFIG_IPV6 is not set
|
||||||
CONFIG_CAN=y
|
CONFIG_CAN=y
|
||||||
CONFIG_BT=m
|
CONFIG_BT=y
|
||||||
CONFIG_BT_RFCOMM=m
|
CONFIG_BT_RFCOMM=y
|
||||||
CONFIG_BT_BNEP=m
|
CONFIG_BT_BNEP=y
|
||||||
CONFIG_BT_HIDP=m
|
CONFIG_BT_HIDP=y
|
||||||
CONFIG_BT_HCIBTUSB=m
|
CONFIG_BT_HCIBTUSB=y
|
||||||
|
CONFIG_UHID=y
|
||||||
|
CONFIG_HIDRAW=y
|
||||||
CONFIG_CFG80211=m
|
CONFIG_CFG80211=m
|
||||||
CONFIG_CFG80211_DEVELOPER_WARNINGS=y
|
CONFIG_CFG80211_DEVELOPER_WARNINGS=y
|
||||||
CONFIG_CFG80211_CERTIFICATION_ONUS=y
|
CONFIG_CFG80211_CERTIFICATION_ONUS=y
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Section "Device"
|
||||||
|
Identifier "LCD"
|
||||||
|
Driver "fbdev"
|
||||||
|
Option "Rotate" "CCW"
|
||||||
|
EndSection
|
||||||
45
br2_external/board/x6200/rootfs-overlay/etc/init.d/S35wifi-bt
Executable file
45
br2_external/board/x6200/rootfs-overlay/etc/init.d/S35wifi-bt
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# S45wifi-bt — aktiverar WiFi/BT-chipet (RTL8723D) via GPIO
|
||||||
|
#
|
||||||
|
# X6200:s WiFi/BT-chip är avstängt vid boot och måste aktiveras
|
||||||
|
# via GPIO pin 357 (active low) innan kernel-drivern kan använda det.
|
||||||
|
#
|
||||||
|
# Måste köras innan S40bluetoothd startar.
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
echo "Starting WiFi/BT hardware"
|
||||||
|
python3 - << 'PYEOF'
|
||||||
|
import ctypes
|
||||||
|
lib = ctypes.CDLL("libaether_x6200_control.so")
|
||||||
|
lib.x6200_gpio_init.restype = ctypes.c_bool
|
||||||
|
lib.x6200_gpio_set.argtypes = [ctypes.c_int, ctypes.c_int]
|
||||||
|
lib.x6200_gpio_init()
|
||||||
|
lib.x6200_gpio_set(357, 0) # active low — 0 = ON
|
||||||
|
PYEOF
|
||||||
|
sleep 1
|
||||||
|
modprobe btusb
|
||||||
|
modprobe uhid
|
||||||
|
modprobe hidp
|
||||||
|
sleep 3
|
||||||
|
echo "OK"
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
python3 - << 'PYEOF'
|
||||||
|
import ctypes
|
||||||
|
lib = ctypes.CDLL("libaether_x6200_control.so")
|
||||||
|
lib.x6200_gpio_init.restype = ctypes.c_bool
|
||||||
|
lib.x6200_gpio_set.argtypes = [ctypes.c_int, ctypes.c_int]
|
||||||
|
lib.x6200_gpio_init()
|
||||||
|
lib.x6200_gpio_set(357, 1) # 1 = OFF
|
||||||
|
PYEOF
|
||||||
|
echo "WiFi/BT hardware stopped"
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop; sleep 1; $0 start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
44
br2_external/board/x6200/rootfs-overlay/etc/init.d/S46wifi
Executable file
44
br2_external/board/x6200/rootfs-overlay/etc/init.d/S46wifi
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# S46wifi — ansluter till WiFi via wpa_supplicant
|
||||||
|
#
|
||||||
|
# Körs efter S45wifi-bt (GPIO aktivering av chipet).
|
||||||
|
# Använder /etc/wpa_supplicant.conf för nätverkslista.
|
||||||
|
|
||||||
|
IFACE="wlan0"
|
||||||
|
PIDFILE="/var/run/wpa_supplicant.pid"
|
||||||
|
LOG="/var/log/wifi.log"
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
echo "Starting WiFi"
|
||||||
|
ip link set "$IFACE" up
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
wpa_supplicant -B \
|
||||||
|
-i "$IFACE" \
|
||||||
|
-c /etc/wpa_supplicant.conf \
|
||||||
|
-P "$PIDFILE" \
|
||||||
|
>> "$LOG" 2>&1
|
||||||
|
|
||||||
|
# Hämta IP via DHCP
|
||||||
|
sleep 3
|
||||||
|
dhclient "$IFACE" >> "$LOG" 2>&1 || \
|
||||||
|
udhcpc -i "$IFACE" -b -p /var/run/udhcpc.pid >> "$LOG" 2>&1
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
echo "Stopping WiFi"
|
||||||
|
[ -f "$PIDFILE" ] && kill $(cat "$PIDFILE") 2>/dev/null
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
ip link set "$IFACE" down
|
||||||
|
echo "OK"
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop; sleep 1; $0 start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
58
br2_external/board/x6200/rootfs-overlay/etc/init.d/S85bt-keyboard
Executable file
58
br2_external/board/x6200/rootfs-overlay/etc/init.d/S85bt-keyboard
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# S85bt-keyboard — auto-connect Linocell BTK16 BT-tangentbord
|
||||||
|
#
|
||||||
|
# Försöker ansluta tangentbordet vid boot och startar en bakgrundsprocess
|
||||||
|
# som reconnectar automatiskt när tangentbordet kommer inom räckhåll.
|
||||||
|
#
|
||||||
|
# Tangentbordet måste vara parat och trustat sedan tidigare.
|
||||||
|
# Para manuellt med: bluetoothctl pair ED:96:00:23:E2:02
|
||||||
|
# bluetoothctl trust ED:96:00:23:E2:02
|
||||||
|
|
||||||
|
KEYBOARD_ADDR="ED:96:00:23:E2:02"
|
||||||
|
PIDFILE="/var/run/bt-keyboard.pid"
|
||||||
|
LOG="/var/log/bt-keyboard.log"
|
||||||
|
|
||||||
|
connect_keyboard() {
|
||||||
|
bluetoothctl power on > /dev/null 2>&1
|
||||||
|
bluetoothctl connect "$KEYBOARD_ADDR" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_loop() {
|
||||||
|
while true; do
|
||||||
|
# Kolla om tangentbordet är anslutet
|
||||||
|
if ! bluetoothctl info "$KEYBOARD_ADDR" 2>/dev/null | grep -q "Connected: yes"; then
|
||||||
|
connect_keyboard
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
echo "Starting BT keyboard auto-connect"
|
||||||
|
|
||||||
|
# Försök första anslutning i bakgrunden (tangentbordet kanske inte är i närheten)
|
||||||
|
(sleep 5 && connect_keyboard) &
|
||||||
|
|
||||||
|
# Starta watch-loop som reconnectar när tgb dyker upp
|
||||||
|
watch_loop >> "$LOG" 2>&1 &
|
||||||
|
echo $! > "$PIDFILE"
|
||||||
|
echo "OK"
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
echo "Stopping BT keyboard auto-connect"
|
||||||
|
if [ -f "$PIDFILE" ]; then
|
||||||
|
kill $(cat "$PIDFILE") 2>/dev/null
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
fi
|
||||||
|
bluetoothctl disconnect "$KEYBOARD_ADDR" > /dev/null 2>&1
|
||||||
|
echo "OK"
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop; sleep 1; $0 start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|restart}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
116
br2_external/board/x6200/rootfs-overlay/etc/ssh/sshd_config
Normal file
116
br2_external/board/x6200/rootfs-overlay/etc/ssh/sshd_config
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# $OpenBSD: sshd_config,v 1.104 2021/07/02 05:11:21 dtucker Exp $
|
||||||
|
|
||||||
|
# This is the sshd server system-wide configuration file. See
|
||||||
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
|
# This sshd was compiled with PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||||
|
|
||||||
|
# The strategy used for options in the default sshd_config shipped with
|
||||||
|
# OpenSSH is to specify options with their default value where
|
||||||
|
# possible, but leave them commented. Uncommented options override the
|
||||||
|
# default value.
|
||||||
|
|
||||||
|
#Port 22
|
||||||
|
#AddressFamily any
|
||||||
|
#ListenAddress 0.0.0.0
|
||||||
|
#ListenAddress ::
|
||||||
|
|
||||||
|
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||||
|
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||||
|
|
||||||
|
# Ciphers and keying
|
||||||
|
#RekeyLimit default none
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
#SyslogFacility AUTH
|
||||||
|
#LogLevel INFO
|
||||||
|
|
||||||
|
# Authentication:
|
||||||
|
|
||||||
|
#LoginGraceTime 2m
|
||||||
|
PermitRootLogin yes
|
||||||
|
#StrictModes yes
|
||||||
|
#MaxAuthTries 6
|
||||||
|
#MaxSessions 10
|
||||||
|
|
||||||
|
#PubkeyAuthentication yes
|
||||||
|
|
||||||
|
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
|
||||||
|
# but this is overridden so installations will only check .ssh/authorized_keys
|
||||||
|
AuthorizedKeysFile .ssh/authorized_keys
|
||||||
|
|
||||||
|
#AuthorizedPrincipalsFile none
|
||||||
|
|
||||||
|
#AuthorizedKeysCommand none
|
||||||
|
#AuthorizedKeysCommandUser nobody
|
||||||
|
|
||||||
|
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||||
|
#HostbasedAuthentication no
|
||||||
|
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||||
|
# HostbasedAuthentication
|
||||||
|
#IgnoreUserKnownHosts no
|
||||||
|
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||||
|
#IgnoreRhosts yes
|
||||||
|
|
||||||
|
# To disable tunneled clear text passwords, change to no here!
|
||||||
|
#PasswordAuthentication yes
|
||||||
|
#PermitEmptyPasswords no
|
||||||
|
|
||||||
|
# Change to no to disable s/key passwords
|
||||||
|
#KbdInteractiveAuthentication yes
|
||||||
|
|
||||||
|
# Kerberos options
|
||||||
|
#KerberosAuthentication no
|
||||||
|
#KerberosOrLocalPasswd yes
|
||||||
|
#KerberosTicketCleanup yes
|
||||||
|
#KerberosGetAFSToken no
|
||||||
|
|
||||||
|
# GSSAPI options
|
||||||
|
#GSSAPIAuthentication no
|
||||||
|
#GSSAPICleanupCredentials yes
|
||||||
|
|
||||||
|
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||||
|
# and session processing. If this is enabled, PAM authentication will
|
||||||
|
# be allowed through the KbdInteractiveAuthentication and
|
||||||
|
# PasswordAuthentication. Depending on your PAM configuration,
|
||||||
|
# PAM authentication via KbdInteractiveAuthentication may bypass
|
||||||
|
# the setting of "PermitRootLogin prohibit-password".
|
||||||
|
# If you just want the PAM account and session checks to run without
|
||||||
|
# PAM authentication, then enable this but set PasswordAuthentication
|
||||||
|
# and KbdInteractiveAuthentication to 'no'.
|
||||||
|
#UsePAM no
|
||||||
|
|
||||||
|
#AllowAgentForwarding yes
|
||||||
|
#AllowTcpForwarding yes
|
||||||
|
#GatewayPorts no
|
||||||
|
#X11Forwarding no
|
||||||
|
#X11DisplayOffset 10
|
||||||
|
#X11UseLocalhost yes
|
||||||
|
#PermitTTY yes
|
||||||
|
#PrintMotd yes
|
||||||
|
#PrintLastLog yes
|
||||||
|
#TCPKeepAlive yes
|
||||||
|
#PermitUserEnvironment no
|
||||||
|
#Compression delayed
|
||||||
|
#ClientAliveInterval 0
|
||||||
|
#ClientAliveCountMax 3
|
||||||
|
#UseDNS no
|
||||||
|
#PidFile /var/run/sshd.pid
|
||||||
|
#MaxStartups 10:30:100
|
||||||
|
#PermitTunnel no
|
||||||
|
#ChrootDirectory none
|
||||||
|
#VersionAddendum none
|
||||||
|
|
||||||
|
# no default banner path
|
||||||
|
#Banner none
|
||||||
|
|
||||||
|
# override default of no subsystems
|
||||||
|
Subsystem sftp /usr/libexec/sftp-server
|
||||||
|
|
||||||
|
# Example of overriding settings on a per-user basis
|
||||||
|
#Match User anoncvs
|
||||||
|
# X11Forwarding no
|
||||||
|
# AllowTcpForwarding no
|
||||||
|
# PermitTTY no
|
||||||
|
# ForceCommand cvs server
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
ctrl_interface=/var/run/wpa_supplicant
|
||||||
|
update_config=1
|
||||||
|
country=SE
|
||||||
|
|
||||||
|
# Lägg till dina nätverk här:
|
||||||
|
network={
|
||||||
|
ssid="Ditt_natverk_1"
|
||||||
|
psk="ditt_losenord_1"
|
||||||
|
priority=10
|
||||||
|
}
|
||||||
|
|
||||||
|
network={
|
||||||
|
ssid="Ditt_natverk_2"
|
||||||
|
psk="ditt_losenord_2"
|
||||||
|
priority=5
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBaWQsEKvTUbfTSKEa9sEuu7LpJm9Z07eSluIQV7+AxA joakim@cameron
|
||||||
@@ -126,7 +126,17 @@ BR2_PACKAGE_STRACE=y
|
|||||||
# - Network Manager (using simple /etc/network/interfaces for now)
|
# - Network Manager (using simple /etc/network/interfaces for now)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BR2_GLOBAL_PATCH_DIR="../br2_external/board/x6200/patches"
|
||||||
|
|
||||||
|
# x6200
|
||||||
|
BR2_PACKAGE_X6200_CONTROL=y
|
||||||
|
|
||||||
|
# MISC
|
||||||
|
BR2_PACKAGE_PYTHON3=y
|
||||||
|
BR2_PACKAGE_PYTHON3_CTYPES=y
|
||||||
|
BR_PACKAGE_JOE=y
|
||||||
|
|
||||||
|
# Audio
|
||||||
BR2_PACKAGE_ALSA_UTILS=y
|
BR2_PACKAGE_ALSA_UTILS=y
|
||||||
BR2_PACKAGE_ALSA_UTILS_APLAY=y
|
BR2_PACKAGE_ALSA_UTILS_APLAY=y
|
||||||
BR2_PACKAGE_ALSA_UTILS_AMIXER=y
|
BR2_PACKAGE_ALSA_UTILS_AMIXER=y
|
||||||
@@ -135,12 +145,47 @@ BR2_PACKAGE_ALSA_LIB=y
|
|||||||
BR2_PACKAGE_ALSA_LIB_MIXER=y
|
BR2_PACKAGE_ALSA_LIB_MIXER=y
|
||||||
BR2_PACKAGE_ALSA_LIB_PCM=y
|
BR2_PACKAGE_ALSA_LIB_PCM=y
|
||||||
BR2_PACKAGE_ALSA_LIB_RAWMIDI=y
|
BR2_PACKAGE_ALSA_LIB_RAWMIDI=y
|
||||||
BR2_PACKAGE_X6200_CONTROL=y
|
|
||||||
BR2_PACKAGE_PYTHON3=y
|
|
||||||
BR2_PACKAGE_PYTHON3_CTYPES=y
|
|
||||||
BR2_PACKAGE_PULSEAUDIO=y
|
BR2_PACKAGE_PULSEAUDIO=y
|
||||||
BR2_PACKAGE_PULSEAUDIO_DAEMON=y
|
BR2_PACKAGE_PULSEAUDIO_DAEMON=y
|
||||||
BR2_PACKAGE_PULSEAUDIO_MODULE_ALSA=y
|
BR2_PACKAGE_PULSEAUDIO_MODULE_ALSA=y
|
||||||
BR2_PACKAGE_PULSEAUDIO=y
|
BR2_PACKAGE_PULSEAUDIO=y
|
||||||
BR2_PACKAGE_PULSEAUDIO_DAEMON=y
|
BR2_PACKAGE_PULSEAUDIO_DAEMON=y
|
||||||
BR2_PACKAGE_PULSEAUDIO_MODULE_ALSA=y
|
BR2_PACKAGE_PULSEAUDIO_MODULE_ALSA=y
|
||||||
|
|
||||||
|
# X11
|
||||||
|
BR2_PACKAGE_XORG7=y
|
||||||
|
BR2_PACKAGE_XSERVER_XORG_SERVER=y
|
||||||
|
BR2_PACKAGE_XAPP_XINIT=y
|
||||||
|
BR2_PACKAGE_XDRIVER_XF86_VIDEO_FBDEV=y
|
||||||
|
BR2_PACKAGE_XDRIVER_XF86_INPUT_LIBINPUT=y
|
||||||
|
BR2_PACKAGE_XKEYBOARD_CONFIG=y
|
||||||
|
BR2_PACKAGE_LIBINPUT=y
|
||||||
|
BR2_PACKAGE_XTERM=y
|
||||||
|
|
||||||
|
# GTK3
|
||||||
|
BR2_PACKAGE_LIBGTK3=y
|
||||||
|
BR2_PACKAGE_LIBGTK3_X11=y
|
||||||
|
BR2_PACKAGE_LIBEPOXY=y
|
||||||
|
|
||||||
|
# BT TGB MOUSE & WIFI
|
||||||
|
BR2_PACKAGE_LINUX_FIRMWARE=y
|
||||||
|
BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX=y
|
||||||
|
BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX_BT=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS_CLIENT=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS_DEPRECATED=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS_TOOLS=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS_PLUGINS_HID=y
|
||||||
|
BR2_PACKAGE_BLUEZ5_UTILS_TOOLS_HID2HCI=y
|
||||||
|
BR2_PACKAGE_EUDEV=y
|
||||||
|
BR2_PACKAGE_XKEYBOARD_CONFIG=y
|
||||||
|
BR2_PACKAGE_XINPUT=y
|
||||||
|
BR2_PACKAGE_XAPP_XRANDR=y
|
||||||
|
BR2_PACKAGE_LINUX_FIRMWARE_RTL_RTW88=y
|
||||||
|
BR2_PACKAGE_RTW88=y
|
||||||
|
BR2_PACKAGE_LINUX_FIRMWARE_REGULATORY=y
|
||||||
|
BR2_PACKAGE_WPA_SUPPLICANT=y
|
||||||
|
BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y
|
||||||
|
BR2_PACKAGE_WPA_SUPPLICANT_CTRL_IFACE=y
|
||||||
|
BR2_PACKAGE_DHCP=y
|
||||||
|
BR2_PACKAGE_DHCP_CLIENT=y
|
||||||
|
|||||||
8
br2_external/package/rtw88/Config.in
Normal file
8
br2_external/package/rtw88/Config.in
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
config BR2_PACKAGE_RTW88
|
||||||
|
bool "rtw88 out-of-tree WiFi driver"
|
||||||
|
depends on BR2_LINUX_KERNEL
|
||||||
|
help
|
||||||
|
Out-of-tree Realtek rtw88 driver from lwfinger/rtw88.
|
||||||
|
Adds RTL8723DU USB WiFi support not available in kernel 6.1.
|
||||||
|
|
||||||
|
https://github.com/lwfinger/rtw88
|
||||||
16
br2_external/package/rtw88/rtw88.mk
Normal file
16
br2_external/package/rtw88/rtw88.mk
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# rtw88 — out-of-tree Realtek WiFi driver
|
||||||
|
# Includes RTL8723DU USB support not yet in kernel 6.1 mainline.
|
||||||
|
# Based on gdyuldin/AetherX6200Buildroot's recipe.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
RTW88_VERSION = 461b696b51317ba4ca585a4ddb32f2e72cd4efc9
|
||||||
|
RTW88_SITE = https://github.com/lwfinger/rtw88
|
||||||
|
RTW88_SITE_METHOD = git
|
||||||
|
RTW88_LICENSE = GPL-2.0
|
||||||
|
RTW88_INSTALL_STAGING = YES
|
||||||
|
|
||||||
|
$(eval $(kernel-module))
|
||||||
|
$(eval $(generic-package))
|
||||||
326
wifi-bt-architecture.md
Normal file
326
wifi-bt-architecture.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# X6200 WiFi and Bluetooth Architecture
|
||||||
|
|
||||||
|
A detailed technical account of how WiFi and Bluetooth work on the
|
||||||
|
Xiegu X6200, reverse-engineered during Mestre development (May 2026).
|
||||||
|
|
||||||
|
## Hardware Overview
|
||||||
|
|
||||||
|
The X6200 uses a **Realtek RTL8723D** combo chip for both WiFi and
|
||||||
|
Bluetooth. It is connected to the Allwinner R16 SoC via USB and
|
||||||
|
appears as a single USB device:
|
||||||
|
|
||||||
|
```
|
||||||
|
USB ID: 0bda:d723 Realtek Semiconductor Corp. 802.11n WLAN Adapter
|
||||||
|
Bus: USB 3 (musb-hdrc, high-speed)
|
||||||
|
```
|
||||||
|
|
||||||
|
The device exposes three USB interfaces:
|
||||||
|
- Interface 0+1: Bluetooth (class 0xE0, Wireless, RF)
|
||||||
|
- Interface 2: WiFi (class 0xFF, Vendor Specific)
|
||||||
|
|
||||||
|
Both WiFi and Bluetooth share the same USB device and antenna.
|
||||||
|
|
||||||
|
## The Power Control Problem
|
||||||
|
|
||||||
|
**The RTL8723D chip is powered off at boot.** It does not appear on
|
||||||
|
the USB bus until explicitly powered on via a GPIO pin. Without this
|
||||||
|
step, neither WiFi nor Bluetooth will work regardless of what drivers
|
||||||
|
or software is loaded.
|
||||||
|
|
||||||
|
The power control pin is managed via the X6200Control library:
|
||||||
|
|
||||||
|
```
|
||||||
|
GPIO chip: 0 (sun8i-a33 main GPIO controller)
|
||||||
|
GPIO pin: 357
|
||||||
|
Logic: Active LOW (0 = powered ON, 1 = powered OFF)
|
||||||
|
```
|
||||||
|
|
||||||
|
This is controlled via the `x6200_gpio_set()` function from
|
||||||
|
`libaether_x6200_control.so`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import ctypes
|
||||||
|
lib = ctypes.CDLL("libaether_x6200_control.so")
|
||||||
|
lib.x6200_gpio_init.restype = ctypes.c_bool
|
||||||
|
lib.x6200_gpio_set.argtypes = [ctypes.c_int, ctypes.c_int]
|
||||||
|
lib.x6200_gpio_init()
|
||||||
|
lib.x6200_gpio_set(357, 0) # 0 = ON (active low!)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Critical:** `gpio_init()` must be called before `gpio_set()`.
|
||||||
|
The active-low logic is counterintuitive — sending `1` powers the
|
||||||
|
chip OFF, sending `0` powers it ON.
|
||||||
|
|
||||||
|
After powering on, the USB device appears within 1-3 seconds:
|
||||||
|
```
|
||||||
|
usb 3-1: new high-speed USB device number 2 using musb-hdrc
|
||||||
|
usb 3-1: New USB device found, idVendor=0bda, idProduct=d723
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bluetooth
|
||||||
|
|
||||||
|
### Driver
|
||||||
|
|
||||||
|
The Bluetooth interface of the RTL8723D is handled by the standard
|
||||||
|
Linux kernel `btusb` driver with the `btrtl` firmware loader.
|
||||||
|
|
||||||
|
Required kernel modules (compiled as `=m`):
|
||||||
|
```
|
||||||
|
CONFIG_BT=m
|
||||||
|
CONFIG_BT_HCIBTUSB=m
|
||||||
|
CONFIG_BT_RFCOMM=m
|
||||||
|
CONFIG_BT_BNEP=m
|
||||||
|
CONFIG_BT_HIDP=m
|
||||||
|
CONFIG_UHID=y ← required for HID input devices
|
||||||
|
CONFIG_INPUT_UINPUT=y ← required for HID input devices
|
||||||
|
```
|
||||||
|
|
||||||
|
Load order:
|
||||||
|
```bash
|
||||||
|
modprobe btusb # registers hci0 and loads firmware
|
||||||
|
modprobe uhid # userspace HID device support
|
||||||
|
modprobe hidp # HID over Bluetooth protocol
|
||||||
|
```
|
||||||
|
|
||||||
|
### Firmware
|
||||||
|
|
||||||
|
The RTL8723D Bluetooth requires two firmware files:
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `rtl_bt/rtw8723d_fw.bin` | Main BT firmware |
|
||||||
|
| `rtl_bt/rtw8723d_config.bin` | Configuration (10 bytes) |
|
||||||
|
|
||||||
|
**Both files are required.** Without `rtw8723d_config.bin`, `hciconfig
|
||||||
|
hci0 up` fails with:
|
||||||
|
```
|
||||||
|
Bluetooth: hci0: RTL: mandatory config file rtl_bt/rtw8723d_config not found
|
||||||
|
```
|
||||||
|
|
||||||
|
`rtw8723d_config.bin` is **not included** in Buildroot's
|
||||||
|
`BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX_BT` package (as of 2025.02).
|
||||||
|
It must be added to the rootfs overlay manually:
|
||||||
|
|
||||||
|
```
|
||||||
|
br2_external/board/x6200/rootfs-overlay/lib/firmware/rtl_bt/rtw8723d_config.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Source: https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/rtl_bt/rtw8723d_config.bin
|
||||||
|
|
||||||
|
### BlueZ Version
|
||||||
|
|
||||||
|
**BlueZ 5.64 is required.** BlueZ 5.79 (Buildroot 2025.02 default)
|
||||||
|
has a build system bug where the HID input plugin (`input_init`) is
|
||||||
|
not linked into `bluetoothd` despite `--enable-hid` being passed to
|
||||||
|
configure. Symptoms:
|
||||||
|
|
||||||
|
```
|
||||||
|
Failed to connect: org.bluez.Error.NotAvailable br-connection-profile-unavailable
|
||||||
|
```
|
||||||
|
|
||||||
|
The bug manifests because `builtin_modules` in the generated Makefile
|
||||||
|
is empty and the input profile is never registered via
|
||||||
|
`btd_profile_register()`. Downgrading to BlueZ 5.64 resolves the
|
||||||
|
issue.
|
||||||
|
|
||||||
|
To force BlueZ 5.64 in Buildroot, override the version in a local
|
||||||
|
`.mk` file or patch `buildroot/package/bluez5_utils/bluez5_utils.mk`.
|
||||||
|
|
||||||
|
### HID Profile Requirements
|
||||||
|
|
||||||
|
For Bluetooth HID devices (keyboard, mouse) to connect, bluetoothd
|
||||||
|
must be started with `--compat`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/usr/libexec/bluetooth/bluetoothd --compat &
|
||||||
|
```
|
||||||
|
|
||||||
|
And `/etc/bluetooth/input.conf` must have:
|
||||||
|
```ini
|
||||||
|
[General]
|
||||||
|
UserspaceHID=true
|
||||||
|
ClassicBondedOnly=false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pairing a Device
|
||||||
|
|
||||||
|
First-time pairing via `bluetoothctl`:
|
||||||
|
|
||||||
|
```
|
||||||
|
power on
|
||||||
|
agent on
|
||||||
|
default-agent
|
||||||
|
scan on
|
||||||
|
# Wait for device to appear, then:
|
||||||
|
pair XX:XX:XX:XX:XX:XX
|
||||||
|
trust XX:XX:XX:XX:XX:XX
|
||||||
|
connect XX:XX:XX:XX:XX:XX
|
||||||
|
```
|
||||||
|
|
||||||
|
Once trusted, subsequent connections are automatic via the
|
||||||
|
`S85bt-keyboard` init script.
|
||||||
|
|
||||||
|
### Auto-reconnect
|
||||||
|
|
||||||
|
The `S85bt-keyboard` init script polls every 20 seconds and
|
||||||
|
reconnects if the device is not connected. It uses `scan on` before
|
||||||
|
each connection attempt to make the device visible:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{
|
||||||
|
echo "power on"; sleep 1
|
||||||
|
echo "scan on"; sleep 8
|
||||||
|
echo "scan off"; sleep 1
|
||||||
|
echo "connect $KEYBOARD_ADDR"; sleep 3
|
||||||
|
echo "quit"
|
||||||
|
} | bluetoothctl
|
||||||
|
```
|
||||||
|
|
||||||
|
## WiFi
|
||||||
|
|
||||||
|
### Driver
|
||||||
|
|
||||||
|
**The RTL8723D USB WiFi interface is NOT supported by any in-tree
|
||||||
|
driver in Linux 6.1.** Specifically:
|
||||||
|
|
||||||
|
| Driver | Status in 6.1 |
|
||||||
|
|--------|---------------|
|
||||||
|
| `rtl8xxxu` | Does not support 0bda:d723 |
|
||||||
|
| `r8723bs` | SDIO only, not USB |
|
||||||
|
| `rtw88` | Only has PCIe variant (RTW88_8723DE), USB variant added in 6.2 |
|
||||||
|
|
||||||
|
The solution is the **out-of-tree `lwfinger/rtw88` driver**, which
|
||||||
|
backports USB support for RTL8723D to older kernels. This is the same
|
||||||
|
approach used by the Xiegu X6100 community.
|
||||||
|
|
||||||
|
Buildroot package: `br2_external/package/rtw88/rtw88.mk`
|
||||||
|
|
||||||
|
```makefile
|
||||||
|
RTW88_VERSION = 461b696b51317ba4ca585a4ddb32f2e72cd4efc9
|
||||||
|
RTW88_SITE = https://github.com/lwfinger/rtw88
|
||||||
|
RTW88_SITE_METHOD = git
|
||||||
|
RTW88_LICENSE = GPL-2.0
|
||||||
|
$(eval $(kernel-module))
|
||||||
|
$(eval $(generic-package))
|
||||||
|
```
|
||||||
|
|
||||||
|
After loading, the driver creates `wlan0`:
|
||||||
|
```
|
||||||
|
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 1000
|
||||||
|
link/ether cc:64:1a:52:91:85 brd ff:ff:ff:ff:ff:ff
|
||||||
|
```
|
||||||
|
|
||||||
|
### Firmware
|
||||||
|
|
||||||
|
Two firmware components are required:
|
||||||
|
|
||||||
|
| Component | Buildroot symbol | File |
|
||||||
|
|-----------|-----------------|------|
|
||||||
|
| WiFi firmware | `BR2_PACKAGE_LINUX_FIRMWARE_RTL_RTW88` | `rtw88/rtw8723d_fw.bin` |
|
||||||
|
| Regulatory DB | `BR2_PACKAGE_LINUX_FIRMWARE_REGULATORY` | `regulatory.db` |
|
||||||
|
|
||||||
|
**Note:** After adding `BR2_PACKAGE_LINUX_FIRMWARE_RTL_RTW88`, you
|
||||||
|
must run `linux-firmware-dirclean` before rebuilding, otherwise the
|
||||||
|
`br-firmware.tar` archive is not regenerated and the firmware file
|
||||||
|
will not appear in the target:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/build.sh linux-firmware-dirclean
|
||||||
|
./scripts/build.sh linux-firmware
|
||||||
|
./scripts/build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Configuration
|
||||||
|
|
||||||
|
WiFi is configured via `wpa_supplicant` with a multi-network
|
||||||
|
configuration file at `/etc/wpa_supplicant.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
ctrl_interface=/var/run/wpa_supplicant
|
||||||
|
update_config=1
|
||||||
|
country=SE
|
||||||
|
|
||||||
|
network={
|
||||||
|
ssid="Network_1"
|
||||||
|
psk="password_1"
|
||||||
|
priority=10
|
||||||
|
}
|
||||||
|
|
||||||
|
network={
|
||||||
|
ssid="Network_2"
|
||||||
|
psk="password_2"
|
||||||
|
priority=5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`wpa_supplicant` automatically connects to the highest-priority
|
||||||
|
available network. Add as many `network={}` blocks as needed.
|
||||||
|
|
||||||
|
The file is installed in the rootfs overlay but should be considered
|
||||||
|
sensitive — do not commit real passwords to git.
|
||||||
|
|
||||||
|
### Boot Sequence
|
||||||
|
|
||||||
|
```
|
||||||
|
S35wifi-bt (or S45wifi-bt)
|
||||||
|
→ x6200_gpio_init()
|
||||||
|
→ x6200_gpio_set(357, 0) ← powers on RTL8723D
|
||||||
|
→ modprobe btusb ← registers hci0
|
||||||
|
→ modprobe uhid
|
||||||
|
→ modprobe hidp
|
||||||
|
→ sleep 3 ← wait for USB enumeration
|
||||||
|
|
||||||
|
S40bluetoothd
|
||||||
|
→ bluetoothd --compat ← BlueZ daemon
|
||||||
|
|
||||||
|
S46wifi
|
||||||
|
→ ip link set wlan0 up
|
||||||
|
→ wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
|
||||||
|
→ udhcpc -i wlan0 ← DHCP client
|
||||||
|
|
||||||
|
S85bt-keyboard
|
||||||
|
→ auto-connect loop (scan + connect every 20s)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ordering is critical:** The GPIO power-on script must run before
|
||||||
|
`bluetoothd` and `wpa_supplicant`. Without GPIO 357 = 0, neither
|
||||||
|
WiFi nor Bluetooth hardware is visible on the USB bus.
|
||||||
|
|
||||||
|
## Known Quirks
|
||||||
|
|
||||||
|
**GPIO is active low.** `gpio_set(357, 0)` = ON, `gpio_set(357, 1)`
|
||||||
|
= OFF. This is the opposite of what one might expect.
|
||||||
|
|
||||||
|
**Single USB device, two functions.** WiFi and BT share `0bda:d723`.
|
||||||
|
Powering on the chip enables both simultaneously. There is no way to
|
||||||
|
enable one without the other.
|
||||||
|
|
||||||
|
**rtw8723d_config.bin is missing from linux-firmware package.**
|
||||||
|
Buildroot's `BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX_BT` does not include
|
||||||
|
this 10-byte config file. Must be added to overlay manually.
|
||||||
|
|
||||||
|
**BlueZ 5.79 HID input plugin is broken.** Downgrade to 5.64 for
|
||||||
|
working Bluetooth keyboard and mouse support.
|
||||||
|
|
||||||
|
**lwfinger/rtw88 is pinned to a specific commit.** The driver is
|
||||||
|
under active development; newer commits may break things. Pin to a
|
||||||
|
known-good commit in `rtw88.mk`.
|
||||||
|
|
||||||
|
**linux-firmware-dirclean required after adding RTW88 firmware.**
|
||||||
|
Buildroot caches `br-firmware.tar` and does not regenerate it when
|
||||||
|
new firmware symbols are added to `.config`. Always run
|
||||||
|
`linux-firmware-dirclean` after changing firmware selection.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [lwfinger/rtw88](https://github.com/lwfinger/rtw88) — out-of-tree
|
||||||
|
RTW88 driver with USB support for RTL8723DU
|
||||||
|
- [AetherRadio/X6200Control](https://github.com/gdyuldin/X6200Control) —
|
||||||
|
GPIO control library; `x6200_pin_wifi` (pin 357) is defined in
|
||||||
|
`include/aether_radio/x6200_control/low/gpio.h`
|
||||||
|
- [gdyuldin/AetherX6200Buildroot](https://github.com/gdyuldin/AetherX6200Buildroot) —
|
||||||
|
reference Buildroot configuration; provided the `rtw88` package
|
||||||
|
recipe that Mestre adapts
|
||||||
|
- Linux kernel `drivers/net/wireless/realtek/rtw88/` — mainline RTW88
|
||||||
|
driver (USB variant added in 6.2, not available in 6.1)
|
||||||
|
- Realtek RTL8723D datasheet — combo WiFi+BT chip used in X6200
|
||||||
Reference in New Issue
Block a user