Kommunikation zwischen Treiber und Gerät über die _HID ACPI-Methode am Beispiel des GPIO des Lynxpoint-Controllers

Erklärung des Problems


Linux verfügt über eine Standardschnittstelle für die Arbeit mit GPIO über sysfs. Die Dokumentation dazu finden Sie hier .

Kurz gesagt, im Ordner "/ sys / class / gpio" befinden sich "Export" - und "Nicht-Export" -Dateien. Durch Schreiben der Nummer X in die Exportdatei können Sie die Benutzeroberfläche im Benutzerbereich öffnen, um GPIOX zu steuern

#    user space   GPIO12 $ echo 12 > /sys/class/gpio/export 

Nach dem Öffnen der Benutzeroberfläche wird der Ordner / sys / class / gpio / gpioX / angezeigt, in dem sich Dateien wie "Wert" oder "Richtung" befinden. Schreiben Sie "In" oder "Out" in die Datei "Richtung" und schreiben Sie 1 oder 0 in die Datei "Wert" kann die GPIO-Ausgabe direkt über die Befehlszeile steuern.

 #  GPIO   $ echo "out" > /sys/class/gpio/gpio12/direction #  1   GPIO $ echo 1 > /sys/class/gpio/gpio12/value 

Damit der Befehl "echo X> / sys / class / gpio / export" den Ordner "gpioX" erstellen kann, muss der GPIO-Controller-Treiber im Kernel registriert sein, wodurch die Schnittstelle zu den GPIO-Leitungen geöffnet wird.

Zufällig arbeite ich daran, Coreboot für ein benutzerdefiniertes Board zu portieren, das auf dem Intel Haswell i7-Prozessor basiert. [Für diejenigen, die es nicht wissen, ist Coreboot ein Open Source-Open-Source-BIOS-Projekt ( https://www.coreboot.org/ ). ]. Die LynxpointLP-Südbrücke mit 94 GPIO-Leitungen ist in meinen Prozessor integriert. Und ich wollte sie in sysfs öffnen ...

Problemlösung (Treiber- und Gerätekommunikation unter Linux)


Nach einer kurzen Suche im Kernel-Code stellte ich fest, dass der Treiber bereits geschrieben, in der Datei "drivers \ gpio \ gpio-lynxpoint.c" gespeichert und mit Kconfig aktiviert wurde

 config GPIO_LYNXPOINT tristate "Intel Lynxpoint GPIO support" depends on ACPI && X86 select GPIOLIB_IRQCHIP help driver for GPIO functionality on Intel Lynxpoint PCH chipset Requires ACPI device enumeration code to set up a platform device. 

Die Option GPIO_LYNXPOINT wurde in dem Kernel aktiviert, mit dem ich gearbeitet habe. Es gab jedoch keinen einzigen Ordner "gpiochipN" für den GPIO-Controller im Ordner "/ sys / class / gpio /" (der sein sollte), und selbst ein solches Skript führte nicht zum Export Linien.

 $ for i in {0..255}; do echo $i > /sys/class/gpio/export; done 

Wenn Sie sich den Coreboot-Code oder die Dokumentation für diese South Bridge ansehen, sehen Sie, dass der GPIO-Controller kein separates PCI-Gerät ist. Es ist Teil eines anderen PCI-Geräts: LPC Interface Bridge. Mithilfe der PCI-Konfigurationsbereichsregister dieses Geräts müssen Sie den GPIO-Controller aktivieren und ihm BASE_ADDRESS im E / A-Bereich zuweisen. Dadurch wird ein Fenster im 1-kV-E / A-Bereich geöffnet. Durch Schreiben / Lesen von Bytes in diesem Fenster können Sie die GPIO-Leitungen steuern.

Was wir im Coreboot-Code sehen können:

Southbridge \ Intel \ Lynxpoint \ pch.h:

 #define DEFAULT_GPIOBASE 0x1400 #define DEFAULT_GPIOSIZE 0x400 ... #define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */ #define GPIO_CNTL 0x4C /* LPC GPIO Control Register */ ... /* PCI Configuration Space (D31:F0): LPC */ #define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0) 

Southbridge \ Intel \ Lynxpoint \ Early_Pch.C:

 /* Setup GPIO Base Address */ pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); /* Enable GPIO functionality. */ pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10); 

Wenn wir uns die LPC-Geräteregister unter Linux über "lspci -xxx" ansehen, werden wir sehen, dass sich die von uns aufgezeichneten Daten in diesen Registern befinden. Also scheint alles so konfiguriert zu sein, wie es sollte.

