Hlavní navigace

Jak umravnit zlobivý udev

9. 2. 2010
Doba čtení: 9 minut

Sdílet

Dnes otevřeme opět jedno z témat, která se na linuxových serverech ani desktopech běžně neřeší, a sice jak umravnit udev s jeho dynamickými jmény vstupních zařízení. Týká se to jak TV karet, pokud jich máme víc než jednu, tak i dálkového ovládání, pokud používá společnou /dev/input architekturu s klávesnicí.

Linuxové ovladače TV karet vytvářejí s pomocí udev své virtuální soubory v adresáři /dev  – analogové TV karty pod názvem /dev/videoX (kde X je postupně rostoucí číslo od 0 v pořadí, jak jsou karty detekovány) a digitální TV karty pod názvem /dev/dvb/adapterX/ (X opět roste od 0 v pořadí detekování karet). V konfiguraci mythbackendu se zadávají a rozlišují jednotlivé TV karty právě touto cestou, takže zásadní zrada nastává, pokud dojde při startu počítače k detekování TV karet v jiném pořadí, než v jakém byly rozpoznány tehdy, když jsme je do mythbackendu konfigurovali.

Nejdříve jednoduchý důkaz, že k této situaci opravdu jednou za čas dochází, i když si třeba 100× restartujete, jen abyste si ověřili, že u vás ne. Po sto prvé k tomu náhodou stejně dojde, mimochodem právě ve chvíli, kdy potřebujete moc nutně nahrát nějaký důležitý pořad – viz následující výpis z /var/log/syslog  zobrazující 4 zapnutí mythbackendu v pátek odpoledne:

Feb  5 12:39:00 htpc kernel: [    9.850792] DVB: registering adapter 0 frontend 0 (Afatech AF9013 DVB-T)...
Feb  5 12:39:00 htpc kernel: [   10.196682] DVB: registering adapter 1 frontend 0 (ST STV0299 DVB-S)...
Feb  5 12:39:00 htpc kernel: [   10.645435] DVB: registering adapter 2 frontend 0 (Conexant CX24116/CX24118)...

Feb  5 14:43:18 htpc kernel: [    9.315456] DVB: registering adapter 0 frontend 0 (Afatech AF9013 DVB-T)...
Feb  5 14:43:18 htpc kernel: [    9.722092] DVB: registering adapter 1 frontend 0 (ST STV0299 DVB-S)...
Feb  5 14:43:18 htpc kernel: [   10.195413] DVB: registering adapter 2 frontend 0 (Conexant CX24116/CX24118)...

Feb  5 17:48:17 htpc kernel: [    9.472184] DVB: registering adapter 0 frontend 0 (ST STV0299 DVB-S)...
Feb  5 17:48:17 htpc kernel: [    9.504940] DVB: registering adapter 1 frontend 0 (Afatech AF9013 DVB-T)...
Feb  5 17:48:17 htpc kernel: [    9.941212] DVB: registering adapter 2 frontend 0 (Conexant CX24116/CX24118)...

Feb  5 20:05:20 htpc kernel: [    9.940885] DVB: registering adapter 0 frontend 0 (Afatech AF9013 DVB-T)...
Feb  5 20:05:20 htpc kernel: [   10.264692] DVB: registering adapter 1 frontend 0 (ST STV0299 DVB-S)...
Feb  5 20:05:20 htpc kernel: [   10.732666] DVB: registering adapter 2 frontend 0 (Conexant CX24116/CX24118)...

V 17:48 se prostě DVB-T Volar X (na USB sběrnici) o 3 setiny sekundy opozdil a dostal se tak nečekaně až za DVB-S SkyStar2 (na PCI sběrnici). Tím pádem se pomíchaly karty v mythbackendu, nenahrály se žádné pořady a WAF byl zásadně ohrožen (WAF = Wife Appreciation Factor je často zaklínaným slovem v MythTV komunitě – vyjadřuje stupeň spokojenosti manželky a dalších rodinných příslušníků se stavem multimediálního centra, a tedy nepřímo i s vámi jakožto jeho správcem). Ve 20:05 jsem to „opravil“ pouhým restartem systému.

