173 lines
4.1 KiB
Bash
173 lines
4.1 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Physical USB ports on the RDKx5 carrier.
|
|
RADAR_PORT="1-1.4"
|
|
IMU_PORT="1-1.1"
|
|
|
|
# RADAR -> usb_ch343, IMU -> cdc_acm.
|
|
RADAR_DRIVER="usb_ch343"
|
|
IMU_DRIVER="cdc_acm"
|
|
|
|
DRIVER_ROOT="/sys/bus/usb/drivers"
|
|
MAX_WAIT_SECONDS="${MAX_WAIT_SECONDS:-30}"
|
|
WAIT_INTERVAL_SECONDS="${WAIT_INTERVAL_SECONDS:-1}"
|
|
INTERFACES=()
|
|
|
|
log() {
|
|
local message="[radar-driver-switch] $*"
|
|
if [ -w /dev/kmsg ]; then
|
|
printf '%s\n' "$message" > /dev/kmsg
|
|
else
|
|
printf '%s\n' "$message"
|
|
fi
|
|
}
|
|
|
|
load_driver() {
|
|
local driver="$1"
|
|
case "$driver" in
|
|
cdc_acm)
|
|
modprobe cdc_acm 2>/dev/null || true
|
|
;;
|
|
usb_ch343)
|
|
modprobe ch343 2>/dev/null || true
|
|
;;
|
|
esac
|
|
}
|
|
|
|
driver_path() {
|
|
printf '%s/%s\n' "$DRIVER_ROOT" "$1"
|
|
}
|
|
|
|
require_driver() {
|
|
local driver="$1"
|
|
local path
|
|
path="$(driver_path "$driver")"
|
|
if [ ! -d "$path" ]; then
|
|
log "error: driver path missing: $path"
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
collect_interfaces() {
|
|
local port="$1"
|
|
shopt -s nullglob
|
|
INTERFACES=(/sys/bus/usb/devices/"${port}":*)
|
|
shopt -u nullglob
|
|
[ "${#INTERFACES[@]}" -gt 0 ]
|
|
}
|
|
|
|
wait_for_interfaces() {
|
|
local label="$1"
|
|
local port="$2"
|
|
local attempt=0
|
|
while [ "$attempt" -le "$MAX_WAIT_SECONDS" ]; do
|
|
if collect_interfaces "$port"; then
|
|
return 0
|
|
fi
|
|
log "waiting for $label port $port interfaces (${attempt}s)"
|
|
sleep "$WAIT_INTERVAL_SECONDS"
|
|
attempt=$((attempt + WAIT_INTERVAL_SECONDS))
|
|
done
|
|
log "error: timed out waiting for $label port $port interfaces"
|
|
exit 1
|
|
}
|
|
|
|
current_driver() {
|
|
local iface="$1"
|
|
local target
|
|
target="$(readlink -f "$iface/driver" 2>/dev/null || true)"
|
|
if [ -n "$target" ]; then
|
|
basename "$target"
|
|
fi
|
|
}
|
|
|
|
unbind_from_current_driver() {
|
|
local iface="$1"
|
|
local iface_name
|
|
local driver
|
|
iface_name="$(basename "$iface")"
|
|
driver="$(current_driver "$iface")"
|
|
if [ -z "$driver" ]; then
|
|
return 0
|
|
fi
|
|
if [ ! -w "$(driver_path "$driver")/unbind" ]; then
|
|
log "error: cannot unbind $iface_name from $driver"
|
|
exit 5
|
|
fi
|
|
printf '%s\n' "$iface_name" > "$(driver_path "$driver")/unbind"
|
|
log "unbound $iface_name from $driver"
|
|
}
|
|
|
|
bind_to_driver() {
|
|
local iface="$1"
|
|
local driver="$2"
|
|
local iface_name
|
|
local bind_path
|
|
iface_name="$(basename "$iface")"
|
|
bind_path="$(driver_path "$driver")/bind"
|
|
if [ ! -w "$bind_path" ]; then
|
|
log "error: cannot bind $iface_name to $driver"
|
|
exit 6
|
|
fi
|
|
if printf '%s\n' "$iface_name" > "$bind_path" 2>/dev/null; then
|
|
log "bound $iface_name to $driver"
|
|
else
|
|
log "warning: $iface_name did not bind to $driver"
|
|
fi
|
|
}
|
|
|
|
ensure_port_bound() {
|
|
local label="$1"
|
|
local port="$2"
|
|
local desired_driver="$3"
|
|
local iface
|
|
local iface_name
|
|
local driver
|
|
local bound_count=0
|
|
|
|
wait_for_interfaces "$label" "$port"
|
|
|
|
for iface in "${INTERFACES[@]}"; do
|
|
iface_name="$(basename "$iface")"
|
|
driver="$(current_driver "$iface")"
|
|
if [ "$driver" = "$desired_driver" ]; then
|
|
log "$label $iface_name already uses $desired_driver"
|
|
bound_count=$((bound_count + 1))
|
|
continue
|
|
fi
|
|
|
|
if [ -n "$driver" ]; then
|
|
unbind_from_current_driver "$iface"
|
|
sleep 0.2
|
|
fi
|
|
|
|
bind_to_driver "$iface" "$desired_driver"
|
|
sleep 0.2
|
|
driver="$(current_driver "$iface")"
|
|
if [ "$driver" = "$desired_driver" ]; then
|
|
bound_count=$((bound_count + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "$bound_count" -eq 0 ]; then
|
|
log "error: no $label interface on $port is bound to $desired_driver"
|
|
exit 7
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
load_driver "$RADAR_DRIVER"
|
|
load_driver "$IMU_DRIVER"
|
|
require_driver "$RADAR_DRIVER"
|
|
require_driver "$IMU_DRIVER"
|
|
|
|
ensure_port_bound "RADAR" "$RADAR_PORT" "$RADAR_DRIVER"
|
|
ensure_port_bound "IMU" "$IMU_PORT" "$IMU_DRIVER"
|
|
|
|
udevadm settle --timeout=5 2>/dev/null || true
|
|
log "done; serial devices: $(ls /dev/ttyACM* /dev/ttyCH343USB* 2>/dev/null | tr '\n' ' ')"
|
|
}
|
|
|
|
main "$@"
|