Digitale Fernsehkarten sind oft nur für wenige Wochen im Handel, bevor sie von einem etwas modifizierten Nachfolger ersetzt werden. Wer der Entwicklung folgen oder die neuesten Modelle in Betrieb nehmen will, kommt um experimentelle DVB-Treiber nicht herum.
Die Digitalisierung des Fernsehens schreitet nicht nur über die Antenne voran, selbst im Kabel und über Satellit werden viele Sender nur noch digital ausgestrahlt. Für den Empfang braucht man daher eine digitale Fernsehkarte nach dem DVB-Standard (Digital Video Broadcasting). Durch die Schnelllebigkeit der Produkte befindet sich die Treiberentwicklung in ständigem Fluss, schon geringfügige Modifikationen der Empfangskarten – wie der Austausch eines Tuners – erfordern Anpassungen im Treiber.
Der Kernel kann mit den DVB-Treiberentwicklern kaum mehr Schritt halten. Wer brandaktuelle Hardware einsetzen möchte, kommt daher an den experimentellen Treibern [1] nicht vorbei. Die Entwickler benutzen an Stelle von CVS oder Subversion das Verwaltungssystem Mercurial, abgekürzt Hg. Da nur die neuesten Distributionen die entsprechenden Mercurial-Tools mitbringen, gelangt man am einfachsten an einen Treiber, indem man einen aktuellen Snapshot über das Web-Frontend von [2] herunterlädt.
Nicht experimentell genug
Im Fall der Satelliten-Empfangskarte Technotrend DVB-S1401 Budget (Abbildung 1) genügten die Snapshots des v4l-dvb-Repositories nicht: Der passende Treiber von Andrew de Quincy war bei Redaktionsschluss in dem separaten Repository v4l-dvb-tt-s-1401 untergebracht. Es dürfte jedoch nur wenige Tage oder Wochen dauern, bis der Treiber in das Hauptrepository übernommen wird.
Zum Übersetzen der Treiber benötigt man die Kernel-Quellen. Wir verwendeten die bei Redaktionsschluss aktuelle Version 2.6.17.7 für den Test. Dabei muss in der Rubrik Device Drivers unter Multimedia devices die Optionen Video For Linux aktiv sein, am besten sollten man zudem im Untermenü Digital Video Broadcasting Devices sämtliche Treiber als Module erstellen.
Ist der Kernel nebst Modulen übersetzt und gestartet, geht es an die neuen DVB-Treiber. Das Haupt-Repository v4l-dvb überschreibt einige Treiber des Kernels und muss daher nach dem Kernel übersetzt werden. Anschließend folgen Spezialtreiber, wie der für die Technotrend DVB-S1401. Wer diese Reihenfolge ändert, riskiert, dass ein älterer Treiber einen neuen überschreibt.
Manuelle Anpassungen
Direkt aus dem Repository ließen sich die Quellen vom 26.07.2006 nicht übersetzen: Die Quelldatei linux/drivers/media/radio/radio-sf16fmr2.c war fehlerhaft und musste deshalb deaktiviert werden. Das geht sehr einfach, indem man die Datei löscht oder mit der Endung .no versieht. Damit wird der Treiber nicht mehr übersetzt, steht also auch nicht zur Verfügung – im Fall der Technotrend DVB-S1401 spielt das jedoch keine Rolle.
Eine zweite Anpassung betraf die experimentellen Treiber von Andrew de Quincy. In der Version vom 26.07.2006 waren die Debugging-Ausgabe in den Modulen tda10086, tda80xx und zoran_card aktiviert. Das führt aufgrund der gewaltigen Menge an Debug-Ausgaben auf langsameren Rechnern zu Geschwindigkeitsproblemen. Das Problem lässt sich lösen, indem man in den betroffenen Modulen die Variable debug auf 0 setzt:
static int debug = 0;
Die ständig wachsenden Treibervielfalt führt zu Problemen dabei, die jeweils passenden Module zu laden, etwa bevor das Programm VDR startet, und nach dessen Terminierung wieder zu entladen. Üblicherweise erledigt man das heute per make insmod, mit /etc/init.d/dvb start oder auch über /etc/init.d/runvdr start und lädt dabei ohne Rücksicht auf die tatsächliche Rechnerausstattung einen ganzen Stapel an Treibern. Der Nachteil dieser Methode: Die Treiberliste muss von Hand gepflegt werden, oft umfasst sie die experimentellen nicht. Zudem benötigt jedes Kernel-Modul eine gewisse Zeit, bis es initialisiert – selbst wenn die passende Hardware gar nicht vorhanden ist. Das zieht den Systemstart unnötig in die Länge.
Selbstlader
Das verkürzte Skript in Listing 1 hingegen lädt nur solche Treiber, die das System für den Betrieb der im jeweiligen Rechner eingebauten Hardware tatsächlich benötigt. Das Skript besteht aus vier wesentlichen Teilen: Einer Hardwareerkennung in den Zeilen 11 bis 27, einem Filter für die benötigten Kernel-Module in den Zeilen 29 bis 66, sowie dem Lade- und Entlademechanismus in den Zeilen 68 bis 85 und 88 bis 94.
Die Hardwareerkennung funktioniert im abgedruckten Listing nur dann, wenn der Kernel über sysfs-Unterstützung verfügt. Das vollständige Skript von [3] kann zusätzlich mit dem althergebrachten proc-Dateisystem umgehen. Die for-Schleife in Zeile 12 arbeitet nacheinander sämtliche PCI-Geräte ab und bestimmt in den Zeilen 13 bis 22 neben Vendor- und Product-ID auch die Sub-Vendor- und Sub-Device-IDs sowie die Geräteklasse. Dabei berücksichtigt das Skript lediglich die letzten 16 Bit der IDs, ohne vorangestelltes 0x, lediglich die Geräteklasse ist 24 Bit lang.
Ohne Auswertung der Sub-Vendor- und Sub-Device-IDs funktioniert die Treiberbestimmung nicht, da die unterschiedlichsten DVB-Empfangskarten die gleichen Vendor- und Product-IDs verwenden. Selbst anhand der Sub-Vendor- und Sub-Device-ID ist eine eindeutige Zuordnung nicht immer möglich, wie am Beispiel der Technotrend DVB-S Premium Revision 1.3, 1.5 und 1.6 in Tabelle “DVB-Empfangskarten” zu sehen. Auch kommt es vor, dass eine Empfangskarte gleich mehrere PCI-Geräte anmeldet, wie etwa im Fall der Hauppauge Nova-T 90002 – hier sind allerdings Sub-Vendor- und Sub-Device-ID für alle PCI-Devices identisch.
Den wichtigsten Unterschied zwischen den einzelnen Revisionen der DVB-Empfangskarten machen die verwendeten Tuner aus. Welchen Tuner der Hersteller auf welcher Karte verbaut hat, lässt sich jedoch nicht über die Geräte-IDs erkennen, sondern nur per I2C-Bus auslesen. Daher kann es durchaus vorkommen, dass eine DVB-Empfangskarte nach den Geräte-IDs zwar funktionieren müsste, der dafür zuständige Treiber dvb-ttpci jedoch nichts empfangen kann, weil ein bislang unbekannter Tuner vorliegt. Dagegen ist die Hardware-Erkennung aus Listing 1 machtlos.
Die Zeile 24 prüft, um welche Geräteklassen es sich handelt. DVB-Empfangskarten gehören stets der Multimedia-Geräteklassen 0x0400 und 0x0480 an – also fügt Zeile 25 nur solche Geräte der PCI-Geräteliste hinzu, die aus diesen beiden Multimedia-Geräteklassen stammen. Grundsätzlich eignet sich die Hardwareerkennung in den Zeilen 11 bis 27 für sämtliche PCI-Geräte, das Skript beschränkt sich jedoch auf die DVB-Empfangskarten.
Treibersuche
Der Codeblock von Zeile 29 bis 66 sucht nach Kernel-Modulen, die für die vorher herausgefilterten und in der Variablen PciDev gespeicherten PCI-Empfangskarten zuständig sind. Dabei nutzt das Skript aus, dass der Kernel eine Zuordnung von Modulnamen und Geräte-IDs in der Datei modules.pcilist speichert. In dieser Datei finden sich nur Treiber, die tatsächlich als ladbares Kernel-Modul zur Verfügung stehen – nicht kompilierte oder fest eingebundene Treiber tauchen hier also nicht auf. Die Einträge für die Module dvb-ttpci und stradis und deren Bedeutung zeigt die Tabelle “Treibereinträge”.
Treibereinträge
| Vendor | Product-ID | Sub-Vendor | Sub-Device | Class | Bitmask | ||
|---|---|---|---|---|---|---|---|
| dvb-ttpci | 0x00001131 | 0x00007146 | 0x0000110a | 0x00000000 | 0x00000000 | 0x00000000 | 0x0 |
| stradis | 0x00001131 | 0x00007146 | 0xffffffff | 0xffffffff | 0x00000000 | 0x00000000 | 0x0 |
Wie bereits bei den PCI-IDs benötigen wir von den Spalten 2 bis 5 aus der Tabelle “Treibereinträge” lediglich die letzten 16 Bit, nur bei der Geräteklasse und der Bit-Maske müssen wir mit 24 Bit arbeiten. Das Herausfiltern der entsprechenden IDs übernehmen die Zeilen 32 bis 41 in Listing 1. Die Schleife ab Zeile 44 vergleicht nun jeden Eintrag in der Treiberliste mit der Liste der DVB-Empfangskarten, die die Hardwareerkennung ermittelt hat.
Listen aufteilen
In der Liste der DVB-Karten trennen Tabulatoren die einzelnen Geräte voneinander, während die IDs der jeweiligen Devices mit Kommas separiert sind (Zeile 25). Diese unterschiedliche Trennung nutzt die Zeile 43 aus, indem die Trennungsvariable IFS zunächst nur an Tabulatoren und Zeilenumbrüchen auftrennt – dementsprechend arbeitet die for-Schleife in Zeile 44 die einzelnen Geräte ab, die IDs bilden für die Schleife jedoch eine Einheit.
Dies ändert sich erst in Zeile 45, wenn IFS nun auch das Komma als Trennzeichen interpretiert – Zeile 46 betrachtet daher jede einzelne ID des jeweiligen Geräts als einzelnen Parameter und ordnet diesen einer der Variablen $1 bis $5 zu. Die gleiche Methode, nur mit Leerzeichen als Trenner, hatte bereits Zeile 33 verwendet und ebenfalls die Variablen $1 bis $7 als Zwischenspeicher benutzt. Die in Zeile 33 zugewiesenen Werte werden daher in Zeile 45 überschrieben – daher speichert das Skript die Variablen $1 bis $7 in den Zeilen 35 bis 41 in andere Variablen um.
Generische Treiber
Die Zeilen 48 und 49 vergleichen die Vendor- und Product-ID aus der Treiberliste mit den IDs des Geräts – stimmen sie überein, ist der Treiber für das Gerät zuständig. Dabei berücksichtigt das Skript auch generische Treiber, die etwa eine ganze Geräteklasse unterstützen, wie etwa den USB-Treiber uhci-hcd.
Als zweites Kriterium werden die Sub-Vendor- und Sub-Device-IDs herangezogen – nur wenn hier ebenfalls die Daten aus der Liste mit den IDs des Geräts übereinstimmen, eignet sich der Treiber tatsächlich für die gerade untersuchte Karte. Auch hier gibt es generische Treiber, beispielsweise stradis.
Die Geräteklasse interessiert eigentlich nur für generische Treiber, denen keine Vendor-, Product-, Sub-Vendor- und Sub-Device-ID zugeordnet ist. DVB-Treiber fallen aktuell nicht darunter – das Prüfen der Klassenangabe und Bitmaske in den Zeilen 52 und 53 ist daher derzeit nicht notwendig, könnte es aber in Zukunft werden. Die Schleife von Zeile 54 bis 57 unterbindet doppelte Moduleinträge. Externe Programme wie sort und uniq lassen sich dazu nicht heranziehen, da sie auch die Reihenfolge der Liste verändern würden.
Stimmten alle Angaben zwischen Treiberliste und Gerät überein, ist der Treiber für dieses Gerät zuständig und muss geladen werden. Dementsprechend fügt ihn Zeile 60 ans Ende der bisherigen Treiberliste an. Zeile 29 hat diese Treiberliste bereits mit den in der Variablen FIXEDMODULES angegebenen Kernel-Modulen initialisiert, Tabulatoren trennen hier die einzelnen Modulnamen.
Treiberabhängigkeiten
Der Befehlsblock von Zeile 68 bis 78 übernimmt das Laden der Module. Eine Besonderheit stellt die Schleife von Zeile 72 bis 75 dar: Sie löst auf, welche Treiber zusätzlich benötigt werden, damit sich ein bestimmtes Kernel-Modul überhaupt laden lässt. Dazu benutzt das Skript die Datei modules.dep, die der Befehl depmod -a generiert und auch modprobe auswertet.
Jede Zeile der beginnt modules.dep beginnt mit dem Dateipfad und dem Namen des Treibers, gefolgt von einem Doppelpunkt und einer Liste von Kernel-Modulen, von denen das Modul abhängt. Nach dem Laden des Treiber in Zeile 77 per modprobe arbeitet das Programm die Treiberliste von hinten nach vorn ab und bindet ein Modul nach dem anderen ein, bis es schließlich den ursprünglich gewünschten Treiber als letztes lädt.
Zeile 76 sorgt dafür, dass sämtliche Module in umgekehrter Ladereihenfolge in der Variablen DvbModules eingetragen sind, die Zeilen 83 bis 85 speichern diese Reihenfolge dann in der Datei /var/run/dvbdriver.modules. Diese Notation macht es später leichter, die Module per modprobe -r nacheinander wieder zu entladen – fängt man am falschen Ende der Liste an, lassen sich die Module nicht entladen, da sie noch verwendet werden.
Prinzipiell kann modprobe -r die abhängigen Module auch selbst entladen, sodass das Auflösen der abhängigen Module in den Zeilen 72 bis 75 überflüssig wäre. Dies gilt jedoch nur für den Idealfall – braucht etwa ein Treiber etwas länger, um hinter sich aufzuräumen, blockiert er unter Umständen das Entladen anderer, abhängige Module. Ein erneutes modprobe -r würde jedoch scheitern, da das obenliegende Modul bereits entfernt wurde – die abhängigen Module blieben im Kernel. Mit einer Liste der abhängigen Module jedoch lassen sich durch den erneuten Aufruf von dvbdriver stop die noch verbliebenen Treiber entladen. Dies übernehmen die Zeilen 92 bis 94.
DVB-Empfangskarten
| Bezeichnung | Typ | Vendor-ID | Product-ID | Sub-Vendor-ID | Sub-Device-ID | Class-ID |
| CTX917 | DVB-T Budget | 0x1131 | 0x7134 | 0x16be | 0x0030 | 0x0480 |
| Hauppauge Nexus-CA Rev. 1.1 | DVB-C Premium | 0x1131 | 0x7146 | 0x13c2 | 0x000a | 0x0480 |
| Hauppauge Nexus-s Rev. 2.1 | DVB-S Premium | 0x1131 | 0x7146 | 0x13c2 | 0x0003 | 0x0480 |
| Hauppauge Nova-T 90002 | DVB-T Budget | 0x14f1 | 0x8800, 0x8802, 0x8804 | 0x0070 | 0x9002 | 0x0400, 0x0480 |
| Hauppauge Nova-T | DVB-T Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1011 | 0x0480 |
| Technotrend DVB-S Budget CI | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x100f | 0x0480 |
| Technotrend DVB-S Budget CI Rev. 1.0 | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1017 | 0x0480 |
| Technotrend DVB-S Budget Rev. 1.0 | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x100f | 0x0480 |
| Technotrend DVB-S Budget Rev. 1.1 | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1003 | 0x0480 |
| Technotrend DVB-S Budget Rev. 1.4 | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1016 | 0x0480 |
| Technotrend DVB-S1400 Budget | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1016 | 0x0480 |
| Technotrend DVB-S1401 Budget | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1018 | 0x0480 |
| Technotrend DVB-S1500 Budget CI | DVB-S Budget | 0x1131 | 0x7146 | 0x13c2 | 0x1017 | 0x0480 |
| Technotrend DVB-S Premium Rev. 1.3 | DVB-S Premium | 0x1131 | 0x7146 | 0x13c2 | 0x0000 | 0x0480 |
| Technotrend DVB-S Premium Rev. 1.5 | DVB-S Premium | 0x1131 | 0x7146 | 0x13c2 | 0x0000 | 0x0480 |
| Technotrend DVB-S Premium Rev. 1.6 | DVB-S Premium | 0x1131 | 0x7146 | 0x13c2 | 0x0000 | 0x0480 |
| Technotrend DVB-S2300 Premium | DVB-S Premium | 0x1131 | 0x7146 | 0x13c2 | 0x000e | 0x0480 |
In Reih und Glied
Ein großes Problem sind implizite Abhängigkeiten, ohne die ein Treiber die zugehörige Hardware nicht initialisieren kann, die jedoch nicht in der Modulliste modules.pcimap aufgeführt sind. Das Modul stradis für die Technotrend DVB-S1401 Budget bereitete besonderen Ärger: Es darf erst nach den Modulen dvb-ttpci und budget geladen werden, weil es sonst die DVB-Karte nicht in Betrieb nehmen kann. Statt dessen tauchen im Log Meldungen der Art i2c setup read timeout und i2c read timeout auf.
Die Lösung für das Skript dvbdriver sieht so aus, dass hinter der Variablen FIXEDMODULES in Zeile 4 von Listing 1 die beiden Module dvb-ttpci und budget eingetragen werden. Da das Skript zunächst sämtliche Module der Variablen FIXEDMODULES in genau der angegebenen Reihenfolge lädt und anschließend erst die Treiber der vorgefundenen DVB-Karten, ist sichergestellt, dass dvb-ttpci und budget in jedem Fall vor dem Modul stradis geladen sind.
Damit ist dvbdriver flexibel genug, auch in Zukunft auf implizite Abhängigkeiten oder vorgegebene Reihenfolgen beim Laden der Module zu reagieren. Durch die Hardware-Erkennung wird zudem sicher gestellt, dass sich lediglich die Treiber im Speicher befinden, die auch tatsächlich für den Betrieb der eingebauten Hardware benötigt werden – eine manuelle Pflege der Treiberlisten ist damit überflüssig.
Listing 1
#!/bin/bash
MODDEP=/lib/modules/`uname -r`/modules.dep
PCIMAP=/lib/modules/`uname -r`/modules.pcimap
FIXEDMODULES="dvb-ttpci,budget"
Tab=$'\t'
Newline=$'\n'
case "$1" in
load)
IFS=",${Tab}${Newline}"
for device in /sys/bus/pci/devices/*; do
read Vendor < ${device}/vendor
Vendor=${Vendor:2:4}
read Device < ${device}/device
Device=${Device:2:4}
read SubVendor < ${device}/subsystem_vendor
SubVendor=${SubVendor:2:4}
read SubDevice < ${device}/subsystem_device
SubDevice=${SubDevice:2:4}
read Class < ${device}/class
Class=${Class:2:6}
if [ "${Class:0:4}" = "0400" -o "${Class:0:4}" = "0480" ]; then
PciDev="${PciDev}${Tab}${Vendor},${Device},${SubVendor},${SubDevice},${Class}"
fi
done
Modules2Load="${FIXEDMODULES//,/${Tab}}"
IFS="${Newline}"
for z in `cat $PCIMAP`; do
IFS=" "
set – $z
module="$1"
vendor="${2:6:4}"
product="${3:6:4}"
subvendor="${4:6:4}"
subdevice="${5:6:4}"
class="${6:4:6}"
classmask="${7:4:6}"
IFS="${Tab}${Newline}"
for d in ${PciDev}; do
IFS=",${Tab}${Newline}"
set – $d
if [ "${vendor}" = "${1}" -a "${product}" = "${2}" -o \
"${vendor}" = "ffff" -a "${product}" = "ffff" ]; then
if [ "${subvendor}" = "${3}" -a "${subdevice}" = "${4}" -o \
"${subvendor}" = "ffff" -a "${subdevice}" = "ffff" ]; then
if [ "${class}" = "0000" -o \
"$[ 0x${5} & 0x${classmask} ]" = "$[0x${class}]" ]; then
for m in ${Modules2Load}; do
if [ "${m}" = "${module}" ]; then
module=""
fi
done
if [ -n "${module}" ]; then
Modules2Load="${Modules2Load}${Tab}${module}"
fi
fi
fi
fi
done
done
IFS=" ${Tab}${Newline}"
DvbModules=""
for m in ${Modules2Load}; do
dep=""
for d in `grep "${m}\.ko:" ${MODDEP}`; do
d=${d##*/}
dep="${dep}${Tab}${d%.ko*}"
done
DvbModules="${dep}${Tab}${DvbModules}"
modprobe ${m}
done
if [ -e /var/run/dvbdriver.modules ]; then
rm -f /var/run/dvbdriver.modules
fi
for m in ${DvbModules}; do
echo ${m} >> /var/run/dvbdriver.modules
done
;;
unload)
if [ ! -r /var/run/dvbdriver.modules ]; then
echo "Keine Modulliste gefunden (/var/run/dvbdriver.modules)"
exit 1
fi
for m in `cat /var/run/dvbdriver.modules`; do
modprobe -r ${m}
done
;;
esac
Infos
[1] DVB-Treiber: http://linuxtv.org
[2] Mercurial Repositoryies der DVB-Treiber: http://linuxtv.org/hg
[3] DVD-Treiber-Skript dvbdriver: http://www.linux-user.de/Downloads/2006/09/dvb





