01signal.com

Ein USB-Gerät unter Linux zurücksetzen (und vielleicht seine Stromversorgung steuern)

Einführung

In einer perfekten Welt benehmen sich USB-Geräte und USB-Hubs vorbildlich und schaffen es, jedes auftretende Problem selbst zu beheben. In der Realität passieren jedoch alle möglichen seltsamen Dinge, und man muss etwas dagegen unternehmen. Die übliche Lösung ist, den USB-Stecker herauszuziehen und das Gerät wieder anzuschließen. Aber was, wenn das automatisch passieren muss?

Die radikalste Lösung wird auf einer anderen Webseite vorgestellt: Sie fährt die Treiber des USB-Hubs herunter und startet sie neu. Der Effekt entspricht fast dem Abziehen und Wiederanschließen aller USB-Geräte am Computer. Das ist der schwerste Hammer – und manchmal notwendig.

Diese Seite stellt zwei Methoden vor, um ein bestimmtes USB-Gerät zurückzusetzen. Ich zeige zunächst, wie man jede Methode ausführt, mit ein paar kurzen Erläuterungen. Danach gehe ich auf viele technische Details ein.

Die gesamte Betrachtung beschränkt sich hier auf Linux, obwohl die zweite vorgestellte Methode wahrscheinlich auch auf anderen Plattformen möglich ist.

Viele Informationen hier stammen aus der Universal Serial Bus Specification Revision 2.0. Die Datei heißt üblicherweise usb_20.pdf.

Methode Nr. 1: Den Linux-Kernel bitten, das Gerät zurückzusetzen

Für diesen Zweck gibt es mehrere Werkzeuge. Das ausgefeilteste ist Teil von usbutils. Laden Sie usbreset.c herunter und übersetzen Sie es mit gcc:

$ gcc -O3 -Wall -g usbreset.c -o usbreset

Und dann:

$ ./usbreset
Usage:
  usbreset PPPP:VVVV - reset by product and vendor id
  usbreset BBB/DDD   - reset by bus and device number
  usbreset "Product" - reset by product name

Devices:
  Number 001/004  ID 045e:07b2  Microsoft® Nano Transceiver v1.0
  Number 001/002  ID 04f3:0103
$ ./usbreset 001/004
Resetting Microsoft® Nano Transceiver v1.0 ... can't open [Permission denied]
$ sudo ./usbreset 001/004
Resetting Microsoft® Nano Transceiver v1.0 ... ok
$ sudo ./usbreset 045e:07b2
Resetting Microsoft® Nano Transceiver v1.0 ... ok

Als Reaktion auf die Resets erscheint Folgendes im Kernel-Log:

usb 1-6: reset full-speed USB device number 4 using xhci_hcd

Was diese Sitzung zeigt:

Was tut usbreset also? Es läuft im Endeffekt auf diesen Befehl auf der Gerätedatei des USB-Geräts hinaus:

ioctl(fd, USBDEVFS_RESET, 0)

Das führt zu einem Funktionsaufruf an usb_reset_device() des Kernels, das viel mehr tut, als das Gerät nur zurückzusetzen. Die Idee ist, den gesamten Ablauf reibungslos zu gestalten: Die Funktion benachrichtigt den Treiber des Geräts vor und nach dem Reset, sofern dies gewünscht ist. Sie löst den Treiber vor dem Reset und bindet ihn danach wieder an. Die Konfiguration des Geräts wird nach dem Reset ebenfalls geladen. Ohne das wüsste das Gerät seine Busadresse nicht und wäre für keinen Datenaustausch bereit.

Es ist also fast wie ein erneutes Anschließen des Geräts, aber ohne dass es sich ausweisen muss (weil die Informationen bereits bekannt sind) und ohne dass ihm eine neue Adresse zugewiesen wird.

Bei USB 3.x handelt es sich dabei um einen Hot Reset – die weniger effiziente Variante. Weiter unten mehr zum Hot Reset.

Methode Nr. 2: Den Stromzustand des USB-Ports umschalten

Für diesen Zweck gibt es ebenfalls mehrere Werkzeuge. Ich zeige die Methode anhand von hubpower. Laden Sie hubpower.c herunter und übersetzen Sie es:

$ gcc -O3 -Wall -g hubpower.c -o hubpower

