O sběrnici PCI
PCI (Peripheral Component Interconnect) je počítačová sběrnice pro připojování vnitřních periférií. Byla navržena na počátku 90. let 20. století a první specifikace se dočkala v roce 1992. Cílem jejího vytvoření bylo nahradit zastaralou sběrnici ISA (která mimochodem přežila dodnes, byť jen u průmyslových počítačů) a celou škálu různých sběrnic trpících všelijakými neduhy (EISA, MCA, VL-BUS atd.).
Zařízení pro sběrnici PCI mají obvykle podobu karty do příslušného slotu, ovšem nemalý je též podíl zařízení integrovaných do základní desky. Z programátorského pohledu je zcela lhostejné, o který způsob připojení se jedná (resp. to může mít vliv maximálně z hlediska řízení spotřeby).
Důležité jsou parametry sběrnice – datová šířka 32 (skoro vždy) nebo 64 bitů, 32bitový adresní prostor paměti i portů (u novějších počítačů lze používat i 64bitovou adresaci paměti), geografická adresace zařízení (zařízení je na sběrnici identifikováno slotem, kam je připojeno), přenosový kmitočet 25 až 133 (většinou 33 nebo 66) MHz.
PCI není zdaleka jen doménou procesorové platformy x86, používá se i na systémech Alpha, SPARC64, PowerPC a dalších. Existují již rychlejší a modernější sběrnice, které PCI pozvolna nahrazují (AGP, PCI-X, PCI Express), ale zřejmě ještě dlouho se bude v počítačích intenzivně používat.
PCI ze softwarového pohledu
Základní identifikační jednotkou je funkce zařízení (logické zařízení). Jedno fyzické zařízení jich může mít až 8 (většinou jen 1, ale běžně více). Na každé sběrnici může být až 32 zařízení. Sběrnice se sdružují do domén, každá může obsahovat max. 256 sběrnic. Osobní počítače mívají typicky dvě sběrnice, velké systémy jich ovšem mohou mít klidně desítky až stovky.
Paměťový a portový adresní prostor je v rámci jedné sběrnice sdílený. Specifické jsou však konfigurační prostory (o velikosti 256 bajtů) přístupné přes geografickou adresaci. Každé zařízení má k dispozici čtyři linky pro přerušení. Přerušení lze sdílet.Každé PCI zařízení se musí před použitím nakonfigurovat – namapovat jeho adresní prostor do adresního prostoru procesoru. O to se ovšem postará BIOS nebo PCI subsystém jádra, proto se tím vývojář ovladače nemusí zabývat.
Indikace podporovaných zařízení
Modul s ovladačem PCI zařízení musí poskytovat informace o tom, která zařízení podporuje – to proto, aby bylo možné v jádře i v uživatelském prostoru (např. v programu HAL) zjistit, která zařízení umí modul obsluhovat. Tyto informace jsou reprezentovány identifikátorem, jejž lze přečíst ze registrů zařízení. Identifikátor má vždy nejméně tři složky: vendorID (identifikace výrobce), deviceID (identifikace zařízení) a class (třída zařízení; má dvě části, skupinu a samotnou třídu). Může obsahovat ještě dvě další složky.
U PCI registrů se pracuje zásadně s pořadím bajtů little-endian. Na platformách, kde se nativně používá opačné pořadí, je nutno provádět potřebné konverze. Některé funkce (například pro přístup ke konfiguračním registrům) konverzi provádějí automaticky.
Identifikátor se ukládá do struktury pci_device_id. Přestože jsou složky 16bitové, má každá hodnota struktury velikost 32 bitů. Je to výhodné kvůli rezervě do budoucna, ale je potřeba to mít na paměti. Strukturu není třeba vyplňovat ručně, máme k dispozici dvě makra – PCI_DEVICE a PCI_DEVICE_CLASS. Příklad ještě chvíli počká, bude uveden později.
K exportu navenek slouží makro MODULE_DEVICE_TABLE. Předá se mu kategorie zařízení a seznam výše uvedených struktur, makro pak způsobí, že jsou informace dostupné zvenku. Následně si je přečte program depmod a uloží do souboru modules.pcimap. Odtud pak čerpá program HAL nebo jiné další, aby na základě získaného identifikátoru vybraly modul, který bude načten. Zde je slíbený příklad:
static struct pci_device_id s_dev_ids[] = { { PCI_DEVICE_CLASS(PCI_CLASS_NETWORK_ETHERNET, ~0), PCI_DEVICE(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139) }, { 0, } }; MODULE_DEVICE_TABLE(pci, s_dev_ids);
Jak je vidět na první pohled, příklad by mohl být součástí ovladače síťové karty Realtek 8139. Třída odpovídá zařízení typu Ethernet, identifikátory výrobce a zařízení přímo popisují zmíněnou kartu. Pozor – implementace použitých maker je taková, že pokud se uvádějí obě uvedená, musí se PCI_DEVICE_CLASS uvést jako první, jinak by přepsalo důležité položky struktury.
Základní operace
Než lze cokoliv dělat, než lze vůbec ovladač zaregistrovat v PCI subsystému, musí se připravit příslušná datová struktura. Je to obdobná záležitost jako v jiných podobných případech – struktura obsahuje název ovladače, ukazatel na strukturu s identifikátorem (viz výše) a ukazatele na řadu funkcí, které subsystém volá. Není tu bohužel místo pro jejich detailnější popis, takže jen stručně: je tam funkce probe (ověřuje, zda je ovladač schopen obsluhovat určité zařízení), remove (volá se při odstraňování zařízení nebo ovladače) a potom řada funkcí souvisejících s řízením spotřeby (nemusí se implementovat, není-li pro ně využití).
Máme-li strukturu připravenou, lze ji zaregistrovat. To se provede zavoláním pci_register_driver() v inicializační funkci modulu. Odstranění se pak zcela logicky vyvolá funkcí pci_unregister_driver(). Aby bylo možné PCI zařízení používat, musí se ještě aktivovat funkcí pci_enable_device(). Tato funkce se zavolá uvnitř funkce nastavené ve struktuře ovladače pro operaci probe.
Konfigurace
I když běžně není potřeba v ovladači konfigurovat PCI zařízení, někdy je to nutné. Ale hlavně, z konfiguračních registrů potřebujeme získávat důležité informace. Proto je nutné používat mechanismy pro přístup do zmíněných registrů.
Existují v zásadě dvě skupiny funkcí – první využívají přímo strukturu pci_dev, k níž máme obvykle přístup (ve funkcích jako probe atd.), druhé pak strukturu pci_bus a číslo zařízení. Tyto druhé funkce se používají jen ve speciálních případech.
Pro čtení a zápis máme k dispozici tři dvojice funkcí rozlišené podle velikosti registrů (8, 16 nebo 32 bitů). Funkce automaticky zajišťují konverzi pořadí bajtů, takže se tím již nemusíme zabývat. Následující příklad ukazuje, jak se funkce používají:
u16 status; if (pci_read_config_word(dev, PCI_STATUS, &status) == 0) { // vyhodnocení stavu }
V příkladu se přečte stav zařízení. Veškeré konfigurační funkce obecně mohou selhat, proto se musí vždy testovat jejich návratová hodnota.
Přenos dat
Data mezi PCI zařízením a ovladačem lze přenášet obvyklým způsobem – tedy tak, jak bylo popsáno v kapitole o komunikaci se zařízením (přes porty a paměť zařízení). Je tu jen několik detailů, které je třeba si ujasnit.
Prvním z nich jsou adresy paměťových a portových oblastí (může jich být až 6). Ty lze získat z konfiguračních registrů výše uvedeným způsobem. Je to ale zbytečně komplikované, jádro poskytuje jednodušší metodu. Jsou jí funkce pci_resource_start() a pci_resource_end(), přijímající kromě ukazatele na strukturu pci_dev také číslo oblasti (0-5). První funkce vrací začátek oblasti, druhá konec (jde o interval zdola uzavřený, shora otevřený). Funkce pci_resource_len() zjišťuje velikost oblasti.
Dále nás ještě zajímá, jaké vlastnosti daná oblast má – zda jde o paměť či porty, zda lze jen číst nebo i zapisovat atd. K tomu slouží další podobná funkce, a to pci_resource_flags(). Pomocí bitových masek (např. IORESOURCE_MEM, IORESOURCE_READONLY) pak testujeme vrácenou hodnotu na jednotlivé vlastnosti.
Přerušení a DMA
Používat přerušení je u PCI zařízení velmi jednoduché. Není třeba řešit přiřazení IRQ, to totiž udělá BIOS či jiný firmware, případně jádro samotné. Jde tedy jen o to zjistit, jaký je aktuální stav (jaký kanál je přiřazen). Je to snadné, stačí přečíst 8bitový konfigurační registr PCI_INTERRUPT_LINE. Pak už se normálně nastaví obslužná rutina, jako u jiných druhů zařízení.
Co se týká DMA přes PCI, z drtivé většiny platí to, co bylo řečeno pro DMA obecně. Jen dvě věci stojí za zmínku. Jednak to, že standardně se používá 32bitová sběrnicová adresa. Proto, pokud se pracuje na 64bitové platformě, musí být paměť pro DMA alokována v části fyzické paměti dosažitelné přes tyto adresy (to je ale obecná podmínka pro DMA). A zadruhé, lze používat 64bitový režim adresace (adresace nadvakrát – Double-Address Cycle, DAC). Je však pomalý a pokud ho vyloženě nepotřebujeme, není důvod k jeho používání.
Sběrnice USB. Druhou ze dvou nejpoužívanějších sběrnic je dnes bezpochyby USB. Na rozdíl od PCI se používá pro vnější periférie. I tato sběrnice má svá specifika, která si zaslouží zvláštní zmínku. Proto na ni dojde v příštím dílu seriálu.