Anatomie eines Plattformtreibers unter Linux
Wie entsteht ein generischer GPIO-Treiber unter Linux? Um einen eigenen Plattformtreiber entwickeln zu können, sollte man sich einmal deren grundsätzlichen Aufbau näher betrachten.
Anbieter zum Thema

Der Linux-Kernel unterstützt Geräte für die verschiedensten Bus-Systeme, wie z.B. PCI, USB, I2C und viele mehr. Hierzu gibt es jeweils separate Subsysteme, welche neben den generellen Aufgaben wie z.B. Probing, Power-Management, diverse Highlevel-Funktionen/-Protokolle, etc, auch APIs für Backend- (z.B. PCI-Bridges, USB-HCDs, etc) und Frontend-Treiber (Geräte, die an einem bestimmten Bus angeschlossen sind) bieten.
Innerhalb des Linux-Treibermodells (LDM) sind alle Geräte an einem Bus zugeordnet. Nicht alle Geräte sind aber tatsächlich an einem generischen Bus wie z.B. PCI angeschlossen (z.B. in einem System-on-Chip integrierte PCI-Bridges) - die Treiber können hier auf kein generisches Subsystem zurückgreifen. Hierfür gibt es den virtuellen Plattform-Bus, über den diese Geräte verwaltet werden können. Die zugehörigen Treiber werden platform drivers genannt.
Eben jene wollen wir uns in diesem Artikel genauer anschauen. Als Beispiel dient uns hier das PCEngines APUv2-Board - hier benötigen wir Unterstützung für die Front-LEDs und einen Taster.
Bestandsaufnahme
Auf dem APUv2 sitzt ein AMD GX-412TC SoC, der eine Reihe GPIOs mitbringt. An einigen dieser hängen die drei Front-LEDs und der Taster.
Für GPIO-basierte LEDs und Tastaturen bringt Linux bereits generische Treiber mit - allerdings müssen diese zunächst gerätespezifisch konfiguriert bzw. initialisiert werden. Am GPIO-Treiber für den GX-412TC mangelt es jedoch noch.
Teil 1: GPIO-Treiber
Zunächst muss ein Treiber registriert werden, damit er von anderen Komponenten angefordert werden kann:
static struct platform_driver amd_fch_gpio_driver = {
driver = {
. .name = „gpio_amd_fch",
.},
..probe = amd_fch_gpio_probe,
};
...
module_platform_driver(amd_fch_gpio_driver);
MODULE_ALIAS("platform:gpio_amd_fch");
Mittels module_platform_driver() erzeugen wir Modul-Metadaten, anhand derer der Kernel den Treiber automatisch registriert, ohne dass wir noch eigene init-Routine schreiben müssten. MODULE_ALIAS() hilft modprobe
Schauen wir uns nun die probing-Routine an. Diese wird dann vom Kernel aufgerufen, wenn der Treiber initialisiert werden soll. Hier sollte normalerweise auch geprüft werden, ob die entsprechende Hawrdware auch vorhanden ist. In unserem Beispiel haben wir dazu keine Möglichkeit, da die Hawrdware derartiges nicht anbietet - die Konfiguration wird aber ohnehin von einem separaten Board-Treiber (siehe Teil 2) übernommen.
static int amd_fch_gpio_probe(struct platform_device *pdev)
{
struct amd_fch_gpio_priv *priv;
struct amd_fch_gpio_pdata *pdata = pdev->dev.platform_data;
int err;
if (!pdata) {
dev_err(&pdev->dev, „no platform_data\n");
return -ENOENT;
}
Der Kernel hat bereits eine 'struct platform_device'-Struktur allokiert und einige Felder ausgefüllt.
In pdev->dev.platform_data wird uns ein Zeiger auf eine treiberspezifische Konfiguration durchgereicht, die der Board-Treiber aus Teil 2 mitgibt.
if (!(priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL))) {
dev_err(&pdev->dev, „failed to allocate priv struct\n");
return -ENOMEM;
}
priv->pdata = pdata;
priv->pdev = pdev;
Hier allokieren wir etwas Speicher für private Daten. Das schöne an den devm_*()-Funktionen ist, dass alle zugewiesenen Ressourcen automatisch freigegeben werden, wenn das zugehörige Device wieder freigegeben wird - die früheren Aufräum-Orgien (z.B. wenn beim probe etwas schief geht) erübrigen sich.
if (IS_ERR(priv->base = devm_ioremap_resource(&pdev->dev, &priv->pdata->res))) {
dev_err(&pdev->dev, „failed to map iomem\n");
return -ENXIO;
}
Mittels devm_ioremap_resource() werden die Hardware-Register in den virtuellen Adressraum des Kernels eingeblendet. Die Parameter hierzu wurden vom Board-Treiber mitgegeben.
Anschließend folgen noch allerlei gerätespezifische Initialisierungen und die Registrierung von Devices in den jeweiligen Subsystemen (wie z.B. gpio, irq, etc). Dies verdient aber eigene Artikel, und soll hier nicht genauer besprochen werden.
:quality(80)/images.vogel.de/vogelonline/bdb/1402700/1402710/original.jpg)
Embedded Linux Grundlagen
Embedded Linux – Kernel, Aufbau, Toolchain
Zum Schluss speichern wir noch den Zeiger auf obige Treiber-private Daten und sind damit fertig.
platform_set_drvdata(pdev, priv);
return err;
}
Nun haben wir unseren generischen GPIO-Treiber, der dann vom Board-Treiber im Teil 2 konfiguriert wird.
(ID:45724613)