Die Verwendung dieses Werkzeugs ist etwas kniffliger, denn die Befehle werden nicht an das USB-Gerät selbst geschickt. Stattdessen gehen die Anforderungen an den USB-Hub, an den das USB-Gerät angeschlossen ist.

Der erste Schritt ist also herauszufinden, um welchen Hub es sich handelt. Zuerst ermitteln wir die Adresse des USB-Geräts:

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 045e:07b2 Microsoft Corp.
Bus 001 Device 002: ID 04f3:0103 Elan Microelectronics Corp. ActiveJet K-2024 Multimedia Keyboard
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Das Gerät befindet sich also auf Bus Nummer 1 und hat die Gerätenummer 4. Beachten Sie, dass sich die Gerätenummer bei jeder Enumeration des USB-Geräts ändert.

An welchen Hub ist das USB-Gerät also angeschlossen? Und an welchen Port?

$ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 6: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 6: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 6: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 11: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 11: Dev 2, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M

Gerät Nummer 4 ist also an Port 6 angeschlossen. Der Hub hat die Busnummer 1 und die Gerätenummer 1 (es ist der USB-Controller des Mainboards, der als Root-Hub fungiert).

Schauen wir uns an, was hubpower dazu sagt:

$ sudo ./hubpower 1:1 status
Port  1 status: 0100  Power-On
Port  2 status: 0100  Power-On
Port  3 status: 0100  Power-On
Port  4 status: 0100  Power-On
Port  5 status: 0100  Power-On
Port  6 status: 0103  Power-On Enabled Connected
Port  7 status: 0100  Power-On
Port  8 status: 0100  Power-On
Port  9 status: 0100  Power-On
Port 10 status: 0100  Power-On
Port 11 status: 0303  Low-Speed Power-On Enabled Connected
Port 12 status: 0100  Power-On

Beachten Sie, dass der Teil »1:1« die Busadresse des Hubs ist. Wenn es sich um einen externen Hub handelt, ändert sich diese Adresse jedes Mal, wenn der Hub an den Computer angeschlossen wird.

Die Nummern der Ports ändern sich dagegen nie (solange das Gerät am selben physischen Port angeschlossen ist).

Nun, da wir die Portnummer des Geräts kennen, setzen wir es zurück:

$ sudo ./hubpower 1:1 power 6 off
Port  6 status: 0000  Power-Off
$ sudo ./hubpower 1:1 power 6 on
Port  6 status: 0100  Power-On

Nach dem ersten Befehl wird das Gerät im Kernel-Log als getrennt gemeldet:

usb 1-6: USB disconnect, device number 4

Nach dem zweiten Befehl verhält sich der Computer so, als wäre das Gerät physisch angeschlossen worden:

usb 1-6: new full-speed USB device number 5 using xhci_hcd
usb 1-6: New USB device found, idVendor=045e, idProduct=07b2, bcd Device= 7.04
usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0
usb 1-6: Product: Microsoft® Nano Transceiver v1.0
usb 1-6: Manufacturer: Microsoft
[ ... ]

Wegen dieser erneuten Enumeration wird dem Gerät eine neue Busadresse zugewiesen:

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 045e:07b2 Microsoft Corp.
Bus 001 Device 002: ID 04f3:0103 Elan Microelectronics Corp. ActiveJet K-2024 Multimedia Keyboard
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Handelt es sich also um ein physisches Abziehen des USB-Geräts und ein erneutes Anschließen? Die Antwort lautet: vielleicht. In den meisten Fällen schalten die hubpower-Befehle die Stromversorgung des USB-Geräts nicht wirklich ab. Das Gerät erhält also weiterhin seine VBUS-Spannung (5 V). Nur sehr wenige USB-Hubs schalten die Spannung tatsächlich ab. Mehr dazu weiter unten.

Bei der überwiegenden Mehrheit der Hubs versetzt der erste Befehl (»power off«) den Port nur in einen Zustand, in dem er das Gerät ignoriert. Der zweite Befehl (»power on«) bringt den Port in seinen natürlichen Zustand zurück. Das Ergebnis ist, dass das Gerät erkannt wird und seine Initialisierungsprozedur beginnt. Unter anderem wird das Gerät zurückgesetzt und enumeriert, und sein Treiber initialisiert es.

