Die Entwicklung von Gerätetreibern ist dank der Ignoranz vieler Hersteller nach wie vor eine der Hauptaufgaben der Linux-Community. Dieser Artikel zeigt am Beispiel eines USB-Empfängers für DVB-T und analoges Fernsehen, wie Linux-Treiber mit viel Aufwand und Hilfe der Windows-Treiber entstehen.
Linux-Treiber stehen bei vielen Hardwareherstellern nach wie vor ganz unten auf der Prioritätsliste, nur bei Grafikkarten, Mainboard-Chipsätzen und Raid-Controllern haben die Anbieter den Linux-Trend erkannt. So bleibt die Arbeit der Treiberentwicklung meist an der Linux-Gemeinde hängen. Erschwerend ist dabei, dass die Ansteuerung von Spezialchips oder die Funktionen der Firmware zum Geschäftsgeheimnis erklärt werden, sodass die Linux-Entwickler keinerlei Informationen bekommen, wie die Geräte überhaupt arbeiten.
Vor dieser Situation stand der Autor dieses Artikels, als es darum ging, für den USB-TV-Empfänger Medion MD 95700 einen Linux-Treiber zu entwickeln.
Gezielte Treibersuche
Wer damit beginnt, einen eigenen Treiber zu schreiben, sollte zuvor unbedingt nachsehen, ob nicht bereits jemand an einem Treiber arbeitet. Die üblichen Suchmaschinen sind die ersten Anlaufstellen, weitere Informationen liefern die Kernelquellen und die Kernel-Mailingliste. Bei relativ neuen Geräten lohnt sich auch ein Blick auf die Website der für die Geräteklasse zuständigen Kernel-Maintainer und -Maintainergruppen, die in der Datei »MAINTAINERS« im Kernel-Tree genannt sind. Bringt auch das keinen Erfolg, bleibt als letzte Möglichkeit, noch auf der Mailingliste nachzufragen, ob schon andere an einem Treiber für das Gerät arbeiten.
Ein Problem bei der Suche nach vorhandenen Treibern ist, dass viele Hersteller dem Gerät eine eigene Bezeichnung geben, obwohl unter der Haube ein Referenzdesign steckt, das auch andere Anbieter verwenden, oder dass es sich nur um eine geringfügige Modifikation eines älteren, bereits unterstützten Geräts handelt. Diese Fragen kann nur ein Blick ins Innere des Geräts beantworten.
Wegen der hohen Abstraktion bei der Open-Source-Treiberentwicklung kann es sein, dass der Treiber einen Namen trägt, der vom Chipsatz oder vom Gerätenamen eines anderen Herstellers abgeleitet ist. Handelt es sich nur um den Klon eines anderen Geräts, das bereits unterstützt wird, kann auch bereits ein Treiber existieren, der sich nur aufgrund veränderter Device-IDs nicht für das neue Gerät zuständig fühlt, es aber teilweise oder sogar komplett unterstützt.
In solchen Idealfällen zeigt sich die Stärke von Open-Source-Implementationen: Während unter Windows ein neuer Treiber aus den Quellen des Herstellers übersetzt werden müsste, die nicht öffentlich verfügbar sind, genügt es bei Linux, die neue ID im offenen Treiber einzutragen. Ein gutes Beispiel dafür sind TV-Karten mit Brooktrees BT-8xx- oder Conexant-88xx-Chipsätzen.
Klone und Neuentwicklungen
Bei USB-Geräten ist es nicht immer so einfach: Einige der vom Autor im Vorfeld untersuchten USB-Geräte sind zwar Hardware-seitig nur Klone, durch Veränderungen in der Firmware unterscheidet sich ihre Ansteuerung aber völlig von der bei den bekannten Geräten. Für den Hybrid-Empfänger MD 95700 für analoges und digitales Fernsehen gab es noch keinen Linux-Treiber, sodass der Autor selbst Hand anlegen musste.
Das vorliegende Gerät wurde von einem Linux-Anwender eingesandt, der es bei dem Discounter Aldi gekauft und sich auf der Suche nach Treibern an den Autor gewandt hatte. Dessen Recherche lieferte keine Ergebnisse, die Suchmaschinen fanden lediglich zwei Diskussionen, in denen es um den Windows- und Mac-OS-Treiber ging. Dort waren aber wenigstens die genauen Bezeichnungen der Chips, die das Medion-Gerät enthält, aufgelistet.
Geräte-IDs auslesen
Auch ohne Treiber liefern die Aufrufe »lsusb« und »lsusb -v« unter Linux erste Informationen zum neuen Gerät. Wenn der Medion-Empfänger, am besten als einziges Gerät, am USB-Port angeschlossen ist, listet »lsusb« gleich drei neue Geräte auf:
Bus 004 Device 001: ID 0000:0000 Bus 003 Device 001: ID 0000:0000 Bus 002 Device 001: ID 0000:0000 Bus 001 Device 006: ID 0bc7:0006 U X10 Wireless Technology, Inc. Bus 001 Device 005: ID 1660:0932 Bus 001 Device 004: ID 04b4:6560 U Cypress Semiconductor Corp. CY7C65640 U USB-2.0 "TetraHub" Bus 001 Device 001: ID 0000:0000
Die USB-IDs »0000:0000« tragen die USB-Host-Controller, die anderen IDs gehören zum Medion MD 95700. Hinter der ID »04b4:6560« verbirgt sich ein integrierter USB-Hub, an dem die Empfangseinheit mit ID »1660:0932«, der Infrarotempfänger »0bc7:0006« sowie der externe USB-Anschluss des MD 95700 angehängt sind. Der Infrarotempfänger ist besser als X10-Funkfernbedienung oder ATI-Remote bekannt, dafür gibt es bereits einen funktionierenden Treiber. Somit fehlte nur noch die Linux-Unterstützung für den Hybrid-Empfänger.

