GPIO kernelio modulis
Šiandien anksti ryte pabudęs supratau, kad jums, mieli skaitytojai, gyvenime kažko trūksta. Pamąsčius paaiškėjo, kad trūkti gali tik vieno - mirksinčio LED. Aš nebūčiau aš, jeigu neatskubėčiau jums į pagalbą. Šiandien parašysiu kaip pamirksinti LEDą iš kernelio modulio.
Teorija
Tai nebepirma mūsų pažintis su kernelio draiveriais: jau išmokome parašyti paprastą labas pasauli ir SPI modulius. Šį kartą išmoksime naudotis GPIO interfeisu ir taimeriais. Tiesą sakant GPIO interfeisui jau yra sukurta nemažai kernelio tvarkyklių, tad naujos rašymas naudingas tik mokymosi tikslams. Jeigu tenori vien tik pamirksinti LEDą, siūlyčiau pasidomėti gpio-leds moduliu, kuris sukurs /sys/class/leds/ledo-pavadinimas/ ‘sysfs’ interfeisą, kuriame bus galima nurodyti kaip mirksėti: pagal CPU naudojimą, sistemos apkrovimą, HDD naudojimą, CPAS LOCK būseną ar pan.
GPIO interfeisas
Yra du GPIO interfeisai - pasenęs (gpio_) ir naujas (gpiod_). Taip pat yra ir pagalbinės funkcijos perėjimui iš senojo interfeiso į naująjį.
Senasis interfeisas
Šio API naudoti nerekomenduojama, nes jis pasenęs. Taip pat kyla problemų turint ne vieną GPIO kontrolerį, o ypač juos pajungiant sistemai veikiant (hotplug). Kadangi pagal seną interfeisą GPIO numeruojami nuo 0 iki kiek yra per visus kontrolerius, gali būti sudėtinga atsirinkti kuris numeriukas kurį GPIO junginėja.
Antrą vertus naujasis interfeisas gan panašus į senąjį, tad ieškant informacijos galima užmesti akį ir į jį. Visai geras senojo aprašymas: http://derekmolloy.ie/kernel-gpio-programming-buttons-and-leds/ .
Naujasis interfeisas
Naujasis interfeisas geras tuo, kad nebenaudojami GPIO numeriai, o moduliui reikalingi GPIO aprašomi iš karto device-tree. Arba jeigu kas nors pasistengė į device-tree surašyti visus GPIO pavadinimus, užteks ir pavadinimo.
Dar vienas pliusas - naudojant metodus prasidedančius devm_ nebereikės sukti galvos apie resursų atlaisvinimą - jie bus automatiškai atlaisvinti atjungus modulį arba jam nulūžus.
Apie kitus privalumus išgirsite pažiūrėję youtube prezentaciją ar pasiskaitinėję skaidres.
Pereinamasis interfeisas
Pereinamasis interfeisas naująjį papildo dviem funkcijom, susiejančiomis gpio_desc objektą su senojo interfeiso GPIO numeriuku. Jis naudotinas tik perrašinėjant senas tvarkykles.
Papildomos funkcijos:
- int desc_to_gpio(const struct gpio_desc *desc)
- struct gpio_desc *gpio_to_desc(unsigned gpio)
Taimerių interfeisas
Linuxe yra visokiausių taimerių, bet mes naudosime high-resolution timerį, nes jis man labiausiai patiko. Labai trumpas ir gražus pavyzdys: https://stackoverflow.com/a/20303613
Įrenginio tvarkyklė
Iki šiol mes rašime paprastus kernelio modulius, kurie nebuvo tikros įrenginių tvarkyklės. Šį kartą rašysime tvarkyklę (draiverį) LEDui.
Visa laimė, kad gerieji linux žmonės jau parašė pagalbines funkcijas, kurias naudojant dėl nieko nereikės sukti galvos: bus pasirūpinta device-tree paieška, atminties atlaisvinimu tiek atjungiant modulį, tiek jam nulūžus ir visom kitom smulkmenomis.
Tiesiog vietoj module_init() ir module_exit() reikės naudoti module_platform_driver.
container_of
container_of yra macro, kuri gali iš struktūros laukelio pasiimti pačia struktūrą. Labai naudinga macro naudojantis callback‘ais ir norint perduoti daugiau duomenų, nei įmanoma paduoti per parametrus. Detaliau su ja susipažinsime praktiniame pavyzdyje.
Praktika
Praktika susidės iš dviejų dalių: device-tree ir tvarkyklės kodo. Panagrinėkime kiekvieną atskirai:
device-tree
Per device-tree mes nurodysime kurį GPIO kontaktą (pin) naudoti. Tam į pagrindinę sekciją įdedame tokią atšaką:
1 | ledo_lempute { |
Pavadinimas ‘ledo_lempute’ visiškai nesvarbus. Svarbu tik, kad nesikirstų su jau panaudotais pavadinimais. Toliau įrašome ‘compatible’, pagal kurį mūsų tvarkyklė ir susiras šią atšaką. ‘led-gpios’ nurodome kurį GPIO naudosime. Šiam pavyzdžiui pasirinkau PI11 išvadą. P kontroleris šiame device-tree aprašyras kaip pio, o raidė I skaičiuojant nuo 0 yra aštunta, todėl ir rašome <&pio 8 11 >. GPIO_ACTIVE_HIGH reiškia, kad jeigu kode išvestį nurodysime kaip 1, tai ir fiziniame kontakte bus aukštas signalas. Jeigu būtume nurodę atvirkščiai - GPIO_ACTIVE_LOW - nurodžius vienetą LEDas užgestų.
Tvarkyklė
Tvarkyklę aprašiau komentaruose pačiame kode.
1 |
|
Bandymai
Norėdami paleisti mūsų tvarkyklę iš pradžių sukompiliuojame naująjį device-tree: įmetame jį prie kitų kernelio device-tree į arch/arm/boot/dts/ ir iš pagrindinio kernelio katalogo paleidžiame:
1 | make sun7i-a20-pcduino3b.dtb |
Tada sukompiliuotą failą arch/arm/boot/dts/sun7i-a20-pcduino3b.dtb perkeliame į kompiuteriuką ir nustatome, kad įjungiant būtų naudojamas mūsiškis. Paprasčiausia tiesiog pakeisti senąjį ‘*.dtb’ failą naujuoju.
Perkrovus kompiuteriuką perkeliame ir sukompiliuojame savo tvarkyklę, prijungiame LEDą ir aktyvuojame sukompiliuotą modulį:
1 | insmod mirksiukas.ko |
Ir štai rezultatas:
Kaip visada, kodą rasite github’e.