Ein physisches Abtrennen ist daher meist wirkungsvoller als diese Befehle, insbesondere wenn das Gerät seine Stromversorgung über den USB-Stecker bezieht. Wenn der Hub die VBUS-Spannung aber wirklich abschaltet, ist hubpower genauso wirkungsvoll.

Beachten Sie, dass die Befehle in diesem Beispiel an den USB-Controller des Mainboards (den Root-Hub) geschickt wurden. Der Grund ist, dass das USB-Gerät direkt am Computer angeschlossen war. Ist das Gerät über einen externen Hub an den Computer angeschlossen, sollten die Befehle an diesen externen Hub geschickt werden. In beiden Fällen wird hubpower auf dieselbe Weise verwendet.

hubpower unterstützt USB 3.0 (SuperSpeed) leider nicht. Mehr dazu weiter unten.

Unterschiede zwischen den beiden Methoden

Worin unterscheidet sich die zweite Methode (mit hubpower) nun von der ersten (mit usbreset)? Zur Lösung eines Problems mit einem Gerät tun beide Methoden im Wesentlichen dasselbe: Sie senden einen Reset-Befehl. Die Art und Weise, wie das geschieht, ist grundlegend verschieden, aber es ist immer noch dieser Reset-Befehl, der das Problem wahrscheinlich löst – wenn überhaupt einer es löst.

Es gibt jedoch einige wichtige Unterschiede:

Elektrische Geräte steuern (?)

Auch wenn das Hauptthema dieser Seite die Behebung von USB-Problemen ist, gibt es auch einen interessanten Nebeneffekt: Es ist manchmal möglich, die 5-V-Stromversorgung des USB-Ports zu steuern. Mit anderen Worten: Ein einfacher und billiger USB-Hub lässt sich dazu verwenden, eine Stromversorgung mit einer Belastbarkeit von 2,5 Watt ein- und auszuschalten.

Das ist mehr als genug, um ein elektromechanisches Relais anzusteuern. Es muss also nur eine Komponente hinzugefügt werden, um ein elektrisches Gerät zu schalten, das mit 110 V / 220 V betrieben wird. Es ist allerdings ratsam, eine einfache Freilaufdiode einzubauen, um den USB-Hub vor Schäden zu schützen. Und das war es auch schon.

Leider ist die Fähigkeit zur Steuerung der Stromversorgung optional: Laut Abschnitt 11.11 der USB-2.0-Spezifikation darf ein Hub über Schalter verfügen, die die 5-V-Stromversorgung eines Ports abschalten, wenn sich der Port im Powered-Off-Zustand befindet. Alternativ kann ein Stromversorgungsschalter auch die Stromversorgung mehrerer Ports steuern (»ganged power switching«). Der Zweck der Steuerung der Stromversorgung besteht in erster Linie darin, USB-Geräte abzuschalten, die zu viel Strom ziehen, damit die übrigen Ports normal weiterarbeiten können.

Wie bereits erwähnt, ist die physische Steuerung der Stromversorgung optional. Was hubpower tatsächlich tut, ist, den Wert des Attributs PORT_POWER des Ports zu ändern (in den USB-Spezifikationen wird das als »Feature« bezeichnet). Diese Änderung betrifft zwei verschiedene Aspekte des Hub-Ports:

PORT_POWER wird weiter unten genauer erklärt.

Steuert mein Hub die Spannung?

Woher wissen Sie, ob Ihr Hub die Spannung tatsächlich abschaltet? Der einzige Weg, es sicher herauszufinden, ist ein Test. Schließen Sie etwas an, das kein USB-Gerät ist, aber Strom aus dem USB-Port bezieht. Echte USB-Geräte können dabei verwirren. Eine optische Maus beispielsweise schaltet ihre LED normalerweise als Reaktion auf den Power-Off-Befehl aus, auch wenn die 5-V-Spannung weiterhin anliegt.

Jeder Hub gibt in den Informationen, die mit »lsusb -v« sichtbar sind (im Hub-Deskriptor, definiert in Abschnitt 11.23.2.1 der Spezifikation), an, ob und in welcher Abstufung er die Spannung steuert. Der Hub kann angeben, dass er die Spannung nicht steuert, dass er die Spannung für Portgruppen steuert (»ganged power switching«) oder dass er die Spannung jedes Ports einzeln steuert: Laut Abschnitt 11.11 der USB-2.0-Spezifikation kann ein Hub mit Stromversorgungsschaltern die Stromversorgung aller Ports als Gruppe (Gang), für jeden Port einzeln oder in einer beliebigen Zahl von Gruppen (Gangs) mit einem oder mehreren Ports schalten.

