RK3326: fix the shipped-but-broken v2.0 fixes for issues #19 and #35

Issue #19 (RTL8188EUS dongle can scan but never authenticates): the
udev swap rule shipped in v2.0 was structurally a no-op. It matched
the usb_device add event and echoed the device name ("1-1") into
rtl8xxxu's unbind node, but USB drivers bind to the interface
("1-1:1.0"), and at usb_device add time nothing is bound yet anyway;
the follow-up modprobe never rebinds an interface the in-tree driver
already claimed. Users confirmed the auth failure survived into the
release. Rewrite the rule to match the usb_interface event, where the
kernel has already finished its synchronous bind, and deterministically
move the interface: unbind from rtl8xxxu (no-op if it lost the probe
race) and write it into 8188eu's bind node via udev's own %k
substitution (no shell $$ escaping to get wrong).

Issue #35 (R36S Clone V20 speaker silent, headphones fine): the V20-era
clone boards route the speaker through the external amplifier exactly
like the Soysauce (the eeclone DTS already ships rk817-sound-amplified)
but the R36S Clone quirk never exported the playback mux paths nor the
jack input device. Reporters on the issue validated this exact config
by hand-editing the quirk. Also fix headphone_sense: every amixer cset
passed the control name unquoted, so names with spaces ("Playback Mux")
split into two arguments and the call failed; and the boot-time jack
probe read a legacy /sys/class/gpio node that mainline kernels don't
expose, now guarded with a speaker-path default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Douglas Teles
2026-07-07 16:18:39 -03:00
parent 4385a37cbf
commit df92325273
3 changed files with 43 additions and 27 deletions
@@ -5,8 +5,7 @@
# Minimal per-device config: wire up the temperature sensors so System
# Information (archr-info) and EmulationStation show CPU/GPU temps. The
# quirks loader matches the folder name against /proc/device-tree/model
# exactly, so this folder name MUST equal the DTS `model` string. Audio
# routing and LED config are intentionally left to the working defaults.
# exactly, so this folder name MUST equal the DTS `model` string.
cat <<CONF >/storage/.config/profile.d/001-device_config
# LED nodes use color+function (no label), so sysfs is blue:power.
# Surfaces the DISABLE POWER LED switch in ES (the var is already in
@@ -14,6 +13,13 @@ cat <<CONF >/storage/.config/profile.d/001-device_config
# invisible on every device).
DEVICE_PWR_LED_CONTROL="true"
DEVICE_POWER_LED="blue:power"
# V20-era clone boards route the speaker through the external amp like
# the Soysauce (the eeclone DTS ships the rk817-sound-amplified card),
# so the playback mux and the jack input device must be wired up or the
# speaker stays silent (Arch-R#35, config validated by reporters).
DEVICE_PLAYBACK_PATH_SPK="SPK"
DEVICE_PLAYBACK_PATH_HP="HP"
DEVICE_HEADPHONE_DEV="/dev/input/by-path/platform-rk817-sound-amplified-event"
DEVICE_TEMP_SENSOR="/sys/devices/virtual/thermal/thermal_zone0/temp"
DEVICE_GPU_TEMP_SENSOR="/sys/devices/virtual/thermal/thermal_zone1/temp"
CONF
@@ -1,11 +1,22 @@
# Force the out-of-tree r8188eu driver for RTL8188EUS dongles, instead
# of the in-tree rtl8xxxu (which scans but can't complete WPA2 4-way
# handshake on this chip see archr-linux/Arch-R#19).
# handshake on this chip, see archr-linux/Arch-R#19).
#
# rtl8xxxu is loaded for many other RTL chips and we don't want to
# blacklist it globally. Instead, this rule catches the 8188EUS USB
# IDs at hotplug time, unbinds rtl8xxxu from the interface, and
# (re)loads 8188eu.
# IDs and moves the network interface over to 8188eu.
#
# The first shipped version of this rule matched the usb_device event
# and echoed the device name (e.g. "1-1") into rtl8xxxu's unbind node.
# That never worked: USB drivers bind to the *interface* ("1-1:1.0"),
# and at usb_device add time no driver is bound yet anyway, while a
# plain modprobe never rebinds an interface another driver already
# claimed. Match the usb_interface event instead: by the time RUN
# fires the kernel has finished its synchronous bind, so pulling the
# interface off rtl8xxxu (a no-op when it lost the probe race) and
# writing it into 8188eu's bind node is deterministic. %k is the
# interface name, substituted by udev itself, so no shell expansion
# or `$$` escaping is involved.
#
# Known RTL8188EUS USB IDs (vendor:product):
# 0bda:0179 Realtek RTL8188EUS Wireless Adapter
@@ -16,25 +27,17 @@
ACTION!="add", GOTO="rtl8188eus_end"
SUBSYSTEM!="usb", GOTO="rtl8188eus_end"
ENV{DEVTYPE}!="usb_device", GOTO="rtl8188eus_end"
ENV{DEVTYPE}!="usb_interface", GOTO="rtl8188eus_end"
ATTR{idVendor}=="0bda", ATTR{idProduct}=="0179", GOTO="rtl8188eus_swap"
ATTR{idVendor}=="0bda", ATTR{idProduct}=="8179", GOTO="rtl8188eus_swap"
ATTR{idVendor}=="0bda", ATTR{idProduct}=="8189", GOTO="rtl8188eus_swap"
ATTR{idVendor}=="7392", ATTR{idProduct}=="7811", GOTO="rtl8188eus_swap"
ATTR{idVendor}=="056e", ATTR{idProduct}=="4008", GOTO="rtl8188eus_swap"
# ATTRS{} walks up to the parent usb_device for the vendor/product IDs.
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="0179", GOTO="rtl8188eus_swap"
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="8179", GOTO="rtl8188eus_swap"
ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="8189", GOTO="rtl8188eus_swap"
ATTRS{idVendor}=="7392", ATTRS{idProduct}=="7811", GOTO="rtl8188eus_swap"
ATTRS{idVendor}=="056e", ATTRS{idProduct}=="4008", GOTO="rtl8188eus_swap"
GOTO="rtl8188eus_end"
LABEL="rtl8188eus_swap"
# Unbind apenas a interface USB específica do rtl8xxxu (não tira o
# módulo do kernel — outros dongles RTL podem estar conectados ao
# mesmo tempo); depois carrega 8188eu, que vai bindar pela alias
# table. ${BUSNUM} e ${DEVNUM} são fornecidos por udev e identificam
# o device USB sem ambiguidade.
# udev parses `$` in RUN as its own specifier prefix, so shell variables
# must be doubled (`$$` -> literal `$`). The previous single-`$` form made
# udev reject the whole rule ("invalid substitution type"), so the rebind
# never ran and the dongle was left on rtl8xxxu by luck of probe order.
RUN+="/usr/bin/sh -c 'busid=$$(basename %p); for drv in /sys/bus/usb/drivers/rtl8xxxu; do [ -e \"$$drv/$$busid\" ] && echo \"$$busid\" > \"$$drv/unbind\" 2>/dev/null; done; modprobe 8188eu 2>/dev/null'"
RUN+="/usr/bin/sh -c 'modprobe 8188eu; echo %k > /sys/bus/usb/drivers/rtl8xxxu/unbind 2>/dev/null; echo %k > /sys/bus/usb/drivers/8188eu/bind 2>/dev/null; exit 0'"
LABEL="rtl8188eus_end"
@@ -11,8 +11,15 @@ if [[ ${AUDIO_MANAGEMENT} == "UCM" ]]; then
exit 0
fi
# Switch to headphones if we have them already connected at boot
GPIO=$(cat /sys/class/gpio/gpio${DEVICE_JACK}/value)
# Switch to headphones if we have them already connected at boot.
# DEVICE_JACK is a legacy gpio sysfs number; mainline kernels don't
# expose /sys/class/gpio, so only trust it when the node really exists
# and otherwise start from the speaker path.
if [ -n "${DEVICE_JACK}" ] && [ -e "/sys/class/gpio/gpio${DEVICE_JACK}/value" ]; then
GPIO=$(cat /sys/class/gpio/gpio${DEVICE_JACK}/value)
else
GPIO=1
fi
[[ "$GPIO" == "0" ]] && set_setting "audio.device" "headphone" || set_setting "audio.device" "speakers"
### Set the default audio path, needed for some devices
@@ -24,10 +31,10 @@ fi
STARTUP_DEVICE=$(get_setting "audio.device")
case "${2}" in
"headphone")
amixer -c 0 -M cset name=${DEVICE_PLAYBACK_PATH} ${DEVICE_PLAYBACK_PATH_HP}
amixer -c 0 -M cset "name=${DEVICE_PLAYBACK_PATH}" "${DEVICE_PLAYBACK_PATH_HP}"
;;
"auto"|"speakers"|*)
amixer -c 0 -M cset name=${DEVICE_PLAYBACK_PATH} ${DEVICE_PLAYBACK_PATH_SPK}
amixer -c 0 -M cset "name=${DEVICE_PLAYBACK_PATH}" "${DEVICE_PLAYBACK_PATH_SPK}"
;;
esac
@@ -42,11 +49,11 @@ HP_OFF='*(SW_HEADPHONE_INSERT), value 1*'
evtest "${DEVICE}" | while read line; do
case $line in
(${HP_ON})
amixer -c 0 cset name=${DEVICE_PLAYBACK_PATH} ${DEVICE_PLAYBACK_PATH_HP}
amixer -c 0 cset "name=${DEVICE_PLAYBACK_PATH}" "${DEVICE_PLAYBACK_PATH_HP}"
set_setting "audio.device" "headphone"
;;
(${HP_OFF})
amixer -c 0 cset name=${DEVICE_PLAYBACK_PATH} ${DEVICE_PLAYBACK_PATH_SPK}
amixer -c 0 cset "name=${DEVICE_PLAYBACK_PATH}" "${DEVICE_PLAYBACK_PATH_SPK}"
set_setting "audio.device" "speakers"
;;
esac