Abbildung 1: Die einfachste Hardwarediagnose besteht meist darin, das Gerät zu zerlegen. Die auf der Platinenunterseite des Medion MD95700 aufgelöteten Conexant-Chips sind alte Bekannte, für die es bereits Linux-Treiber gibt.
Wird das Gerät nicht mit dem Netzteil, sondern über den USB-Anschluss mit Strom versorgt, melden sich lediglich die X10-Funkfernbedienung und der USB-Hub an. Dieser Umstand weist darauf hin, dass sich der Hybrid-Empfänger über das externe Netzteil versorgt, während der USB-Hub und die Fernbedienung ihren Strom vom USB-Anschluss des Rechners beziehen.
Eine weitere Informationsquelle sind die zum Windows-Treiber gehörenden Info-Dateien, hier hinterlassen die Entwickler manchmal wertvolle Hinweise. Im Falle des Medion-Empfängers brachte die Analyse der Datei »ctxusbtv.inf« kaum Verwertbares: Der eigentliche Hersteller scheint Creatix Systems zu sein, zudem erwähnt sie, dass es sich bei dem Hybrid-Tuner um einen Philips FMD1612ME handelt. So viel war aus der Auflistung aus dem Internet schon bekannt.
Der Aufruf »strings ctxusbtv.sys« zeigt, dass der Windows-Treiber Code für den DVB-T-Demodulator Conexant CX22702 enthält, der ebenfalls in der Internet-Diskussion erwähnt ist. Einzig der Umstand, dass die Dateien mit der Endung ».rom« wahrscheinlich den Audio-Teil der Firmware für den Analog-Empfangsteil enthalten, könnte hilfreich sein.
Innenansichten
Nach dem Öffnen des Geräts fand der Autor folgende Chips vor (siehe Abbildung 1): Den DVB-T-Demodulator Conexant CX22702, den Audio/Video-Decoder Conexant CX 25842, den Tetra-USB-Hub Cypress CY7C65640 sowie den programmierbaren USB-2.0-Mikrocontroller Cypress CY7C68013, der besser als FX2 und EZ-USB2 bekannt ist.
Den USB-Hub unterstützt der Kernel bereits, ebenso wie den Demodulator Conexant CX 22702. Den Treiber für den Audio/Video-Demodulator CX25842 fand der Autor nach längerer Recherche im CVS des Ivtv-Projekts unter [1], er läuft dort unter der Bezeichnung »cx25840«.
Der FX2 alias Cypress CY7C68013 ist frei programmierbar, sodass sich dafür kein generischer Treiber entwickeln lässt. Die Firmware des USB-Mikrocontrolers kann so gestaltet sein, dass sie auch die übrigen Chips ansteuert und der Treiber somit nur kurze Kommandos direkt an den FX2 zu übertragen braucht. Andererseits kann der Hersteller aber auch nur einen Transferbus, meist I2C, in der Firmware des FX2 implementieren, weshalb ein Treiber die angeschlossenen Chips direkt ansteuern muss. Für welche Lösung sich der Hersteller entschieden hat, zeigte später das USB-Logging.
Beim MD 95700 enthält die Firmware des FX2 nur einen I2C-over-USB-Bus, sodass die Programmierung der IC tatsächlich der jeweilige Treiber übernehmen muss. Im konkreten Fall ist dies durchaus ein Vorteil, da es für die beiden Conexant-Chips ja bereits fertige Treiber gibt – es fehlt nur noch ein Bustreiber für die Ansteuerung des FX2.
Um die Kommunikation zwischen Computer und USB-Peripherie zu verstehen, ist etwas Grundlagenwissen nötig, das am Beispiel des Medion-Empfängers erklärt werden soll. Der Aufruf von »lsusb -v« zeigt bei jedem Gerät, wie viele Konfigurationen und Interfaces es besitzt. Im Fall des MD 95700 gab es nur eine Konfiguration mit einem Interface.
Datenkanäle und -protokolle
Jedes Interface kann mehrere so genannte Alternate Settings besitzen, die jeweils eine bestimmte Bandbreite zwischen Peripherie und Computer definieren. Gleichzeitig kann nur ein Alternate Setting, also eine Bandbreite, ausgewählt sein. Der Hybrid-Empfänger verfügt über sieben Alternate Settings, also sieben Kommunikationsgeschwindigkeiten. Zudem zeigte »lsusb -v« für jedes Alternate Setting drei Pipes (Datentransferkanäle) an: eine eingehende vom Typ Bulk, eine ausgehende vom Typ Bulk und eine eingehende vom Typ Isochronous.
Insgesamt gibt es vier Datentransfertypen, die einer Pipe zugeordnet sein können. Der Control-Typ wird für Standard Control Messages benutzt, etwa um die Gerätekonfiguration, die Alternate Settings und die Endpoints auszulesen. Die Pipes vom Typ Bulk sind einfache schnelle Datentransferkanäle, während solche vom Typ Isochronous eine bestimmte minimale Übertragungsbandbreite garantieren. Der letzte Typ ist Interrupt, über diese Pipes melden USB-Geräte bestimmte Ereignisse an den Treiber zurück, etwa wenn der Anwender eine Maustaste drückt.
Die beiden Bulk-Pipes sind beim Medion-Empfänger zu einem Endpoint zusammengefasst (Bulk-In, Bulk-Out), der zweite Endpoint enthält lediglich die dritte Pipe mit dem Isochronous-Datenkanal (Isochronous-In). Diese Zusammenstellung ist typisch für USB-Empfänger: Die beiden Bulk-Pipes des ersten Endpoints übertragen wahrscheinlich Kommando- und Steuerungsfunktionen, während die Pipe des zweiten Endpoints die Audio- und Video-Daten liefert.
Virtueller USB-Anschluss
Ohne Informationen des Herstellers, welche Steuerbefehle nötig sind und nach welchem Protokoll der Datenverkehr abläuft, ist es fast unmöglich, einen eigenen Treiber zu schreiben. Ein Ansatzpunkt ist es daher, die Funktion des Windows-Treibers im Betrieb zu analysieren. Dazu hat der Autor unter Windows den gesamten Datenaustausch über den USB-Bus mit Hilfe des Programms »usbsnoop« [2] belauscht.
Das Programm emuliert einen virtuellen USB-Anschluss und protokolliert die Kommunikation zwischen Betriebssystem und USB-Gerät in einer Logdatei (Abbildung 2). Dabei ist es wichtig, jedes einzelne Feature des DVB-Empfängers mindestens einmal anzusprechen, damit das USB-Log alle Funktionsaufrufe enthält und später unter Linux auch alle Funktionen nutzbar sind.