Diese Information ist jedoch nicht zuverlässig. Ich bin schon auf mehrere Hubs gestoßen, die angegeben haben, die Spannung zu steuern, aber keiner hat es getan.

Hier ist ein Beispiel für die Ausgabe von »lsusb -v«:

Bus 001 Device 073: ID 0bda:5411 Realtek Semiconductor Corp.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.10
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         2 TT per port
  bMaxPacketSize0        64
  idVendor           0x0bda Realtek Semiconductor Corp.
  idProduct          0x5411
  bcdDevice            1.23
  iManufacturer           1 Generic
  iProduct                2 4-Port USB 2.0 Hub
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:

[ ... ]

Hub Descriptor:
  bLength               9
  bDescriptorType      41
  nNbrPorts             4
  wHubCharacteristic 0x00a9
    Per-port power switching
    Per-port overcurrent protection
    TT think time 16 FS bits
    Port indicators
  bPwrOn2PwrGood        0 * 2 milli seconds
  bHubContrCurrent    100 milli Ampere
  DeviceRemovable    0x00
  PortPwrCtrlMask    0xff
 Hub Port Status:
   Port 1: 0000.0503 highspeed power enable connect
   Port 2: 0000.0503 highspeed power enable connect
   Port 3: 0000.0100 power
   Port 4: 0000.0100 power

Sieht vielversprechend aus, nicht wahr? In Wirklichkeit steuert dieser Hub die Spannungen überhaupt nicht.

Im Gegensatz dazu ein ganz gewöhnlicher Mainboard-Hub:

Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 Unused
  bDeviceProtocol         1 Single TT
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0002 2.0 root hub
  bcdDevice            4.15
  iManufacturer           3 Linux 4.15.0-20-generic xhci-hcd
  iProduct                2 xHCI Host Controller
  iSerial                 1 0000:06:00.0
  bNumConfigurations      1
  Configuration Descriptor:

[ ... ]

Hub Descriptor:
  bLength               9
  bDescriptorType      41
  nNbrPorts             2
  wHubCharacteristic 0x000a
    No power switching (usb 1.0)
    Per-port overcurrent protection
    TT think time 8 FS bits
  bPwrOn2PwrGood       10 * 2 milli seconds
  bHubContrCurrent      0 milli Ampere
  DeviceRemovable    0x00
  PortPwrCtrlMask    0xff
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
Device Status:     0x0001
  Self Powered

Dieser Hub gibt also offen zu, keine Spannungssteuerung zu unterstützen.

Beachten Sie, dass es neben den Angaben zur Stromversorgungsschaltung auch Statusinformationen zu jedem Port gibt, die als »Hub Port Status« ausgegeben werden. Diese Statusbits sind in Tabelle 11-21 der USB-2.0-Spezifikation (Abschnitt 11.24.2.7.1) definiert. Genau diese Informationen ruft hubpower ab.

Das Werkzeug uhubctl

Die Idee, mit einem USB-Hub ein Relais zu steuern, ist die Motivation für viele Initiativen. Ein Blick auf uhubctl lohnt sich, vor allem weil dieses Projekt eine Liste von USB-Hubs pflegt, die die Spannung steuern.

Dieses Werkzeug ist eindeutig nur für die Spannungssteuerung gedacht, nicht zur Lösung von Problemen mit einem USB-Gerät. Beispielsweise ignoriert uhubctl standardmäßig Hubs, die nicht angeben, die Spannungen für jeden Port einzeln steuern zu können.

Die folgenden Befehle entsprechen den obigen hubpower-Befehlen:

$ sudo ./uhubctl -f -l 1 -p 6 -a 0
Current status for hub 1 [1d6b:0002 Linux 5.16.0 xhci-hcd xHCI Host Controller 0000:00:14.0, USB 2.00, 12 ports, nops]
  Port 6: 0103 power enable connect [045e:07b2 Microsoft Microsoft? Nano Transceiver v1.0]
Sent power off request
New status for hub 1 [1d6b:0002 Linux 5.16.0 xhci-hcd xHCI Host Controller 0000:00:14.0, USB 2.00, 12 ports, nops]
  Port 6: 0000 off
