Turris Omnia: USB-Devices per Hotplug-Daemon einrichten
Wenn ich an meinem Turris Omnia USB-Geräte anschließe, so werden bei mir entsprechende Devices (z.B. /dev/ttyACM*
) mit einer fortlaufenden Nummer angelegt. Soweit so gut, nur leider können die per hotplug Serial Devices nicht wie bei udev über eine die ID (/dev/serial/by-id/*
) angesprochen werden. Dies kann dann zu einem Problem werden, wenn mehrere serielle ACM-Geräte gleichzeitig verwendet werden. Dann lässt sich oft nur schwer rausfinden, welches Gerät hinter welcher Nummer steckt. Erst recht, wenn sich die Reihenfolge z.B. beim Booten ändert oder die Sticks in einen anderen Port eingesteckt werden.
Aber der hotplug-Daemon bietet (ähnlich wie udev) die Möglichkeit Scripte beim Anschließen von USB-Devices automatisch auszuführen. Dass kann z.B. genutzt werden um Berechtigungen für Devices anzupdassen, symbolische Links anzulegen oder auch Devices umzubennen.
Product-ID ermitteln
Die Scripte die beim Anschließen eines USB-Gerätes nacheinander ausgeführt werden sollen, müssen im Verzeichnis /etc/hotplug.d/usb
abgelegt werden. Um an die nötigen Informationen über USB-Devices zu bekommen, wird in diesem Verzeichnis zunächst ein Script angelegt, dass nlediglich ein paar Informationen zum angeschlossenen Device ausgibt:
cat <<\EOF > /etc/hotplug.d/usb/99-debug
if [ "${ACTION}" = "add" ]; then
DEVICE_NAME=$(find /sys/${DEVPATH} -name tty* | grep -Eo "tty[^/]+$" | tail -1)
logger -t hotplug "99-debug: Product: ${PRODUCT} / Device: ${DEVICE_NAME} / DevName: ${DEVICENAME}"
echo >> /tmp/envs_hotplug.log
date >> /tmp/envs_hotplug.log
env >> /tmp/envs_hotplug.log
fi
EOF
Dieses Script wird immer dann ausgeführt, wenn ein USB-Device anggeschlossen wird (siehe in Zeile 2). Dann versucht das Scipt den eigentlichen Device-Namen (/dev/tty*
) zu ermitteln. Für einige serielle Geräte funktioniert das auch bereits und es wird bereits z.B. ein /dev/ttyUSB0
ermittelt (siehe Zeile 3). In Zeile 4 werden die für mich relevanten Daten per dem Befehl logger
in /var/log/messages
ausgegeben. Die Umgebungsvariablen $PRODUCT
und $DEVICENAME
werden von hotplug automatisch gesetzt. Die Product-ID aus $PRODUCT
ist später relevant um gerätespezifische Aktionen durchführen zu können. In $DEVICENAME
wird von hotplug der numerische Device-Name (z.B. 2-1.3:1.0
) gespeichert. Dieser wird zwar in der Regel nicht zum ansprechen des Geräte verwendet, kann aber helfen, den eigentlichen Device-Namen des Gerätes im Verzeichnis /dev
zu ermitteln.
In den Zeilen 5 bis 7 wird zu guter letzt noch eine detailierte Übersicht der hotplug-Umgebungsvariablen in die Datei /tmp/envs_hotplug.log
geschrieben.
Wird jetzt ein USB-Gerät angeschlossen (in diesem Beispiel ein Aeotec Z-STick Gen5), so solten in /var/log/messages
entsprechende Einträge auftauchen:
grep hotplug /var/log/messages
Apr 15 13:55:53 turris hotplug: 99-debug: Product: 658/200/0 / Device: / DevName: 2-1.3
Apr 15 13:55:53 turris hotplug: 99-debug: Product: 658/200/0 / Device: tty / DevName: 2-1.3:1.0
Apr 15 13:55:53 turris hotplug: 99-debug: Product: 658/200/0 / Device: / DevName: 2-1.3:1.1
Außerdem sollten die Datei /tmp/envs_hotplug.log
mit den Detailinformationen angelegt worden sein.
Device-Namen ermitteln
Bereits im oben erstellten Debug-Script wird versucht das Linux-Device für das angeschlossene serielle USB-Gerät zu ermitteln. Während das für viele USB-Devices gut funktioniert (z.B. für den ConBee-Stick für deCONZ) klappt das leider nicht so einfach für ein cdc_acm Gerät. Wie man in /var/log/messages sieht, erscheint hier lediglich tty. Der vollständige Devicename (z.B. ttyACM0
) wird aber in den Kernellogs angezeigt, wenn das Gerät angeschlossen wird. Mit dmesg | grep USB
kann die Information abgefragt werden (siehe Zeile 2):
[67383.153902] usb 2-1.3: new full-speed USB device number 27 using xhci-hcd
[67383.284910] cdc_acm 2-1.3:1.0: ttyACM0: USB ACM device
Zusammen mit den Informationan aus dem Script 99-debug
sind jetzt alle Informationen für das Script zur Anpassung der Berechtigungen und zur Erstellung eines symbolischen Links vorhanden.
Beispiel-Script für Aeotec Z-Stick Gen5
Das Script hat nur wenige Zeilen und ist schnell erstellt:
cat <<\EOF > /etc/hotplug.d/usb/20-zwave
ZWAVE_PRODID="658/200/0"
SYMLINK="zwave"
if [ "${PRODUCT}" = "${ZWAVE_PRODID}" ]; then
if [ "${ACTION}" = "add" ]; then
DEVICE_NAME=$(find /sys/${DEVPATH}/tty/ -name ttyACM* | grep -Eo "ttyACM.*$")
if [ -z ${DEVICE_NAME} ]; then
logger -t hotplug "20-zwave: Info: no tty device created - exiting"
exit
fi
logger -t hotplug "20-zwave: determined ${DEVICE_NAME} as device name for Aeotec Z-Stick "
/bin/chown root:plugdev /dev/${DEVICE_NAME}
/bin/chmod 664 /dev/${DEVICE_NAME}
logger -t hotplug "20-zwave: Changed access rights for /dev/${DEVICE_NAME}"
ln -s /dev/${DEVICE_NAME} /dev/${SYMLINK}
logger -t hotplug "20-zwave: Symlink /dev/${SYMLINK} -> /dev/${DEVICE_NAME} created"
fi
if [ "${ACTION}" = "remove" ]; then
rm /dev/${SYMLINK}
logger -t hotplug "20-zwave: Symlink /dev/${SYMLINK} removed"
fi
fi
EOF
In Zeile 2 wird zunächst die Produkt-ID des USB-Gerätes eingetragen. Die ID kann aus den Informationen des 99-debug
Scriptes in /var/log/messages
übernommen werden. In Zeile 5 wird sichergestellt, dass das Script nur dann ausgeführt wird, wenn ein USB-Gerät mit der entsprechenden Produkt-ID angeschlossen wird. Der folgende Block wird nur ausgeführt, wenn das USB-Device angeschlossen wird (Zeile 6).
Die nächsten Zeilen sind nicht so selbsterklärend. Schaut man sich die Ausgaben des 99-debug
Scriptes genauer an, wo wird man festellen, dass das einstecken des Usbgerätes dazu führt, dass das Debug-Script drei mal laufgerufen wird. Dabei wird aber nur bei einem Aufruf das Device /dev/ttyACM*
angelegt. Daher wird auf Basis der hotplug-Umgebungsvariable $DEVPATH
ermittelt, ob Device bereits angelegt wurde. Falls nicht, kann das Script beendet werden. Zur Kontrolle wird das ganze in /var/log/messages
protokolliert (Zeilen 7 - 12).
Anschließend werden die Berechtigungen für das zuvor ermittelte Device angepasst und der symbolische Link angelegt dessen Name in Zeile 3 definiert wurde (siehe 14 - 19)
Zeilen 22ff sorgen dafür, dass der symbolische Link wieder gelöscht wird, wenn das USB-Gerät wieder entfernt wird.
Initialisieren beim Booten
Wird der Z-Stick angeschlossen, so sollten das Script von Hotplug ausgeführt werden und die Berechtigungen automatisch angepasst werden. Wird der Turris Omnia allerdins neu gestartet, so werden die Anpassungen aus dem Script leider nicht durchgeführt. Scheinbar wird beim Einbinde der Device während des Boot-Vorgangs der Hotplug-Mechanismus nicht verwendet. Werden aber zuvor in einem Script die richtigen Umgebungsvariablen gestzt, kann das Hotplug-Scripte auch einmal nach einem erfolgreichen Reboot ausgeführt werden:
cat <<\EOF > /root/usbinit.sh
#!/bin/bash
ID_ZWAVE="658/200/0"
# Devices werden beim Booten quasi eingesteckt...
ACTION="add"
# Init cdc_acm devices
for i in $(dmesg | grep -Eo "cdc_acm.*ttyACM.*$" | cut -d " " -f 2); do
DEVICENAME=${i%:}
DEVPATH="bus/usb/devices/${DEVICENAME}"
PRODUCT=$(grep PRODUCT /sys/bus/usb/devices/${DEVICENAME}/uevent | cut -d "=" -f 2)
case "${PRODUCT}" in
${ID_ZWAVE})
. /etc/hotplug.d/usb/20-zwave
;;
esac
done
EOF
Mit folgendem Eintrag in die Datei /etc/rc.local
werden dann die Berechtigungen auch bei einem Neustart korrekt gesetzt:
# USB-Devices Workaround: Init USB-Devices
###########################################
# Da beim booten beim Einrichten der USB-Devices nicht der hotplug-Mechanismus
# genutzt wird, werden die Devices beim initialen Booten nicht korrekt initialisiert.
# Daher müssen die Berechtigungen und die fehlenden symbolischen Links nach dem
# Booten per Script gesetzt werden
. /root/bin/usbinit.sh
Hinweis: wird am Ende in der rc.local Datei der Befehl exit aufgerufen, so ist muss der Aufruf des Init-Scripts unbedingt davor eingetragen werden, damit das Script ausgeführt wird.
Fazit
Auch wenn hotplug
aus meiner Sicht etwas umständlicher ist als udev
, so kann man mit einem kleinen Script dann doch noch zum gewünschten Ergebins kommen. Da mit dem Script auch die Berechtigungen für das Device angepasst werden, ist es die ideale Vorraussetzung zur Verwendung der Devices in LXC-Containern.
Das hier beschriebene Script ist eine gute Ausgangsbasis für eigene Experimente mit dem hotplug-Daemon. Wer tiefer in die Hotplug-Implementierung auf OpenWRT einsteigen möchte, der sollte sich auch den in den Quellen genannten Link anschauen.
Kommentare