Abbildung 2: Das Windows-Programm »usbsnoop« emuliert einen virtuellen USB-Anschluss und schreibt die gesamte Kommunikation zwischen Betriebssystem und Hardware in ein Log. Auf diese Weise lässt sich das Protokoll für den Linux-Treiber ermitteln.
Für die Analyse der mehrere MByte großen Logdatei ist wichtig zu wissen, dass alle Anfragen und Antworten über den USB-Bus in so genannten USB-Request-Blocks (URB) verpackt sind. Für eine Anfrage erstellt der Treiber einen neuen URB, weist ihm einen Datenpuffer und eine Pipe zu, schreibt etwaige Kommandos in den Datenpuffer und schickt den Block ins USB-Subsystem.
USB-Transfers im Detail
Enthält die Anfrage einen Lesebefehl, reicht das USB-Subsystem den Block mit gefülltem Puffer zurück, bei einem Schreibbefehl stehen die Steuerkommandos beim Absenden im Transfer-Buffer des USB-Request-Blocks.
Für den URB-Transfer gibt es zwei Methoden: Bei der synchronen Übertragung wartet der Treiber, bis das USB-Subsystem die Antwort des gerade abgesendeten Blocks liefert. Kommandos nutzen sinnvollerweise meist den synchronen Datentransfer, damit der Treiber auf Erfolgs- oder Misserfolgsmeldung reagieren kann. Bei asynchronem Datentransfer schickt der Treiber die URBs in eine Pipe des USB-Subsystems, das die Befehle nacheinander abarbeitet und dann eine Callback-Funktion des Treibers aufruft. Diese Methode eignet sich besonders für große Datenmengen, da der Treiber unter Umständen nicht so schnell wie das USB-Subsystem reagiert.
Ein Blick in das USB-Log zeigt, dass die Steuerung des Medion MD9570 nicht über zwei Control-Pipes, sondern über zwei Bulk-Pipes erfolgt, wie in den Listings 1 und 2 zu sehen. Das bedeutet, dass zum Lesen von Daten zwei URB-Transfers erforderlich sind: Der erste URB teilt dem Gerät mit, welche Daten es liefern soll, und reicht beim zweiten URB-Transfer den Empfangspuffer nach. Dies ist nötig, weil der Puffer bei Schreiboperationen lediglich bis zum Absenden des URB gültig ist, während er bei Leseoperationen erst nach dem Versand gültig wird. Die Listings 1 und 2 zeigen die entsprechenden USB-Log-Auszüge für die beiden Operationen.
|
Listing 1: Steuerbefehl |
|---|
01 [132484 ms] >>> URB 8334 going down >>> 02 -- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: 03 PipeHandle = 820b9044 [endpoint 0x00000001] 04 TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK) 05 TransferBufferLength = 00000005 06 TransferBuffer = eed969b8 07 TransferBufferMDL = 00000000 08 00000000: 09 01 01 63 23 09 UrbLink = 00000000 10 [132484 ms] UsbSnoop - MyInternalIOCTLCompletion(f893fdb0) : fido=00000000, Irp=81cc0560, Context=8216bd88, IRQL=2 11 [132484 ms] <<< URB 8334 coming back <<< 12 -- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: 13 PipeHandle = 820b9044 [endpoint 0x00000001] 14 TransferFlags = 00000002 (USBD_TRANSFER_DIRECTION_OUT, USBD_SHORT_TRANSFER_OK) 15 TransferBufferLength = 00000005 16 TransferBuffer = eed969b8 17 TransferBufferMDL = 822d9cb0 18 UrbLink = 00000000 19 [132484 ms] UsbSnoop - MyDispatchInternalIOCTL(f893fe80) : fdo=81b90538, Irp=820c8498, IRQL=0 |