$ sudo ./uhubctl -f -l 1 -p 6 -a 1
Current status for hub 1 [1d6b:0002 Linux 5.16.0 xhci-hcd xHCI Host Controller 0000:00:14.0, USB 2.00, 12 ports, nops]
  Port 6: 0000 off
Sent power on request
New status for hub 1 [1d6b:0002 Linux 5.16.0 xhci-hcd xHCI Host Controller 0000:00:14.0, USB 2.00, 12 ports, nops]
  Port 6: 0100 power

Die Befehlssyntax ist deutlich unbequemer. Sie wird weiter unten kurz erklärt.

uhubctl basiert auf libusb und funktioniert daher auch mit anderen Betriebssystemen. hubpower greift dagegen direkt auf die Gerätedatei des Hubs unter /dev/bus/usb/ (bzw. /proc/bus/usb/) zu, was nur unter Linux funktioniert.

uhubctl unterstützt zwar auch USB 3.x (siehe Git-Commit und seine Fortsetzung). Allerdings scheint sich niemand darum gekümmert zu haben, was passiert, wenn ein echtes USB-Gerät am Hub hängt: Meine Versuche, ein SuperSpeed-Gerät zurückzusetzen, führten zu seltsamen Ergebnissen: Nach dem Wiedereinschalten der Spannung verharrte das Gerät im Polling-Zustand und wurde nicht enumeriert. Außerdem blieb »lsusb -v« hängen, solange die Spannung abgeschaltet war. Da ist also einiges schiefgelaufen.

Das sind die Befehle, um an einem SuperSpeed-Port die Stromversorgung abzuschalten und wieder einzuschalten. Mehr zu SuperSpeed weiter unten.

# ./uhubctl -f -e -l 2 -p 4 -a 0
# ./uhubctl -f -e -l 2 -p 4 -a 1

PORT_POWER erklärt

Wenn vom Host ein Befehl eintrifft, PORT_POWER auf null zu ändern, geht der Port bedingungslos in den Powered-Off-Zustand über. Das gilt auch, wenn der Hub das Gerät weiterhin mit 5 V versorgt. Es gibt weitere Gründe, warum PORT_POWER null wird, insbesondere eine Überstrombedingung am Port (das Gerät zieht zu viel Strom).

PORT_POWER kann nur durch einen Befehl des Hosts wieder auf »1« gesetzt werden. Dadurch geht der Port in den Disconnected-Zustand über. USB-Ports, an denen nichts angeschlossen ist, befinden sich normalerweise in diesem Zustand. Wenn ein Gerät am Port erkannt wird, wechselt der Zustand nach einer kurzen Verzögerung zu Disabled. Ist das Gerät bereits angeschlossen und PORT_POWER wechselt auf »1«, geschieht das sofort.

Von diesem Zustand aus führt der einzige Weg zur Aktivierung des Geräts über das Zurücksetzen des Ports (mit PORT_RESET, siehe unten). Nur der Host kann das tun. Der Hub kann also nur den Host benachrichtigen, dass ein angeschlossenes Gerät seine Aufmerksamkeit benötigt.

Hier kommt eines der Statusbits des Ports ins Spiel: PORT_CONNECTION. Dieses Bit muss null sein, wenn sich der Port im Powered-Off- oder im Disconnected-Zustand befindet. PORT_CONNECTION wechselt auf »1«, wenn der Port vom Disconnected- in den Disabled-Zustand übergeht. Eine Änderung von PORT_CONNECTION erzeugt ein Hub-Ereignis, sodass der Treiber benachrichtigt wird (d. h. in hub.c des Kernels wird port_event() mit gesetztem USB_PORT_FEAT_C_CONNECTION aufgerufen). Der Treiber reagiert darauf, indem er den Port zurücksetzt (er setzt das PORT_RESET-Bit des Ports auf »1«) und das angeschlossene Gerät enumeriert.

Das alles bezieht sich auf USB 2.0. Abbildung 11-10 der USB-2.0-Spezifikation zeigt, wie ein Hub-Port seine Zustände wechselt.

Direkte Steuerung von PORT_RESET und PORT_ENABLE