Als ich den Treibercode weiter betrachtete, bemerkte ich, dass der Linux-Treiber über das Feld .acpi_match_table mit dem Gerät kommuniziert. Da unser Gerät nicht aufgelistet werden kann (es befindet sich weder auf der PCI noch auf dem USB-Bus), ist dafür ein Plattformtreiber erforderlich, und die Verbindung dieses Treibers mit dem Gerät erfolgt über ACPI-Tabellen. Im üblichen Fall für x86, im Fall von ARM, würden wir unser Gerät in DeviceTree registrieren oder die alten Hardcodes im Kernel verwenden.

Treiber \ gpio \ gpio-lynxpoint.c:

 static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = { { "INT33C7", 0 }, { "INT3437", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match); static struct platform_driver lp_gpio_driver = { .probe = lp_gpio_probe, .remove = lp_gpio_remove, .driver = { .name = "lp_gpio", .pm = &lp_gpio_pm_ops, .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match), }, }; 

Dies funktioniert folgendermaßen: Wenn der Kernel beim Parsen der ACPI-Tabelle ein Gerät mit der _HID-Kennung „INT33C7“ sieht, versucht er, den Plattformtreiber dafür mit übereinstimmenden Bezeichnern in den Feldern der Struktur „.driver-> acpi_match_table“ zu finden.

Wenn eine Übereinstimmung gefunden wird, führt Linux die .probe-Treiberfunktion aus.

Wie sich herausstellte, wurde der ACPI-Code für dieses Gerät in coreboot dargestellt, ich habe ihn nur auskommentiert. Auskommentiert aufgrund der Tatsache, dass Windows für dieses Gerät den Treiber nicht finden konnte und im Geräte-Manager "Unbekanntes Gerät" angezeigt wurde. Mehr dazu weiter unten.