V podstatě stejná situace nastává u dálkových ovládání, které jsou už převedené na univerzální vstupní architekturu v Linuxu, a tedy se objeví v /dev/input/eventX (X opět roste od nuly podle pořadí detekování). Tady stačí, abychom například připojili či odpojili klávesnici, myš nebo jinou TV kartu, která má taky své dálkové ovládání, a hned se rozsype naše pečlivě vyladěná konfigurace LIRC, ve které máme zapsanou cestu například /dev/input/even­t5, zatímco ovladač se před chvilkou posunul na event6 či 4.

Jak z toho ven? Řešením je přesvědčit udev, aby nám poskytl nějaký pevný bod, soubor se jménem, které se nezmění při příštím restartu. Možnosti jsou dvě. Za prvé, upravit konfiguraci udev tak, aby nám kromě těch automaticky číslovaných souborů vytvořil i symbolické linky s nějakým normálním pevným jménem – tento symlink bude vždy ukazovat na správnou kartu a proto budeme moci jeho jméno použít pro stabilní konfiguraci mythbackendu. Druhá možnost je přemluvit udev, ať čísla nepřiřazuje podle pořadí detekování, tedy jak jsem předvedl výše v podstatě náhodně, ale ať dává konkrétním zařízením stále stejná čísla.

První možnost – symbolická jména

Nechci tady zabíhat moc podrobně do konfigurace udev, která byla jistě popsána detailně už dříve, takže jen naznačím potřebné kroky pro vytvoření stabilního jména dálkového ovladače pro LIRC konfiguraci:

1) ve /var/log/syslog (nebo ve výstupu příkazu dmesg) si najdeme, jak se ta TV karta (či jiné vstupní zařízení) jmenuje právě teď – třeba moje TeVii S460, jejíž dálkové ovládání aktuálně používám, dnes nabootovala pod názvem /dev/dvb/adap­ter2/frontend0.

2) požádáme udev, ať nám vypíše všechny informace, co o tomto zařízení zná (výpis jsem pro potřeby článku zkrátil). A pokud udevinfo už nemáte (protože máte hodně nový udev díky nové distribuci), použijte místo něj na obou místech následujícího řetězce příkazů  udevadm info:

# udevinfo -a -p $(udevinfo -q path -n /dev/input/event5)
  looking at device '/devices/pci0000:00/0000:00:06.0/0000:01:07.2/input/input5/event5':
    KERNEL=="event5"
    SUBSYSTEM=="input"
    DRIVER==""

  looking at parent device '/devices/pci0000:00/0000:00:06.0/0000:01:07.2/input/input5':
    KERNELS=="input5"
    SUBSYSTEMS=="input"
    DRIVERS==""
    ATTRS{name}=="cx88 IR (TeVii S460 DVB-S/S2)"
    ATTRS{phys}=="pci-0000:01:07.2/ir0"
    ATTRS{uniq}==""
    ATTRS{modalias}=="input:b0001vD460p9022e0001-e0,1,14,k71,72,73,74,86,8B,9E,A4,A7,A8,D0,E3,160,166,167,16C,16D,16E,172,174,182,188,18B,192,193,195,196,ramlsfw"

3) najdeme si některý z atributů, který je dostatečně unikátní a popisuje právě to zařízení, které chceme identifikovat a vytvořit pro něj stabilní symbolické jméno. Protože mám v systému právě jednu TeVii kartu, stačí mi se chytit atributu „name“, abych dálkové ovládání identifikoval jednoznačně. Mít těch karet víc stejných, asi bych se musel chytit ještě třeba čísla PCI slotu (toho „pci-0000:01:07.2“), protože ten se taky při restartech normálně nemění (pokud tedy karty nepřehazuji přímo fyzicky v počítači ze slotu do slotu).

4) vytvoříme nové pravidlo pro udev – v Debianu v adresáři /etc/udev/rules.d/ založíme soubor nazvaný třeba 10-local.rules (v jiných operačních systémech to bude nejspíš velmi podobné až úplně stejné) a do něj napíšeme

KERNEL=="event?", ATTRS{name}=="cx88 IR (TeVii S460 DVB-S/S2)", SYMLINK+="input/IR-TeVii"

a restartujeme systém, aby udev znovu nadetekoval všechna zařízení (alternativou k restartu by bylo odstranit a znovuzavést ovladač daného zařízení, ale vzhledem k závislostem mezi kernel moduly bude restart celého systému kolikrát nejrychlejší a nejjednodušší).