hubpower kann auch zwei weitere Attribute direkt ändern: PORT_RESET und PORT_ENABLE. Ich habe diese Fähigkeit in meinem eigenen Fork des Werkzeugs ergänzt. Beachten Sie jedoch, dass man damit nur eines bewerkstelligen kann: Man kann das USB-Gerät absichtlich in einen Fehlerzustand bringen (und damit den Computer dazu bringen, das Gerät zur Korrektur zurückzusetzen). Nun ins Detail:

Um einen Port-Reset gemäß Abschnitt 11.5.1.5 der USB-2.0-Spezifikation einzuleiten, muss der Host PORT_RESET auf »1« setzen. Der Hub setzt PORT_RESET nach Abschluss des Resets wieder auf »0«. Der Hub beginnt einen Port-Reset niemals aus eigenem Antrieb, und der Host darf in dieses Attribut nicht »0« schreiben (Abschnitt 11.24.2.7.1.5).

Der Hub ignoriert PORT_RESET, wenn sich der Port im Powered-Off- oder im Disconnected-Zustand befindet.

Was PORT_ENABLE betrifft: Wenn dieses Attribut auf »0« wechselt, geht der Port in den Disabled-Zustand über. Dies kann aufgrund einer Anforderung des Hosts geschehen, aber auch durch das Abtrennen des USB-Geräts, einen Powered-Off-Zustand des Ports oder einen Fehler während des Reset-Vorgangs.

PORT_ENABLE kann nur als Ergebnis einer Port-Reset-Anforderung des Hosts auf »1« wechseln (Abschnitt 11.24.2.7.1.2 der USB-2.0-Spezifikation).

Der Host darf PORT_RESET daher nicht auf »0« oder PORT_ENABLE auf »1« ändern. Ein solcher Versuch führt zu einer Fehlerantwort des Hubs (der entsprechende ioctl()-Aufruf liefert einen Fehlerstatus zurück).

hubpower kann einen Reset direkt anfordern, indem es das PORT_RESET des Ports auf »1« setzt. Dadurch wird das USB-Gerät zurückgesetzt und verliert dabei seine Busadresse. Das Gerät ist danach nicht mehr erreichbar.

Dieser Befehl wird direkt an den Hub geschickt, sodass der Hub-Treiber im Linux-Kernel nichts davon erfährt. Tatsächlich bemerkt der Computer erst dann, dass sich etwas geändert hat, wenn er versucht, auf das USB-Gerät zuzugreifen. Was als Nächstes passiert, hängt vom Treiber des Geräts ab. Das Gerät wird so behandelt, als hätte es einen Hardware-Fehler. Entsprechend wird eine Maßnahme zur Fehlerkorrektur ergriffen. Höchstwahrscheinlich beinhaltet diese einen Reset.

Ein direkter Reset mit hubpower wird also wahrscheinlich das gewünschte Ergebnis erzielen, aber mit viel unnötigem Drama. PORT_POWER erledigt das eleganter. Der einzige mögliche Vorteil eines direkten PORT_RESET ist, dass der Treiber dann vielleicht sagt: »Hey, mit diesem Gerät stimmt wirklich etwas nicht, lass uns etwas Drastisches tun, um es zu beheben.« Und das könnte helfen.

Was die Manipulation von PORT_ENABLE betrifft, passiert dasselbe: Das Gerät verschwindet plötzlich. Doch auch in diesem Fall merkt der Computer nicht sofort, dass etwas passiert ist: Laut Abschnitt 11.24.2.7.2.2 der USB-2.0-Spezifikation wird eine Änderungsmeldung für PORT_ENABLE (d. h. C_PORT_ENABLE) nur dann ausgelöst, wenn der Port aufgrund eines Fehlers auf dem Link deaktiviert wird. Die Spezifikation sagt ausdrücklich, dass diese Meldung aus keinem anderen Grund erfolgt.

Das Ändern von PORT_ENABLE auf null hat also in etwa dieselbe Wirkung wie das direkte Ändern von PORT_RESET. Mit einem Nachteil: Laut Abschnitt 10.14.2.6.1 der USB-3.0-Spezifikation wird PORT_ENABLE »von SuperSpeed-Hubs nicht unterstützt«.

SuperSpeed (USB 3.x)