Wir sind also an den Informationen aus der Datei interessiert
src \ southbridge \ intel \ lynxpoint \ acpi \ serialio.asl (der Code ist etwas vereinfacht):

 /*     * src\southbridge\intel\lynxpoint\pch.h * #define DEFAULT_GPIOBASE 0x1400 * #define DEFAULT_GPIOSIZE 0x400 */ Scope (\_SB) { Device (PCI0) { ... Device (GPIO) { // GPIO Controller Name (_HID, "INT33C7") Name (_CID, "INT33C7") Name (_UID, 1) Name (RBUF, ResourceTemplate() { DWordIo (ResourceProducer, MinFixed, // IsMinFixed MaxFixed, // IsMaxFixed PosDecode, // Decode EntireRange, // ISARanges 0x00000000, // AddressGranularity 0x00000000, // AddressMinimum 0x00000000, // AddressMaximum 0x00000000, // AddressTranslation 0x00000001, // RangeLength , // ResourceSourceIndex , // ResourceSource BAR0) Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, , , ) {14} }) Method (_CRS, 0, NotSerialized) { CreateDwordField (^RBUF, ^BAR0._MIN, BMIN) CreateDwordField (^RBUF, ^BAR0._MAX, BMAX) CreateDwordField (^RBUF, ^BAR0._LEN, BLEN) Store (DEFAULT_GPIOSIZE, BLEN) Store (DEFAULT_GPIOBASE, BMIN) Store (Subtract (Add (DEFAULT_GPIOBASE, DEFAULT_GPIOSIZE), 1), BMAX) Return (RBUF) } Method (_STA, 0, NotSerialized) { Return (0xF) } } ... } } 

Um diesen Code im Detail zu verstehen, sollten Sie sich mit der ASL-Syntax in der ACPI-Spezifikation vertraut machen.

Kurz gesagt, dieser Code erstellt ein Gerät mit der Kennung "INT33C7", das über zwei Ressourcen verfügt:

 I/O memory: 1400-17ff; IRQ: 14; 

Innerhalb seiner .probe Linux-Funktion erhält der Treiber die oben genannten Geräteressourcen wie folgt:

 io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 

Basierend auf diesen Daten füllt der Treibercode die Struktur gpio_chip aus und registriert den gpio-Controller im System, wodurch er über die sysfs-Schnittstelle zugänglich wird.

Nachdem der ASL-Code des Geräts zurückgegeben und das BIOS-Image neu kompiliert wurde, gelang es dem System, über sysfs auf das GPIO zuzugreifen.

Zu Beginn wurde der Ordner "gpiochip162" in / sys / class / gpio angezeigt. Dieser Ordner enthält die Dateien "base" und "ngpio". Die Basisdatei ist für die Nummer des ersten GPIO dieses Controllers verantwortlich, ngpio für deren Nummer.

 $ cat /sys/class/gpio/gpiochip162/base 162 $ cat /sys/class/gpio/gpiochip162/ngpio 94 

Somit wurde alles so exportiert, wie es sollte. Wir führen das Skript aus:

 $ for i in {162..255}; do echo $i > /sys/class/gpio/export; done 

Danach werden die gpioN-Unterordner im Ordner / sys / class / gpio / angezeigt, in dem sich Dateien befinden, mit denen der Status der Zeile gesteuert werden kann.

Ein paar Kommentare:

  • Der Ordner / sys / class / gpio162 / ist für die Verwaltung von GPIO0 verantwortlich, der Ordner / sys / class / gpio163 / ist für GPIO1 verantwortlich Diese Verschiebung erfolgte aufgrund der Tatsache, dass der Treiber während der Initialisierung der Kontrollstruktur "struct gpio_chip" "gc-> base = -1;" zugewiesen hat. Das heißt, ich habe den Kernel verlassen, um die Zahlen selbst auszuwählen. Dies ist im Allgemeinen nicht kritisch, aber es lohnt sich, sich daran zu erinnern.
  • Der Zugriff wird nur auf GPIO-Leitungen gewährt, die als GPIO konfiguriert sind, und nicht auf native Southbridge-Funktionen. Für solche Zeilen zeigt der Treiber Informationen in dmesg an: "gpio% d reserviert für ACPI". Bei Coreboot wird das GPIO in der Datei „gpio.h“ im Ordner mit dem Motherboard konfiguriert.
  • Das Gerät und der Treiber können auch mithilfe der _CID-Methode (Compatible ID) zugeordnet werden. Die Dokumentation zu unserem Thema im Kernel finden Sie im Dokument „ACPI-basierte Geräteaufzählung“.

Es ist anzumerken, dass das INT33C7-Gerät nicht über zwei proprietäre Motherboards auf demselben Chipsatz in den ACPI-Tabellen verfügt (von IBASE und DFI). Es stimmt, es werden höchstwahrscheinlich keine GPIO-Leitungen ausgegeben (ich habe mir die Dokumentation zu diesem Zeitpunkt nicht im Detail angesehen).

Kennung "INT33C7"


Nachdem ich die sysfs-Funktionalität erhöht hatte, hatte ich eine Frage, woher die Identifikationsnummer „INT33C7“ stammt.

Nach Durchsicht der Dokumentation zur _HID-Methode wurde klar, dass es sich lohnt, einen Blick auf http://www.uefi.org/PNP_ACPI_Registry zu werfen

_HID (Hardware ID)
_HID (Hardware ID)
Dieses Objekt wird verwendet, um OSPM mit der PNP-ID oder ACPI-ID * des Geräts zu versorgen.
Bei der Beschreibung einer Plattform ist die Verwendung von _HID-Objekten optional. Ein _HID-Objekt muss jedoch sein
wird verwendet, um alle Geräte zu beschreiben, die von OSPM aufgelistet werden. OSPM zählt nur ein Gerät auf
wenn kein Bus-Enumerator die Geräte-ID erkennen kann. Beispielsweise sind Geräte an einem ISA-Bus
von OSPM aufgezählt. Verwenden Sie das _ADR-Objekt, um Geräte zu beschreiben, die von Bus-Enumeratoren aufgelistet werden
außer OSPM.

Argumente:
Keine

Rückgabewert:
Eine Ganzzahl oder Zeichenfolge, die die HID enthält
Ein _HID-Objekt wird entweder als numerische 32-Bit-komprimierte EISA-Typ-ID oder als Zeichenfolge ausgewertet. Wenn a
Zeichenfolge, das Format muss eine alphanumerische PNP- oder ACPI-ID ohne Sternchen oder andere führende Zeichen sein
Zeichen.

Eine gültige PNP-ID muss die Form "AAA ####" haben, wobei A ein Großbuchstabe und # ein Hex ist
Ziffer. Eine gültige ACPI-ID muss die Form "NNNN ####" haben, wobei N ein Großbuchstabe oder a ist
Ziffer ('0' - '9') und # ist eine hexadezimale Ziffer. Diese Spezifikation reserviert die Zeichenfolge "ACPI" nur zur Verwendung
mit Geräten definierte Liste. Es reserviert ferner alle Zeichenfolgen, die 4 HEX-Ziffern für darstellen
Ausschließliche Verwendung mit PCI-zugewiesenen Hersteller-IDs.

* -PNP ID und ACPI ID Registry finden Sie unter http://www.uefi.org/PNP_ACPI_Registry

Es gibt 3 Punkte auf diesem Link:

  • Hier sind alle Arten von 3-Buchstaben-Kennungen (PNP-ID) angegeben
  • Hier werden PNP-IDs angezeigt, die mit "PNP" beginnen, das von Microsoft reserviert wurde .
  • Hier werden alle Arten von 4-Buchstaben-Kennungen (ACPI-ID) angegeben

Es ist nicht ganz klar, warum, aber aus der PNP-ID-Liste können Sie entnehmen, dass die "INT" -Kennungen bei INTERPHASE CORPORATION reserviert sind:

 INTERPHASE CORPORATION INT 11/29/1996 

Anscheinend wird keine einzige Liste vollständiger Gerätekennungen (Buchstabenteil + digital) veröffentlicht. Mit Hilfe von Google konnten beispielsweise hier oder hier Listen von Geräten und deren _HID gefunden werden .

Sie zeigen an:

 INT33C7=Intel Serial I/O GPIO Host Controller 

Und nach dem Rest der Zeilen aus dieser Liste sind alle INTxxxx-Geräte Intel-Geräte (jetzt klingt es ziemlich offensichtlich, aber die Verbindung mit INTERPHASE CORPORATION ist immer noch nicht klar, es ist auch nicht sehr klar, warum die Nummerierung mit so großen Zahlen beginnt, aber sie ist sichtbar auf Intel Diskretion).

Kommunikationstreiber und Gerät in Windows


Nachdem ich meine Neugier befriedigt hatte, beschloss ich, Windows auf mein Board herunterzuladen. Wie erwartet konnte das System keinen Treiber für das Gerät finden. Es gab keine Hilfe von den Treibern für die IBASE- und DFI-Karten, was verständlich ist, da dieses Gerät im BIOS dieser Karten nicht angegeben ist.

Ich habe auf der Microsoft-Website einen Treiber gefunden

Dort wird dieser Treiber jedoch nur für Windows 8.1 und höher angezeigt. Ich arbeite immer noch mit Windows 7.

Trotzdem habe ich versucht, einen der Treiber herunterzuladen und seinen Ordner anzugeben, wenn ich nach einem Treiber für mein unbekanntes Gerät gesucht habe.

Der Dispatcher konnte den Treiber jedoch nicht dem Gerät zuordnen. Obwohl die Inf-Datei eindeutig Informationen über das INT33C7-Gerät enthielt.

 [Manufacturer] %INTEL%=Intel,NTamd64.6.3 [Intel.NTamd64.6.3] %iaLPSS_GPIO.DeviceDesc_LPT%=iaLPSS_GPIO_Device, ACPI\INT33C7 %iaLPSS_GPIO.DeviceDesc_WPT%=iaLPSS_GPIO_Device, ACPI\INT3437 

Beim Parsen der INF-Datei stellte sich heraus, dass im Abschnitt [Hersteller] eindeutig angegeben wurde, dass sie nicht für mein System bestimmt ist:

Was Intel.NTamd64.6.3 bedeutet, kann der Beschreibung entnommen werden:

 nt[Architecture][.[OSMajorVersion][.[OSMinorVersion] OSMajorVersion=6 => Windows 7/Windows 8.1/Windows Server 2012 R2/... OSMinorVersion=3 => Windows 8.1/Windows Server 2012 R2 

Der Versuch, den Windows 7-Treiber durch Ersetzen von Intel.NTamd64.6.3 durch Intel.NTamd64.6.1 zu pushen, schlug, gelinde gesagt, fehl, da ich einen Bluescreen des Todes und ein nicht bootfähiges Betriebssystem hatte und daher eine Wiederherstellung durchführen musste.

Der Treiber für Win7 wurde nur auf einer unverständlichen Website im Internet gefunden, und danach wird das Gerät im Geräte-Manager mit einem Ausrufezeichen angezeigt.

Als ich seine Ohnmacht erkannte, beschloss ich, die Funktionalität unter Windows 10 zu testen. Es gab eine angenehme Überraschung. Die Intel Chipset Device Software (INF Update Utility) hat den Treiber für meinen Controller problemlos installiert.



Wie Sie sehen können, verfügt dieses Gerät über die von uns angegebenen Ressourcen.



Theoretisch ist es nach der Installation des Treibers mit dem GPIO-Controller höchstwahrscheinlich möglich, IOCTL-Funktionen ( wie in diesem Dokument) zu verwenden .

Da es jedoch keine GPIO-Programmieraufgabe von Windows gab, wurde die Suche nach einem ähnlichen Dokument für meinen Chipsatz verschoben.



Fazit:


In diesem Artikel wurde die Verbindung zwischen dem Treiber und dem Gerät mithilfe der _HID ACPI-Methode untersucht. Eine solche Kommunikation kann auf einem x86-System für Geräte erforderlich sein, die nicht aufgelistet werden können.

  • Unter Linux erfolgt die Kommunikation mit dem Treiber über .acpi_match_table
  • Bei Windows erfolgt die Kommunikation mit dem Treiber über eine INF-Datei

Source: https://habr.com/ru/post/de429336/


All Articles