Zápis toho udev pravidla je jednoduchý a snad nejen pro programátory lehce pochopitelný. Výsledkem je stabilní symbolické jméno /dev/input/IR-TeVii, ideální pro použití v konfiguraci LIRC:

# ls -l /dev/input/
total 0
drwxr-xr-x 2 root root      60 2010-02-08 07:42 by-id
drwxr-xr-x 2 root root     120 2010-02-08 07:42 by-path
crw-rw---- 1 root root  13, 64 2010-02-08 07:42 event0
crw-rw---- 1 root root  13, 65 2010-02-08 07:42 event1
crw-rw---- 1 root root  13, 66 2010-02-08 07:42 event2
crw-rw---- 1 root root  13, 67 2010-02-08 07:42 event3
crw-rw-r-- 1 root video 13, 68 2010-02-08 07:42 event4
crw-rw-r-- 1 root video 13, 69 2010-02-08 07:42 event5
crw-rw---- 1 root root  13, 70 2010-02-08 07:42 event6
lrwxrwxrwx 1 root root       6 2010-02-08 07:42 IR-TeVii -> event5
crw-rw---- 1 root root  13, 63 2010-02-08 07:42 mice

Pro ilustraci uvádím níže zbytek obsahu mého 10-local.rules, který vytváří stabilní jména pro dvě dříve používané analogové televizní karty – Pinnacle PCTV Pro a Hauppauge WinPVR 150:

KERNEL=="video?", BUS=="pci", SYSFS{subsystem_device}=="0x0012", SYMLINK+="video-pctv"
KERNEL=="vbi?", BUS=="pci", SYSFS{subsystem_device}=="0x0012", SYMLINK+="vbi-pctv"
KERNEL=="video?", BUS=="pci", SYSFS{subsystem_device}=="0x8003", SYMLINK+="video-pvr150"
KERNEL=="vbi?", BUS=="pci", SYSFS{subsystem_device}=="0x8003", SYMLINK+="vbi-pvr150"

V konfiguraci mythbackendu jsem tedy měl místo anonymního a nejistého /dev/video0 rovnou jasné a stabilní /dev/video-pctv a /dev/video-pvr150. Ta vbi jména jsou pro čtení teletextu, který se v mythbackendu u analogových karet konfiguruje zvlášť.

Druhá možnost – stabilní čísla zařízení

Druhá možnost je poněkud méně elegantní na konfiguraci, ale pro satelitní digitální karty je vhodnější, jak si ukážeme, až se dostaneme k dekódování signálu. Stejně tak to může být jediná rozumná možnost v případě, že máme kartu s dvojitým tunerem, anebo třeba dvě naprosto identické karty, které udev jednoznačně a spolehlivě nerozliší. V takovém případě můžeme zkusit požádat přímo ovladač/kernelový modul daného zařízení, aby udev přiřadil konkrétní číslo, a to s pomocí parametru adapter_nr. Nejdříve proto musíme zjistit, který kernelový modul vlastně řídí naše TV karty.

Kombinací příkazů dmesg a lsmod se dopátráme:

# dmesg | grep DVB
[    8.395388] dvb-usb: found a 'AVerMedia AVerTV DVB-T Volar X' in warm state.
[    8.396018] DVB: registering new adapter (AVerMedia AVerTV DVB-T Volar X)
[    9.289273] DVB: registering adapter 0 frontend 0 (Afatech AF9013 DVB-T)...
[    9.472701] DVB: registering new adapter (FlexCop Digital TV device)
[    9.491565] input: IR-receiver inside an USB DVB receiver as /devices/pci0000:00/0000:00:02.1/usb2/2-2/input/input4
[    9.491680] dvb-usb: AVerMedia AVerTV DVB-T Volar X successfully initialized and connected.
[    9.772651] b2c2-flexcop: found 'ST STV0299 DVB-S' .
[    9.772693] DVB: registering adapter 1 frontend 5505090 (ST STV0299 DVB-S)...
[    9.775480] cx88[0]: subsystem: d460:9022, board: TeVii S460 DVB-S/S2 [card=70,autodetected], frontend(s): 1
[   10.164100] input: cx88 IR (TeVii S460 DVB-S/S2) as /devices/pci0000:00/0000:00:06.0/0000:01:07.2/input/input5
[   10.190782] cx88[0]/2: subsystem: d460:9022, board: TeVii S460 DVB-S/S2 [card=70]
[   10.190828] cx88[0]/2: cx2388x based DVB/ATSC card
[   10.238687] DVB: registering new adapter (cx88[0])
[   10.238728] DVB: registering adapter 2 frontend 0 (Conexant CX24116/CX24118)...