Zuallererst: Wenn Sie das hier lesen, weil Sie ein Problem mit einem USB-3.x-Gerät haben, fragen Sie sich, ob Sie die Datenrate, die USB 3.x bietet, wirklich benötigen. Ist die Antwort nein, versuchen Sie, das Gerät über einen USB-Hub ohne USB-3.x-Unterstützung (oder mit einem kurzen USB-2.0-Kabel) an den Computer anzuschließen. Das allein könnte das Problem lösen.

SuperSpeed-USB (also USB 3.x) existiert parallel zu USB 2.0. Jedes SuperSpeed-Gerät besteht praktisch aus zwei Geräten: einem separaten Gerät für SuperSpeed und einem weiteren separaten Gerät für USB 2.0. Jede der beiden USB-Versionen nutzt eigene Adern des USB-Kabels. Sie sind sowohl elektrisch als auch konzeptionell voneinander unabhängig.

Die USB-Spezifikationen verlangen, dass jedes SuperSpeed-Gerät aus diesen beiden Geräten besteht, obwohl das praktisch nicht nötig wäre. Mit anderen Worten: Ein SuperSpeed-Gerät ohne USB-2.0-Unterstützung funktioniert einwandfrei, wenn es an einen SuperSpeed-Port angeschlossen wird.

Wenn ein SuperSpeed-Gerät an einen SuperSpeed-Port angeschlossen wird, wird zuerst versucht, über die SuperSpeed-Schnittstelle eine Verbindung herzustellen. Schlägt das fehl, wird ein Verbindungsversuch über USB 2.0 unternommen. In der Praxis (und laut Spezifikation) verbindet sich ein USB-Gerät nie gleichzeitig über beide Versionen. Das ist aber möglich und führt dazu, dass sich das USB-Gerät wie zwei getrennte Geräte verhält.

Ein SuperSpeed-Hub besteht aus zwei parallelen Hubs: einem für USB 2.0 und einem für SuperSpeed. Wenn Sie einen externen SuperSpeed-USB-Hub an einen Computer anschließen, werden dem System zwei Hubs hinzugefügt. Sie erscheinen als zwei getrennte Geräte. Ein normales USB-Gerät darf nicht beide Versionen parallel nutzen, ein Hub muss das jedoch.

Wird ein SuperSpeed-Hub an einen USB-2.0-Port angeschlossen, verhält er sich wie ein gewöhnlicher USB-2.0-Hub.

Allgemein arbeitet jeder dieser beiden Hubs unabhängig vom anderen. Jeder Hub hat seine eigenen Ports, und jeder dieser Ports arbeitet unabhängig. Insbesondere hat eine Änderung der Parameter eines Ports an einem dieser parallelen Hubs keine Auswirkung auf die Ports des anderen Hubs.

Daraus folgt auch: Wenn »lsusb -t« zeigt, dass ein Gerät über den SuperSpeed-Root-Hub mit dem Computer verbunden ist, arbeitet es als SuperSpeed-Gerät. Seine Datenrate beträgt also 5 Gbit/s oder mehr. Ist das Gerät dagegen über den USB-2.0-Root-Hub verbunden, beträgt seine Datenrate 480 Mbit/s oder weniger.

SuperSpeed und PORT_POWER

Zur Erinnerung: hubpower hat im Grunde nur das Attribut PORT_POWER des Ports geändert.

Aber ein SuperSpeed-Hub besteht aus zwei parallelen Hubs. Jeder dieser beiden Hubs hat für jeden Port ein eigenes, unabhängiges PORT_POWER-Attribut. Wann soll der Hub also die VBUS-Spannung abschalten? Jeder physische Stromversorgungsschalter hängt von zwei PORT_POWER-Attributen ab, einem von jedem der parallelen Hubs.

Tabelle 10-2 der USB-3.0-Spezifikation enthält die Wahrheitstabelle dafür, ob der Hub die Stromversorgung einschalten soll oder nicht. Sie lässt sich wie folgt zusammenfassen: Verhält sich der Hub nur als USB-2.0-Hub (z. B. weil er an einen Computer angeschlossen ist, der kein SuperSpeed unterstützt), folgt er dem PORT_POWER von USB 2.0. Ist er als SuperSpeed-Hub angeschlossen (oder sind beide parallelen Hubs verbunden), wird VBUS nur abgeschaltet, wenn beide PORT_POWER null sind.