Abbildung 3: Der Transfer-Buffer ist in Senderichtung gefüllt und enthält die I2C-Steuerbefehle für den FX2-USB-Controller – in diesem Fall wird der Inhalt des Registers 0x23h ausgelesen.
Frage und Antwort
Zeile 3 von Listing 1 wählt die Pipe aus, die den URB überträgt, Zeile 4 legt die Richtung des Datentransfers fest. Die Größe des Datenpuffers wird in Zeile 5 übergeben, während Zeile 6 die Adresse des Transfer-Buffer enthält. Der konkrete Steuerbefehl steht in Zeile 8, er umfasst – wie in Zeile 5 festgelegt – 5 Bytes. Die Zeilen 10 bis 19 von Listing 1 zeigen die Bestätigung durch das USB-Gerät; der Puffer in Zeile 17 enthält keine Daten mehr, der USB-Request-Block kommt also leer zurück.
Listing 2 zeigt, wie Daten vom Hybrid-Empfänger ausgelesen werden: Zeile 4 legt einen eingehenden Datentransfer fest, während die Zeile 5 dem USB-Gerät vorgibt, wie groß der Puffer ist. Der Transfer-Buffer in Zeile 7 bleibt allerdings leer – der Rechner schickt quasi einen leeren Request-Block an die Peripherie. In der Antwort (Zeilen 10 bis 19) hat der Medion-Empfänger den Transfer-Buffer mit den angeforderten Daten gefüllt, sie stehen in Zeile 17.
Konkret liest der Datentransfer in Listing 1 den Inhalt des Registers 0x23h aus, die Bedeutung der einzelnen Bytes schlüsselt Abbildung 3 auf. Die Antwort des USB-Geräts in Listing 2 ist lediglich 2 Bytes lang: Das erste Byte 0x08h signalisiert den erfolgreichen Datentransfer, das zweite enthält den Wert des angefragten Registers 0x23h, in diesem Fall enthält es 0xffh.
|
Listing 2: Daten vom USB-Bus |
|---|
01 [132485 ms] >>> URB 8336 going down >>> 02 -- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: 03 PipeHandle = 820b9024 [endpoint 0x00000081] 04 TransferFlags = 00000003 (USBD_TRANSFER_DIRECTION_IN, USBD_SHORT_TRANSFER_OK) 05 TransferBufferLength = 00000002 06 TransferBuffer = eed968b8 07 TransferBufferMDL = 00000000 08 UrbLink = 00000000 09 [132485 ms] UsbSnoop - MyInternalIOCTLCompletion(f893fdb0) : fido=00000000, Irp=81cc0560, Context=82146650, IRQL=2 10 [132485 ms] <<< URB 8336 coming back <<< 11 -- URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: 12 PipeHandle = 820b9024 [endpoint 0x00000081] 13 TransferFlags = 00000003 (USBD_TRANSFER_DIRECTION_IN, USBD_SHORT_TRANSFER_OK) 14 TransferBufferLength = 00000002 15 TransferBuffer = eed968b8 16 TransferBufferMDL = 822d9cb0 17 00000000: 08 ff 18 UrbLink = 00000000 19 [132491 ms] UsbSnoop - MyInternalIOCTLCompletion(f893fdb0) : fido=00000000, Irp=81b72e70, Context=821dd4b0, IRQL=2 |
Linux-Treiber implementieren
Der schwierigste und aufwändigste Teil der Treiberentwicklung ist, die im USB-Log protokollierten Funktionsaufrufe des Windows-Treibers zu enträtseln. Da die Hersteller die Firmware oft als Geschäftsgeheimnis ansehen und höchstens bei Unterzeichnung eines NDA Einblick in die detaillierte Spezifikation gewähren, muss der Programmierer erraten, welcher Aufruf des Treibers welche Funktion in welchem Teil des Geräts bewirkt. Sind die Kommandos enträtselt, muss er noch die Funktionen des Windows-Treibers nachbilden.
Den Treiber »cx25740« für den analogen Empfangszweig betreut das Ivtv-Projekt [1], sodass sich der Autor auf einen Treiber für den DVB-T-Empfangsteil des Medion-Empfängers beschränken konnte. Ist der Ivtv-Treiber ausreichend stabil, soll er sich jedoch samt Video4Linux-Interface in den neuen DVB-T-Treiber für den Medion MD 95700 einfügen. Als Vorlage verwendete der Autor deshalb das Framework der DVB-USB-Treiber [4] des Linux-TV-Projekts [3] und implementierte mit den Informationen aus dem USB-Log das Modul »cxusb«, das ab Linux 2.6.13 Bestandteil des offiziellen Kernel-Trees sein wird.
Unnötige Hürden
Die Treiberentwicklung ist kein Teufelswerk, wenn Linux bereits Teile der Hardware unterstützt und die Windows-Treiber für eine genauere Erkundung der Kommunikation zwischen Betriebssystem und Gerät zur Verfügung stehen. Bei komplett unbekannter Hardware ist der Aufwand jedoch sehr groß, das Protokoll nur anhand der mitprotokollierten Treiber-Kommunikation zu entschlüsseln. Die Hersteller könnten der Community viel Arbeit ersparen, würden sie detaillierte Spezifikationen zu ihren Geräten veröffentlichen. (mdö)
|
Infos |
|---|
|
[1] Ivtv-Projekt (Treiber für den analogen Empfangszweig): [http://www.ivtv.tv] [2] USB-Sniffer »usbsnoop« für Windows: [http://benoit.papillault.free.fr/usbsnoop/] [3] Linux-TV-Projekt (DVB-Entwicklung): [http://www.linuxtv.org] [4] CVS des DVB-Treibers: [http://www.linuxtv.org/cgi-bin/viewcvs.cgi/] |
|
Der Autor |
|---|
|
Patrick Boettcher engagiert sich im Linux-DVB-Projekt und ist dort für einige USB- und PCI-DVB-Treiber verantwortlich. |