Naši kandidáti jsou „dvb-usb“ pro AverTV Volar X, „b2c2-flexcop“ pro Technisat SkyStar2 a „cx88“ pro TeVii S460. Ještě najdeme skutečná jména kernel modulů ve výpisu lsmod (mlčky předpokládám, že používáme modulární, nikoliv monolitický kernel). Pokud máme kandidátů víc, tak příkazem modinfo jmenomodulu kontrolujeme, který z modulů má pro nás klíčový parametr adapter_nr.

# lsmod | grep dvb_core
dvb_core               75592  6 dvbloopback,cx88_dvb,videobuf_dvb,stv0299,b2c2_flexcop,dvb_usb

V závislostech modulu „dvb_core“ najdeme jména kernel modulů, která jsme hledali: „dvb_usb“, „b2c2_flexcop“ a „cx88_dvb“. „dvb_usb“ ještě není ten pravý ořechový, ale další lsmod to vyjasní:

# lsmod | grep dvb_usb
dvb_usb_af9015         20880  0
dvb_usb                14572  1 dvb_usb_af9015

Pro kontrolu:

# modinfo dvb_usb_af9015 b2c2_flexcop cx88_dvb | grep adapter_nr
parm:           adapter_nr:DVB adapter numbers (array of short)
parm:           adapter_nr:DVB adapter numbers (array of short)
parm:           adapter_nr:DVB adapter numbers (array of short)

A teď už zbývá jen vybrat a přiřadit stabilní čísla, nejlépe nějaká vyšší než nula, abychom nenarazili na kolizi, až příště přidáme další DVB zařízení (stačí totiž třeba jen zastrčit USB kameru do multimediálního centra, nebo další USB tuner a DVB číslování se opět celé posune). V adresáři /etc/modprobe.d/ proto vytvoříme soubor nazvaný třeba dvb a do něj napíšeme (příklad pro moje karty):

options b2c2-flexcop adapter_nr=5
options cx88_dvb adapter_nr=6
options dvb_usb_af9015 adapter_nr=7

Tím vlastně přidáme parametr adapter_nr ke třem modulům, což by se mělo po restartu systému projevit:

# reboot
# dmesg | grep "registering adapter"
[    9.572115] DVB: registering adapter 7 frontend 0 (Afatech AF9013 DVB-T)...
[   10.064682] DVB: registering adapter 5 frontend 0 (ST STV0299 DVB-S)...
[   10.509536] DVB: registering adapter 6 frontend 0 (Conexant CX24116/CX24118)...

Mimochodem, adapter_nr očekává pole čísel, takže pokud nám jeden kernelový modul řídí vícero karet, zapíšeme plánovaná čísla jednoduše oddělena čárkami, například:

options cx88_dvb adapter_nr=3,4,5

Tak a je to – zbývá opravit existující konfiguraci MythTV, což nejlépe provedeme tak, že v mythtv-setup  zrušíme úplně všechny karty a  začneme znovu. Hackeři mohou přímo editovat tabulku capturecard databáze mythconverg a ručně tam jména karet poměnit (za případné následné bolesti hlavy si pak mohou sami).

Pro kontrolu výpis konfigurace mythbackendu přímo z MySQL databáze:

root_podpora

# echo "SELECT DISTINCT videodevice FROM capturecard" | mysql mythconverg
videodevice
/dev/dvb/adapter5/frontend0
/dev/dvb/adapter6/frontend0
/dev/dvb/adapter7/frontend0

A to je vše – WAF by měl být nyní vysoký a nahrávky 100% spolehlivé. Za pointer na správné řešení stabilních čísel děkuji Milošovi.

Příště

Příště si povíme něco o tom, o čem se veřejně na internetu (především na mythtv.org) moc nemluví – kde a jak získat kvalitní televizní program, a taky jak legálně dekódovat české/slovenské satelitní televizní programy.

Byl pro vás článek přínosný?

Autor článku

Petr Stehlík vystudoval aplikovanou informatiku a pracuje jako vývojář webových aplikací a administrátor linuxových serverů. Provozuje vlastní server tvpc.cz.