Falls das kompliziert klang: Das war erst der einfache Teil. Der SuperSpeed-Teil des Hubs besitzt eine andere interne Zustandsmaschine (state machine). Das ist nicht weiter überraschend, denn das Link-Training läuft anders ab. Diese Zustandsmaschine hat jedoch drei verschiedene Zustände (statt wie bei USB 2.0 nur einem), die bei PORT_POWER null vorgesehen sind:

Der Zweck der beiden letzten Zustände ist es, sicherzustellen, dass die Verbindung nicht auf USB 2.0 zurückfällt, wenn ein SuperSpeed-Gerät mit eigener Stromversorgung an den Port angeschlossen wird. Das würde nämlich geschehen, weil das Gerät keine Möglichkeit hätte zu erkennen, dass es an einem SuperSpeed-Port hängt.

Was lernen wir daraus? Vor allem, dass das Zurücksetzen eines Geräts durch Ändern von PORT_POWER an einem SuperSpeed-Hub nicht so einfach ist wie an einem USB-2.0-Hub.

SuperSpeed: Warm Reset und Hot Reset

USB 2.0 hat eine einfache Möglichkeit, das Gerät zurückzusetzen: Der Hub verbindet beide Leitungen für 10 ms mit Masse (SE0). SuperSpeed-Geräte haben dagegen zwei Möglichkeiten: Warm Reset und Hot Reset. Diese sollte man nicht mit PowerOn Reset und Inband Reset verwechseln; diese Begriffe bezeichnen den Grund für den Reset. Inband Reset bedeutet, dass der Reset aufgrund einer Anforderung des Hosts erfolgt. Je nach Art der Anforderung kann das zu einem Warm Reset oder einem Hot Reset führen (gleich mehr dazu).

Es ist wichtig, zwischen Warm Reset und Hot Reset zu unterscheiden: Ein Warm Reset bedeutet insbesondere, dass der Datenstrom angehalten und von vorn aufgebaut wird. Ein Hot Reset wird auf dem laufenden Datenstrom selbst gesendet und lässt diesen Datenstrom weiterlaufen. Der Hot Reset ist also viel schneller. Wenn es jedoch ein Problem mit dem Datenstrom gibt, das durch einen Neustart behoben werden kann, ist ein Warm Reset erforderlich. Ein Beispiel für ein solches Problem sind Bitfehler auf der physischen Schicht. Das Herunterfahren des Bitstroms (bitstream) und ein erneuter Aufbau können das beheben, möglicherweise weil dabei eine nicht optimale Abstimmung des Entzerrers (equalizer) korrigiert wird.

Zur Erinnerung: usbreset führt ein ioctl() mit USBDEVFS_RESET aus. Unter anderem wird dabei PORT_RESET auf null gesetzt, unabhängig von der USB-Version des Geräts (Stand Linux-Kernel v5.16).

Laut Abschnitt 7.4.2 der USB-3.0-Spezifikation führt eine PORT_RESET-Anforderung zu einem Hot Reset. Das bedeutet: Wenn der Datenstrom aktiv ist, wird er nicht abgebaut, sondern der Reset-Befehl wird auf diesem Datenstrom gesendet. Die Spezifikation definiert außerdem BH_PORT_RESET (Feature-Nummer 28), das einen Warm Reset erzwingt (sofern der Port nicht deaktiviert ist). Dieser Reset ist grundlegender: Er fährt den Datenstrom herunter und beginnt die Prozedur für den erneuten Aufbau von vorn (mittels LFPS-Signalisierung). Der wichtige Unterschied ist: Wenn der Datenstrom neu gestartet werden muss, schafft das BH_PORT_RESET, aber PORT_RESET nicht.

Das Anschließen eines neuen SuperSpeed-Geräts ist mit einem Warm Reset verbunden. Es ist daher schade, dass es offenbar kein Werkzeug gibt, das den PORT_POWER-Trick an einem SuperSpeed-Port sauber hinbekommt. Wie oben erwähnt, kann uhubctl das technisch zwar erledigen, aber das Gerät landet danach in einem unschönen Zustand.


Verstreute Notizen

Das sind verstreute Informationen, die nützlich sein können, aber in keinen rechten Zusammenhang passen.

Diese Seite wurde maschinell aus dem Englischen übersetzt. Im Zweifelsfall siehe den Originaltext.
Copyright © 2021-2026. All rights reserved. (dcc38493)