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 $DEVICENAMEwird 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.

Quellen

[1] https://openwrt.org/docs/guide-user/base-system/hotplug

Kommentare

PostadresseE-MailadresseFestnetzMobiltelefonSMS/SignalThreemaTwitter DirektnachrichtFAXWeb Page