Aktivieren des Benutzermoduszugriffs auf GPIO, I2C und SPI

Unter Windows 10 und höher haben APIs im Benutzermodus direkten Zugriff auf General Purpose Input/Output (GPIO), Inter-Integrated Circuit (I2C), Serial Peripheral Interface (SPI) und Universal Asynchronous Receiver/Transmitter (UART). Entwicklungsplatinen wie Raspberry Pi 2 machen eine Teilmenge dieser Verbindungen verfügbar, mit denen Sie ein grundlegendes Compute-Modul mit benutzerdefiniertem Schaltkreis für eine bestimmte Anwendung erweitern können. Diese Feldbusse werden in der Regel mit anderen wichtigen integrierten Funktionen gemeinsam genutzt, wobei nur eine Teilmenge von GPIO-Kontakten und -Bussen auf Kontaktleisten verfügbar gemacht wird. Um die Systemstabilität zu erhalten, muss angegeben werden, welche Kontakte und Busse für Änderungen durch Benutzermodusanwendungen sicher sind.

In diesem Dokument wird beschrieben, wie diese Konfiguration in Advanced Configuration and Power Interface (ACPI) angegeben wird. Zudem finden Sie hier Tools, mit denen Sie überprüfen können, ob die Konfiguration richtig angegeben wurde.

Wichtig

Die Zielgruppe für dieses Dokument sind UEFI-Entwickler*innen (Unified Extensible Firmware Interface) und ACPI-Entwickler*innen. Grundlegende Kenntnisse in ACPI, der Erstellung von ASL-Dateien (ACPI Source Language) und SpbCx/GpioClx werden vorausgesetzt.

Für den Benutzermoduszugriff auf Feldbusse unter Windows werden die vorhandenen GpioClx- und SpbCx-Frameworks verwendet. Ein neuer Treiber namens RhProxy, der unter Windows IoT Core und Windows Enterprise verfügbar ist, macht GpioClx- und SpbCx-Ressourcen für den Benutzermodus verfügbar. Um die APIs zu aktivieren, müssen Sie in Ihren ACPI-Tabellen einen Geräteknoten für RhProxy mit den einzelnen GPIO- und SPB (Simple Peripheral Bus)-Ressourcen deklarieren, die für den Benutzermodus verfügbar gemacht werden sollen. In diesem Dokument wird die Erstellung und Überprüfung der ASL-Datei Schritt für Schritt erläutert.

ASL-Beispiel

Sehen wir uns die Deklaration des RhProxy-Geräteknotens für Raspberry Pi 2 an. Erstellen Sie zunächst die ACPI-Gerätedeklaration im Bereich „\_SB“.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID: Hardware-ID. Legen Sie diesen Wert auf eine herstellerspezifische Hardware-ID fest.
  • _CID: Kompatible ID. Dieser Wert muss „MSFT8000“ lauten.
  • _UID: Eindeutige ID. Legen Sie diesen Wert auf 1 fest.

Als Nächstes deklarieren Sie alle GPIO- und SPB-Ressourcen, die für den Benutzermodus verfügbar gemacht werden sollen. Die Reihenfolge, in der Ressourcen deklariert werden, ist wichtig, da Ressourcenindizes verwendet werden, um Eigenschaften Ressourcen zuzuordnen. Wenn mehrere I2C- oder SPI-Busse verfügbar gemacht werden, wird der erste deklarierte Bus als „Standardbus“ für diesen Typ betrachtet und von den GetDefaultAsync()-Methoden von Windows.Devices.I2c.I2c.I2cController und Windows.Devices.SpiController zurückgegeben.

SPI

Raspberry Pi macht zwei SPI-Busse verfügbar. SPI0 verfügt über zwei Hardware-Chip-Select-Leitungen und SPI1 über eine Hardware-Chip-Select-Leitung. Für jede Chip-Select-Leitung für jeden Bus ist eine SPISerialBus()-Ressourcendeklaration erforderlich. Die folgenden zwei SPISerialBus-Ressourcendeklarationen gelten für die beiden Chip-Select-Leitungen auf SPI0. Das Feld „DeviceSelection“ enthält einen eindeutigen Wert, den der Treiber als Hardware-Chip-Select-Leitungsbezeichner interpretiert. Der genaue Wert, den Sie im Feld „DeviceSelection“ eingeben, hängt davon ab, wie der Treiber dieses Feld des ACPI-Verbindungsdeskriptors interpretiert.

Hinweis

Dieser Artikel enthält Verweise auf den Begriff „Slave“. Microsoft duldet die Verwendung dieses Begriffs nicht und verwendet ihn in neuen Produkten und Dokumentationen nicht mehr. Sobald der Begriff aus der Software entfernt wird, wird er auch aus diesem Artikel entfernt.

// Index 0
SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                           // MOSI - GPIO 10 - Pin 19
                           // MISO - GPIO 9  - Pin 21
                           // CE0  - GPIO 8  - Pin 24
    0,                     // Device selection (CE0)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

// Index 1
SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                           // MOSI - GPIO 10 - Pin 19
                           // MISO - GPIO 9  - Pin 21
                           // CE1  - GPIO 7  - Pin 26
    1,                     // Device selection (CE1)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

Woher weiß Software, dass diese zwei Ressourcen demselben Bus zugeordnet werden sollen? Die Zuordnung zwischen dem Anzeigenamen des Busses und dem Ressourcenindex wird in den gerätespezifischen Daten (Device Specific Data, DSD) angegeben:

Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},

Dadurch wird ein Bus namens „SPI0“ mit zwei Chip-Select-Leitungen erstellt – Ressourcenindizes 0 und 1. Zum Deklarieren der Funktionen des SPI-Busses sind mehrere weitere Eigenschaften erforderlich.

Package(2) { "SPI0-MinClockInHz", 7629 },
Package(2) { "SPI0-MaxClockInHz", 125000000 },

Die Eigenschaften MinClockInHz und MaxClockInHz geben die minimalen und maximalen Taktgeschwindigkeiten an, die vom Controller unterstützt werden. Die API verhindert, dass Benutzer*innen Werte außerhalb dieses Bereichs angeben. Die Taktgeschwindigkeit wird im Feld „_SPE“ des Verbindungsdeskriptors an Ihren SPB-Treiber übergeben (ACPI-Abschnitt 6.4.3.8.2.2).

Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},

Die SupportedDataBitLengths-Eigenschaft listet die vom Controller unterstützten Datenbitlängen auf. Es können mehrere Werte in einer durch Trennzeichen getrennten Liste angegeben werden. Die API verhindert, dass Benutzer*innen Werte angeben, die nicht in dieser Liste enthalten sind. Die Datenbitlänge wird im Feld „_LEN“ des Verbindungsdeskriptors an Ihren SPB-Treiber übergeben (ACPI-Abschnitt 6.4.3.8.2.2).

Sie können sich diese Ressourcendeklarationen als „Vorlagen“ vorstellen. Einige Felder werden beim Systemstart festgelegt, während andere zur Laufzeit dynamisch angegeben werden. Die folgenden Felder des SPISerialBus-Deskriptors sind vorgegebene Felder:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

Die folgenden Felder sind Platzhalter für Werte, die zur Laufzeit von den Benutzer*innen angegeben werden:

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • ClockPhase

Da SPI1 nur eine einzelne Chip-Select-Leitung enthält, wird eine einzelne SPISerialBus()-Ressource deklariert:

// Index 2
SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                           // MOSI - GPIO 20 - Pin 38
                           // MISO - GPIO 19 - Pin 35
                           // CE1  - GPIO 17 - Pin 11
    1,                     // Device selection (CE1)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

Die Deklaration des zugehörigen Anzeigenamens (erforderlich) wird in den gerätespezifischen Daten (DSD) angegeben und bezieht sich auf den Index dieser Ressourcendeklaration.

Package(2) { "bus-SPI-SPI1", Package() { 2 }},

Dadurch wird ein Bus mit dem Namen „SPI1“ erstellt und dem Ressourcenindex 2 zugeordnet.

Anforderungen für den SPI-Treiber

  • Muss SpbCx verwenden oder SpbCx-kompatibel sein
  • Muss die MITT-SPI-Tests bestanden haben
  • Muss eine Taktgeschwindigkeit von 4 MHz unterstützen
  • Muss eine Datenbitlänge von 8 unterstützen
  • Muss alle SPI-Modi unterstützen: 0, 1, 2, 3

I2C

Als Nächstes deklarieren Sie die I2C-Ressourcen. Raspberry Pi macht einen einzelnen I2C-Bus an den Kontakten 3 und 5 verfügbar.

// Index 3
I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
    0xFFFF,                // SlaveAddress: placeholder
    ,                      // SlaveMode: default to ControllerInitiated
    0,                     // ConnectionSpeed: placeholder
    ,                      // Addressing Mode: placeholder
    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
    ,
    ,
    )                      // VendorData

Die Deklaration des zugehörigen Anzeigenamens (erforderlich), wird in den gerätespezifischen Daten (DSD) angegeben:

Package(2) { "bus-I2C-I2C1", Package() { 3 }},

Dadurch wird ein I2C-Bus mit dem Anzeigenamen „I2C1“ deklariert, der sich auf den Ressourcenindex 3 bezieht, d. h. den Index der oben deklarierten I2CSerialBus()-Ressource.

Die folgenden Felder des I2CSerialBus()-Deskriptors sind vorgegebene Felder:

  • SlaveMode
  • ResourceSource

Die folgenden Felder sind Platzhalter für Werte, die zur Laufzeit von den Benutzer*innen angegeben werden.

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

Anforderungen für den I2C-Treiber

  • Muss SpbCx verwenden oder SpbCx-kompatibel sein
  • Muss die MITT-I2C-Tests bestanden haben
  • Muss die 7-Bit-Adressierung unterstützen
  • Muss eine Taktgeschwindigkeit von 100 kHz unterstützen
  • Muss eine Taktgeschwindigkeit von 400 kHz unterstützen

GPIO

Als Nächstes deklarieren Sie alle GPIO-Kontakte, die für den Benutzermodus verfügbar gemacht werden. Orientieren Sie sich bei der Auswahl der Kontakte, die verfügbar gemacht werden sollen, an folgenden Richtlinien:

  • Deklarieren Sie alle Kontakte auf verfügbar gemachten Kontaktleisten.
  • Deklarieren Sie Kontakte, die mit nützlichen integrierten Funktionen wie Tasten und LEDs verbunden sind.
  • Deklarieren Sie keine Kontakte, die für Systemfunktionen reserviert sind oder mit nichts verbunden sind.

Der folgende ASL-Block deklariert zwei Kontakte – GPIO4 und GPIO5. Die anderen Kontakte werden hier der Übersichtlichkeit halber nicht gezeigt. Anhang C enthält ein PowerShell-Beispielskript, das zum Generieren der GPIO-Ressourcen verwendet werden kann.

// Index 4 – GPIO 4
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 4 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 4 }

// Index 6 – GPIO 5
GpioIO(Shared, PullUp, , , , “\\_SB.GPI0”, , , , ) { 5 }
GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, “\\_SB.GPI0”,) { 5 }

Die folgenden Anforderungen müssen beim Deklarieren von GPIO-Kontakten beachtet werden:

  • Nur im Speicher abgebildete GPIO-Controller werden unterstützt. Über eine I2C-/SPI-Schnittstelle verbundene GPIO-Controller werden nicht unterstützt. Der Controllertreiber ist ein im Speicher abgebildeter Controller, wenn er das MemoryMappedController-Flag in der CLIENT_CONTROLLER_BASIC_INFORMATION-Struktur als Reaktion auf den CLIENT_QueryControllerBasicInformation-Rückruf festlegt.
  • Jeder Kontakt erfordert sowohl eine GpioIO- als auch eine GpioInt-Ressource. Die GpioInt-Ressource muss direkt auf die GpioIO-Ressource folgen und auf dieselbe Kontaktnummer verweisen.
  • GPIO-Ressourcen müssen nach aufsteigender Kontaktnummer sortiert werden.
  • Jede GpioIO- und GpioInt-Ressource muss genau eine Kontaktnummer in der Kontaktliste enthalten.
  • Das Feld „ShareType“ beider Deskriptoren muss auf „Shared“ festgelegt sein.
  • Das Feld „EdgeLevel“ des GpioInt-Deskriptors muss auf „Edge“ festgelegt sein.
  • Das Feld „ActiveLevel“ des GpioInt-Deskriptors muss auf „ActiveBoth“ festgelegt sein.
  • Das Feld „PinConfig“
    • Muss in den GpioIO- und GpioInt-Deskriptoren identisch sein.
    • Muss auf „PullUp“, „PullDown“ oder „PullNone“ festgelegt sein. Der Wert „PullDefault“ ist nicht zulässig.
    • Die Pullkonfiguration muss dem Einschaltzustand des Kontakts entsprechen. Der Zustand des Kontakts darf sich nicht ändern, wenn er vom Einschaltzustand in den angegebenen Pullmodus geschaltet wird. Wenn das Datenblatt beispielsweise einen Pull-Up-Widerstand für den Kontakt festlegt, geben Sie „PinConfig“ als „PullUp“ an.

Der Firmware-, UEFI- und Treiberinitialisierungscode sollte den Einschaltzustand eines Kontakts während des Starts nicht ändern. Nur die Benutzer*innen wissen, was an einen Kontakt angeschlossen ist und welche Zustandsübergänge sicher sind. Der Einschaltzustand jedes Kontakts muss dokumentiert sein, damit Benutzer*innen Hardware entwerfen können, die ordnungsgemäß mit einem Kontakt verbunden wird. Ein Kontakt darf seinen Zustand während des Starts nicht unerwartet ändern.

Unterstützte Betriebsmodi

Wenn Ihr GPIO-Controller integrierte Pull-Up- und Pull-Down-Widerstände zusätzlich zu einem hochohmigen Eingang und einem CMOS-Ausgang unterstützt, müssen Sie dies mit der optionalen SupportedDriveModes-Eigenschaft angeben.

Package (2) { “GPIO-SupportedDriveModes”, 0xf },

Die SupportedDriveModes-Eigenschaft gibt an, welche Betriebsmodi vom GPIO-Controller unterstützt werden. Im obigen Beispiel werden alle der folgenden Betriebsmodi unterstützt. Die Eigenschaft ist eine Bitmaske der folgenden Werte:

Flag-Wert Betriebsmodus Beschreibung
0x1 InputHighImpedance Der Kontakt unterstützt einen hochohmigen Eingang, was dem Wert „PullNone“ in ACPI entspricht.
0x2 InputPullUp Der Kontakt unterstützt einen integrierten Pull-Up-Widerstand, was dem Wert „PullUp“ in ACPI entspricht.
0x4 InputPullDown Der Kontakt unterstützt einen integrierten Pull-Down-Widerstand, was dem Wert „PullDown“ in ACPI entspricht.
0x8 OutputCmos Der Kontakt unterstützt das Generieren von starken hohen und niedrigen Pegeln (im Gegensatz zu einem Open-Drain-Ausgang).

InputHighImpedance und OutputCmos werden von fast allen GPIO-Controllern unterstützt. Wenn die SupportedDriveModes-Eigenschaft nicht angegeben ist, ist dies der Standardwert.

Wenn ein GPIO-Signal vor dem Erreichen einer verfügbar gemachten Kontaktleiste einen Pegelwandler durchläuft, deklarieren Sie die vom SOC (System on a Chip) unterstützten Betriebsmodi, auch wenn der Betriebsmodus nicht auf der externen Kontaktleiste festgestellt werden kann. Wenn ein Kontakt beispielsweise über einen bidirektionalen Pegelwandler verbunden wird, durch den ein Kontakt als Open-Drain mit Pull-Up-Widerstand erscheint, wird niemals ein hochohmiger Zustand auf der verfügbaren Kontaktleiste auftreten, auch wenn der Kontakt als hochohmiger Eingang konfiguriert ist. Sie sollten dennoch deklarieren, dass der Kontakt einen hochohmigen Eingang unterstützt.

Kontaktnummerierung

Windows unterstützt zwei Kontaktnummerierungsschemas:

  • Sequenzielle Kontaktnummerierung: Benutzer*innen sehen Nummern wie 0, 1, 2... bis zur Anzahl der verfügbar gemachten Kontakte. 0 ist die erste in ASL deklarierte GpioIo-Ressource, 1 die zweite in ASL deklarierte GpioIo-Ressource usw.
  • Native Kontaktnummerierung: Benutzer*innen sehen die in GpioIo-Deskriptoren angegebenen Kontaktnummern (z. B. 4, 5, 12, 13 usw.).
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

Die UseDescriptorPinNumbers-Eigenschaft weist Windows an, die native Kontaktnummerierung anstelle der sequenziellen Kontaktnummerierung zu verwenden. Wenn die UseDescriptorPinNumbers-Eigenschaft nicht angegeben oder NULL ist, verwendet Windows standardmäßig die sequenzielle Kontaktnummerierung.

Wenn die native Kontaktnummerierung verwendet wird, müssen Sie auch die PinCount-Eigenschaft angeben.

Package (2) { “GPIO-PinCount”, 54 },

Die PinCount-Eigenschaft sollte dem Wert entsprechen, der über die TotalPins-Eigenschaft im CLIENT_QueryControllerBasicInformation-Rückruf des GpioClx-Treibers zurückgegeben wird.

Wählen Sie das Nummerierungsschema aus, das den vorhandenen veröffentlichten Dokumentationen für Ihre Platine am besten entspricht. Beispielsweise verwendet Raspberry Pi die native Kontaktnummerierung, da in vielen vorhandenen Kontaktbelegungsplänen die BCM2835-Kontaktnummern verwendet werden. MinnowBoardMax verwendet die sequenzielle Kontaktnummerierung, da es nur wenige vorhandene Kontaktbelegungspläne gibt, und die sequenzielle Kontaktnummerierung die Entwicklererfahrung verbessert, weil von mehr als 200 Kontakten nur zehn Kontakte verfügbar gemacht werden. Die Entscheidung, ob die sequenzielle oder native Kontaktnummerierung verwendet werden soll, sollte darauf abzielen, Verwirrung aufseiten der Entwickler*innen zu vermeiden.

Anforderungen für den GPIO-Treiber

  • Muss GpioClx verwenden
  • Muss auf dem SOC im Speicher abgebildet sein
  • Muss die emulierte ActiveBoth-Interruptbehandlung verwenden

UART

Wenn Ihr UART-Treiber SerCx oder SerCx2 verwendet, können Sie RhProxy verwenden, um den Treiber für den Benutzermodus verfügbar zu machen. UART-Treiber, die eine Geräteschnittstelle vom Typ GUID_DEVINTERFACE_COMPORT erstellen, müssen nicht RhProxy verwenden. Der Eingangstreiber Serial.sys ist einer dieser Fälle.

Um einen UART im SerCx-Stil für den Benutzermodus verfügbar zu machen, deklarieren Sie wie folgt eine UARTSerialBus-Ressource.

// Index 2
UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
    115200,                // InitialBaudRate: in bits ber second
    ,                      // BitsPerByte: default to 8 bits
    ,                      // StopBits: Defaults to one bit
    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
    ,                      // IsBigEndian: default to LittleEndian
    ,                      // Parity: Defaults to no parity
    ,                      // FlowControl: Defaults to no flow control
    32,                    // ReceiveBufferSize
    32,                    // TransmitBufferSize
    "\\_SB.URT2",          // ResourceSource: UART bus controller name
    ,
    ,
    ,
    )

Nur das Feld „ResourceSource“ ist festgelegt. Alle anderen Felder sind Platzhalter für Werte sind, die zur Laufzeit von Benutzer*innen angegeben werden.

Die Deklaration des zugehörigen Anzeigenamens lautet wie folgt:

Package(2) { "bus-UART-UART2", Package() { 2 }},

Dies weist dem Controller den Anzeigenamen „UART2“ zu. Dabei handelt es sich um den Bezeichner, den Benutzer*innen verwenden, um im Benutzermodus auf den Bus zuzugreifen.

Kontakt-Multiplexing zur Laufzeit

Kontakt-Multiplexing ist die Möglichkeit, denselben physischen Kontakt für verschiedene Funktionen zu verwenden. Mehrere verschiedene On-Chip-Peripheriegeräte, z. B. ein I2C-Controller, SPI-Controller und GPIO-Controller, können an den gleichen physischen Kontakt auf einem SOC geleitet werden. Der Multiplexer-Block steuert, welche Funktion zu einem bestimmten Zeitpunkt auf dem Kontakt aktiv ist. Traditionell ist Firmware dafür verantwortlich, beim Start Funktionszuweisungen einzurichten, und diese Zuweisung bleibt während Startsitzung statisch. Das Kontakt-Multiplexing zur Laufzeit bietet die Möglichkeit, Funktionszuweisungen für Kontakte zur Laufzeit neu zu konfigurieren. Wenn Sie es Benutzer*innen ermöglichen, die Funktion eines Kontakts zur Laufzeit auszuwählen, können Benutzer*innen die Kontakte einer Platine schnell neu konfigurieren, und die Hardware kann eine breitere Palette von Anwendungen unterstützen als eine statische Konfiguration. Dies beschleunigt die Entwicklung.

Benutzer*innen nutzen die Mutiplexing-Unterstützung für GPIO, I2C, SPI und UART, ohne zusätzlichen Code zu schreiben. Wenn Benutzer*innen einen GPIO oder Bus mit OpenPin() oder FromIdAsync() öffnen, werden die zugrunde liegenden physischen Kontakte automatisch im Multiplexmodus der angeforderten Funktion zugewiesen (gemuxt). Wenn die Kontakte bereits von einer anderen Funktion verwendet werden, schlägt der Aufruf von OpenPin() oder FromIdAsync() fehl. Wenn Benutzer*innen das Gerät schließen, indem sie das GpioPin-, I2cDevice-, SpiDevice- oder SerialDevice-Objekt löschen, werden die Kontakte freigegeben, sodass sie später für eine andere Funktion geöffnet werden können.

Windows enthält integrierte Unterstützung für Kontakt-Multiplexing in den GpioClx-, SpbCx- und SerCx-Frameworks. Diese Frameworks arbeiten zusammen, um einen Kontakt automatisch auf die richtige Funktion umzuschalten, wenn auf einen GPIO-Kontakt oder -Bus zugegriffen wird. Der Zugriff auf die Kontakte wird vermittelt, um Konflikte zwischen mehreren Clients zu verhindern. Zusätzlich zu dieser integrierten Unterstützung sind die Schnittstellen und Protokolle für Kontakt-Multiplexing universell und können erweitert werden, um weitere Geräte und Szenarien zu unterstützen.

In diesem Dokument werden zunächst die zugrunde liegenden Schnittstellen und Protokolle für das Kontakt-Multiplexing beschrieben, und anschließend erfahren Sie, wie Sie GpioClx-, SpbCx- und SerCx-Controllertreibern Unterstützung für das Kontakt-Multiplexing hinzufügen.

Kontakt-Multiplexing-Architektur

In diesem Abschnitt werden die zugrunde liegenden Schnittstellen und Protokolle beschrieben, die am Kontakt-Multiplexing beteiligt sind. Kenntnisse der zugrunde liegenden Protokolle sind nicht unbedingt erforderlich, um das Kontakt-Multiplexing mit GpioClx-/SpbCx-/SerCx-Treibern zu unterstützen. Ausführliche Informationen zur Unterstützung des Kontakt-Multiplexings mit GpioCls-/SpbCx-/SerCx-Treibern finden Sie unter Unterstützen des Kontakt-Multiplexings in GpioClx-Clienttreibern und Unterstützen des Kontakt-Multiplexings in SpbCx- und SerCx-Controllertreibern.

Das Kontakt-Multiplexing wird durch die Zusammenarbeit mehrerer Komponenten erreicht.

  • Kontakt-Multiplexing-Server: Dies sind Treiber, die den Kontakt-Multiplexing-Kontrollblock steuern. Kontakt-Multiplexing-Server empfangen Kontakt-Multiplexing-Anforderungen von Clients über Anforderungen zur Reservierung von Multiplexing-Ressourcen (IRP_MJ_CREATE-Anforderungen) und Anforderungen zum Umschalten der Funktion eines Kontakts (*IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-Anforderungen). Der Kontakt-Multiplexing-Server ist in der Regel der GPIO-Treiber, da der Multiplexing-Block manchmal Teil des GPIO-Blocks ist. Selbst wenn der Multiplexing-Block ein separates Peripheriegerät ist, ist der GPIO-Treiber ein logischer Ort für die Platzierung von Multiplexing-Funktionen.
  • Kontakt-Multiplexing-Clients: Dies sind Treiber, die Kontakt-Multiplexing nutzen. Kontakt-Multiplexing-Clients erhalten Kontakt-Multiplexing-Ressourcen von ACPI-Firmware. Kontakt-Multiplexing-Ressourcen sind eine Art von Verbindungsressourcen und werden vom Ressourcenhub verwaltet. Kontakt-Multiplexing-Clients reservieren Kontakt-Multiplexing-Ressourcen, indem sie ein Handle für die Ressource öffnen. Um eine Hardwareänderung zu bewirken, müssen Clients ein Commit für die Konfiguration ausführen, indem sie eine IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-Anforderung senden. Clients geben Kontakt-Multiplexing-Ressourcen frei, indem sie das Handle schließen, wodurch die Multiplexing-Konfiguration auf ihren Standardzustand zurückgesetzt wird.
  • ACPI-Firmware: Gibt die Multiplexing-Konfiguration mit MsftFunctionConfig()-Ressourcen an. MsftFunctionConfig-Ressourcen geben an, welche Kontakte in welcher Multiplexing-Konfiguration von einem Client benötigt werden. MsftFunctionConfig-Ressourcen enthalten die Funktionsnummer, die Pullkonfiguration und die Liste der Kontaktnummern. MsftFunctionConfig-Ressourcen werden für Kontakt-Multiplexing-Clients als Hardwareressourcen bereitgestellt, die von Treibern in ihrem PrepareHardware-Rückruf empfangen werden (ähnlich wie GPIO- und SPB-Verbindungsressourcen). Clients empfangen eine Ressourcenhub-ID, die zum Öffnen eines Handles für die Ressource verwendet werden kann.

Sie müssen die Befehlszeilenoption /MsftInternal an asl.exe übergeben, um ASL-Dateien zu kompilieren, die MsftFunctionConfig()-Deskriptoren enthalten, da diese Deskriptoren derzeit vom ACPI-Arbeitsausschuss überprüft werden. Beispiel: asl.exe /MsftInternal dsdt.asl

Die Abfolge von Vorgängen beim Kontakt-Multiplexing ist unten dargestellt.

Pin muxing client server interaction

  1. Der Client empfängt MsftFunctionConfig-Ressourcen von der ACPI-Firmware in seinem EvtDevicePrepareHardware()-Rückruf.
  2. Der Client verwendet die Ressourcenhub-Hilfsfunktion RESOURCE_HUB_CREATE_PATH_FROM_ID(), um einen Pfad aus der Ressourcen-ID zu erstellen, und öffnet dann ein Handle für den Pfad (mithilfe von ZwCreateFile(), IoGetDeviceObjectPointer() oder WdfIoTargetOpen()).
  3. Der Server extrahiert die Ressourcenhub-ID mithilfe der Ressourcenhub-Hilfsfunktion RESOURCE_HUB_ID_FROM_FILE_NAME() aus dem Dateipfad und fragt dann den Ressourcenhub ab, um den Ressourcendeskriptor abzurufen.
  4. Der Server führt die Freigabevermittlung für jeden Kontakt im Deskriptor aus und schließt die IRP_MJ_CREATE-Anforderung ab.
  5. Der Client gibt eine IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-Anforderung für das empfangene Handle aus.
  6. Als Reaktion auf IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS führt der Server den Hardware-Multiplexing-Vorgang durch, indem er die angegebene Funktion auf jedem Kontakt aktiviert.
  7. Der Client führt dann je nach angeforderter Kontakt-Multiplexing-Konfiguration weitere Vorgänge aus.
  8. Wenn der Client keine gemuxten Kontakte mehr benötigt, schließt er das Handle.
  9. Als Reaktion auf das Schließen des Handles setzt der Server die Kontakte auf ihren Anfangszustand zurück.

Beschreibung des Protokolls für Kontakt-Multiplexing-Clients

In diesem Abschnitt wird beschrieben, wie ein Client Kontakt-Multiplexing-Funktionen nutzt. Dies gilt nicht für SerCx- und SpbCx-Controllertreiber, da die Frameworks dieses Protokoll im Auftrag von Controllertreibern implementieren.

Analysieren von Ressourcen

Ein WDF-Treiber empfängt MsftFunctionConfig()-Ressourcen in seiner EvtDevicePrepareHardware()-Routine. MsftFunctionConfig-Ressourcen können anhand der folgenden Felder identifiziert werden:

CM_PARTIAL_RESOURCE_DESCRIPTOR::Type = CmResourceTypeConnection
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Class = CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Type = CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG

Eine EvtDevicePrepareHardware()-Routine kann MsftFunctionConfig-Ressourcen wie folgt extrahieren:

EVT_WDF_DEVICE_PREPARE_HARDWARE evtDevicePrepareHardware;

_Use_decl_annotations_
NTSTATUS
evtDevicePrepareHardware (
    WDFDEVICE WdfDevice,
    WDFCMRESLIST ResourcesTranslated
    )
{
    PAGED_CODE();

    LARGE_INTEGER connectionId;
    ULONG functionConfigCount = 0;

    const ULONG resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
    for (ULONG index = 0; index < resourceCount; ++index) {
        const CM_PARTIAL_RESOURCE_DESCRIPTOR* resDescPtr =
            WdfCmResourceListGetDescriptor(ResourcesTranslated, index);

        switch (resDescPtr->Type) {
        case CmResourceTypeConnection:
            switch (resDescPtr->u.Connection.Class) {
            case CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG:
                switch (resDescPtr->u.Connection.Type) {
                case CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG:
                    switch (functionConfigCount) {
                    case 0:
                        // save the connection ID
                        connectionId.LowPart = resDescPtr->u.Connection.IdLowPart;
                        connectionId.HighPart = resDescPtr->u.Connection.IdHighPart;
                        break;
                    } // switch (functionConfigCount)
                    ++functionConfigCount;
                    break; // CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG

                } // switch (resDescPtr->u.Connection.Type)
                break; // CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
            } // switch (resDescPtr->u.Connection.Class)
            break;
        } // switch
    } // for (resource list)

    if (functionConfigCount < 1) {
        return STATUS_INVALID_DEVICE_CONFIGURATION;
    }
    // TODO: save connectionId in the device context for later use

    return STATUS_SUCCESS;
}

Reservieren von Ressourcen und Ausführen von Commit für Ressourcen

Wenn ein Client Kontakte muxen möchte, reserviert er die MsftFunctionConfig-Ressource und führt ein Commit für sie aus. Das folgende Beispiel zeigt, wie ein Client MsftFunctionConfig-Ressourcen reservieren und ein Commit für sie ausführen kann.

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS AcquireFunctionConfigResource (
    WDFDEVICE WdfDevice,
    LARGE_INTEGER ConnectionId,
    _Out_ WDFIOTARGET* ResourceHandlePtr
    )
{
    PAGED_CODE();

    //
    // Form the resource path from the connection ID
    //
    DECLARE_UNICODE_STRING_SIZE(resourcePath, RESOURCE_HUB_PATH_CHARS);
    NTSTATUS status = RESOURCE_HUB_CREATE_PATH_FROM_ID(
            &resourcePath,
            ConnectionId.LowPart,
            ConnectionId.HighPart);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Create a WDFIOTARGET
    //
    WDFIOTARGET resourceHandle;
    status = WdfIoTargetCreate(WdfDevice, WDF_NO_ATTRIBUTES, &resourceHandle);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Reserve the resource by opening a WDFIOTARGET to the resource
    //
    WDF_IO_TARGET_OPEN_PARAMS openParams;
    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
        &openParams,
        &resourcePath,
        FILE_GENERIC_READ | FILE_GENERIC_WRITE);

    status = WdfIoTargetOpen(resourceHandle, &openParams);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //
    // Commit the resource
    //
    status = WdfIoTargetSendIoctlSynchronously(
            resourceHandle,
            WDF_NO_HANDLE,      // WdfRequest
            IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS,
            nullptr,            // InputBuffer
            nullptr,            // OutputBuffer
            nullptr,            // RequestOptions
            nullptr);           // BytesReturned

    if (!NT_SUCCESS(status)) {
        WdfIoTargetClose(resourceHandle);
        return status;
    }

    //
    // Pins were successfully muxed, return the handle to the caller
    //
    *ResourceHandlePtr = resourceHandle;
    return STATUS_SUCCESS;
}

Der Treiber sollte das WDFIOTARGET in einem seiner Kontextbereiche speichern, damit es später geschlossen werden kann. Wenn der Treiber bereit ist, die Multiplexing-Konfiguration freizugeben, sollte er das Ressourcenhandle durch Aufrufen von WdfObjectDelete() oder WdfIoTargetClose() schließen, sofern Sie das WDFIOTARGET wiederverwenden möchten.

    WdfObjectDelete(resourceHandle);

Wenn der Client sein Ressourcenhandle schließt, werden die Kontakte wieder auf ihren Anfangszustand gemuxt und können dann von einem anderen Client abgerufen werden.

Beschreibung des Protokolls für Kontakt-Multiplexing-Server

In diesem Abschnitt wird beschrieben, wie ein Kontakt-Multiplexing-Server seine Funktionen für Clients verfügbar macht. Dies gilt nicht für GpioClx-Miniporttreiber, da das Framework dieses Protokoll im Auftrag von Clienttreibern implementiert. Ausführliche Informationen zur Unterstützung von Kontakt-Multiplexing in GpioClx-Clienttreibern finden Sie unter Unterstützen des Kontakt-Multiplexings in GpioClx-Clienttreibern.

Behandeln von IRP_MJ_CREATE-Anforderungen

Clients öffnen ein Handle für eine Ressource, wenn sie eine Kontakt-Multiplexing-Ressource reservieren wollen. Ein Kontakt-Multiplexing-Server empfängt IRP_MJ_CREATE-Anforderungen über einen REPARSE-Vorgang vom Ressourcenhub. Die nachfolgende Pfadkomponente der IRP_MJ_CREATE-Anforderung enthält die Ressourcenhub-ID, bei der es sich um eine ganze 64-Bit-Zahl im Hexadezimalformat handelt. Der Server sollte die Ressourcenhub-ID mithilfe von RESOURCE_HUB_ID_FROM_FILE_NAME() in „reshub.h“ aus dem Dateinamen extrahieren und IOCTL_RH_QUERY_CONNECTION_PROPERTIES an den Ressourcenhub senden, um den MsftFunctionConfig()-Deskriptor abzurufen.

Der Server sollte den Deskriptor überprüfen und den Freigabemodus sowie die Kontaktliste aus dem Deskriptor extrahieren. Anschließend sollte er die Freigabevermittlung für die Kontakte ausführen, und sofern diese erfolgreich ist, die Kontakte als reserviert markieren, bevor er die Anforderung abschließt.

Die Freigabevermittlung ist insgesamt erfolgreich, wenn sie für jeden Kontakt in der Kontaktliste erfolgreich ausgeführt wird. Jeder Kontakt sollte wie folgt vermittelt werden:

  • Wenn der Kontakt noch nicht reserviert ist, ist die Freigabevermittlung erfolgreich.
  • Wenn der Kontakt bereits als „exklusiv“ reserviert ist, schlägt die Freigabevermittlung fehl.
  • Wenn der Kontakt bereits als „freigegeben“ reserviert ist
    • und die eingehende Anforderung freigegeben (Shared) ist, ist die Freigabevermittlung erfolgreich.
    • und die eingehende Anforderung exklusiv (Exclusive) ist, schlägt die Freigabevermittlung fehl.

Wenn die Freigabevermittlung fehlschlägt, sollte die Anforderung mit STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE abgeschlossen werden. Wenn die Freigabevermittlung erfolgreich ist, sollte die Anforderung mit STATUS_SUCCESS abgeschlossen werden.

Beachten Sie, dass der Freigabemodus der eingehenden Anforderung dem MsftFunctionConfig-Deskriptor entnommen werden sollte, nicht IrpSp -> Parameters.Create.ShareAccess>.

Behandeln von IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-Anforderungen

Nachdem der Client eine MsftFunctionConfig-Ressource durch Öffnen eines Handles erfolgreich reserviert hat, kann er IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS senden, um die Ausführung des tatsächlichen Hardware-Multiplexing-Vorgangs durch den Server anzufordern. Wenn der Server IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS empfängt, sollte er für jeden Kontakt in der Kontaktliste die folgenden Schritte ausführen:

  • Festlegen des im PinConfiguration-Element der PNP_FUNCTION_CONFIG_DESCRIPTOR-Struktur angegebenen Pullmodus in der Hardware
  • Muxen des Kontakts auf die Funktion, die durch das FunctionNumber-Element der PNP_FUNCTION_CONFIG_DESCRIPTOR-Struktur angegeben ist

Der Server sollte die Anforderung dann mit STATUS_SUCCESS abschließen.

Die Bedeutung des Felds „FunctionNumber“ wird vom Server definiert, und es wird davon ausgegangen, dass der MsftFunctionConfig-Deskriptor mit Kenntnis der Interpretation dieses Felds durch den Server erstellt wurde.

Denken Sie daran, dass der Server die Kontakte auf die beim Empfang von IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS vorliegende Konfiguration zurücksetzen muss, wenn das Handle geschlossen wird. Daher muss der Server den Zustand der Kontakte möglicherweise speichern, bevor er sie ändert.

Behandeln von IRP_MJ_CLOSE-Anforderungen

Wenn ein Client eine Multiplexing-Ressource nicht mehr benötigt, schließt er sein Handle. Wenn ein Server eine IRP_MJ_CLOSE-Anforderung empfängt, sollte er die Kontakte auf den Zustand zurücksetzen, in dem sie sich beim Empfang von IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS befanden. Wenn der Client nie eine IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-Anforderung gesendet hat, ist keine Aktion erforderlich. Der Server sollte die Kontakte dann in Bezug auf die Freigabevermittlung als „verfügbar“ markieren und die Anforderung mit STATUS_SUCCESS abschließen. Achten Sie darauf, die IRP_MJ_CLOSE-Behandlung ordnungsgemäß mit der IRP_MJ_CREATE-Behandlung zu synchronisieren.

Richtlinien für die Erstellung von ACPI-Tabellen

In diesem Abschnitt wird beschrieben, wie Multiplexing-Ressourcen für Clienttreiber bereitgestellt werden. Beachten Sie, dass Sie Microsoft ASL-Compilerbuild 14327 oder höher benötigen, um Tabellen mit MsftFunctionConfig()-Ressourcen zu kompilieren. MsftFunctionConfig()-Ressourcen werden für Kontakt-Multiplexing-Clients als Hardwareressourcen bereitgestellt. MsftFunctionConfig()-Ressourcen sollten für Treiber bereitgestellt werden, die Kontakt-Multiplexing-Änderungen erfordern (dies sind in der Regel SPB- und serielle Controllertreiber), aber nicht für SPB- und serielle Peripherietreiber, da der Controllertreiber die Multiplexing-Konfiguration behandelt. Das ACPI-Makro MsftFunctionConfig() wird wie folgt definiert:

  MsftFunctionConfig(Shared/Exclusive
                PinPullConfig,
                FunctionNumber,
                ResourceSource,
                ResourceSourceIndex,
                ResourceConsumer/ResourceProducer,
                VendorData) { Pin List }

  • Shared/Exclusive: Beim Wert „Exclusive“, kann der Kontakt jeweils von einem einzelnen Client abgerufen werden. Beim Wert „Shared“ können mehrere freigegebene Clients die Ressource abrufen. Legen Sie diesen Wert immer auf „Exclusive“ fest, da es zu einem „Data Race“ und dadurch zu unvorhersehbaren Ergebnissen führen kann, wenn mehrere nicht koordinierte Clients auf eine änderbare Ressource zugreifen können.
  • Verwenden Sie für PinPullConfig einen der folgenden Werte:
    • PullDefault: Verwendet die vom SOC definierte Standard-Pullkonfiguration beim Einschalten
    • PullUp: Aktiviert den Pull-Up-Widerstand
    • PullDown: Aktiviert den Pull-Down-Widerstand
    • PullNone: Deaktiviert alle Pull-Widerstände
  • FunctionNumber: Die Funktionsnummer, die im Multiplexer programmiert werden soll.
  • ResourceSource: Der ACPI-Namespacepfad des Kontakt-Multiplexing-Servers.
  • ResourceSourceIndex: Legen Sie diesen Wert auf 0 fest.
  • ResourceConsumer/ResourceProducer: Legen Sie diesen Wert auf ResourceConsumer fest.
  • VendorData: Optionale Binärdaten, deren Bedeutung vom Kontakt-Multiplexing-Server definiert wird. Dieser Wert sollte in der Regel leer bleiben.
  • Pin List: Eine durch Trennzeichen getrennte Liste von Kontaktnummern, für die die Konfiguration gilt. Wenn der Kontakt-Multiplexing-Server ein GpioClx-Treiber ist, sind dies GPIO-Kontaktnummern. Sie haben dieselbe Bedeutung wie Kontaktnummern in einem GpioIo-Deskriptor.

Das folgende Beispiel zeigt, wie eine MsftFunctionConfig()-Ressource für einen I2C-Controllertreiber bereitgestellt werden kann.

Device(I2C1)
{
    Name(_HID, "BCM2841")
    Name(_CID, "BCMI2C")
    Name(_UID, 0x1)
    Method(_STA)
    {
        Return(0xf)
    }
    Method(_CRS, 0x0, NotSerialized)
    {
        Name(RBUF, ResourceTemplate()
        {
            Memory32Fixed(ReadWrite, 0x3F804000, 0x20)
            Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x55 }
            MsftFunctionConfig(Exclusive, PullUp, 4, "\\_SB.GPI0", 0, ResourceConsumer, ) { 2, 3 }
        })
        Return(RBUF)
    }
}

Neben den Arbeitsspeicher- und Interruptressourcen, die ein Controllertreiber normalerweise benötigt, wird auch eine MsftFunctionConfig()-Ressource angegeben. Diese Ressource ermöglicht es dem I2C-Controllertreiber, die Kontakte 2 und 3, die vom Geräteknoten unter „\_SB.GPIO0“ verwaltet werden, auf die Funktion 4 mit aktiviertem Pull-Up-Widerstand zu schalten.

Unterstützen des Kontakt-Multiplexings in GpioClx-Clienttreibern

GpioClx verfügt über integrierte Unterstützung für das Kontakt-Multiplexing. GpioClx-Miniporttreiber (auch als GpioClx-Clienttreiber bezeichnet) steuern die GPIO-Controllerhardware. Ab Windows 10 Build 14327 können GpioClx-Miniporttreiber durch die Implementierung von zwei neuen Gerätetreiberschnittstellen (Device Driver Interface, DDI) Unterstützung für Kontakt-Multiplexing hinzufügen:

  • CLIENT_ConnectFunctionConfigPins: Wird von GpioClx aufgerufen, um den Miniporttreiber anzuweisen, die angegebene Multiplexing-Konfiguration anzuwenden.
  • CLIENT_DisconnectFunctionConfigPins: Wird von GpioClx aufgerufen, um den Miniporttreiber anzuweisen, die Multiplexing-Konfiguration zurückzusetzen.

Eine Beschreibung dieser Routinen finden Sie unter GpioClx Event Callback Functions (GpioClx-Ereignisrückruffunktionen).

Zusätzlich zu diesen beiden neuen DDIs sollten vorhandene DDIs auf ihre Kontakt-Multiplexing-Kompatibilität überwacht werden:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt: CLIENT_ConnectIoPins wird von GpioClx aufgerufen, um den Miniporttreiber anzuweisen, festgelegte Kontakte für den GPIO-Eingang oder -Ausgang zu konfigurieren. GPIO und MsftFunctionConfig schließen sich gegenseitig aus, d. h. ein Kontakt wird niemals gleichzeitig für GPIO und MsftFunctionConfig verbunden. Da die Standardfunktion eines Kontakts nicht GPIO sein muss, wird ein Kontakt möglicherweise nicht auf GPIO gemuxt, wenn ConnectIoPins aufgerufen wird. ConnectIoPins ist erforderlich, um alle notwendigen Vorgänge zum Vorbereiten des Kontakts für GPIO-E/A (einschließlich Multiplexing-Vorgänge) auszuführen. CLIENT_ConnectInterrupt sollte sich ähnlich verhalten, da Interrupts als Sonderfall von GPIO-Eingängen betrachtet werden können.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt: Diese Routine sollte Kontakte auf den Zustand zurücksetzen, in dem sie sich beim Aufruf von CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt befanden, es sei denn, das PreserveConfiguration-Flag ist angegeben. Neben dem Zurücksetzen von Kontakten auf ihren Standardzustand sollte der Miniport auch den Multiplexing-Zustand der einzelnen Kontakte auf den Zustand beim Aufruf der „_Connect“-Routine zurücksetzen.

Angenommen, die Standard-Multiplexing-Konfiguration eines Kontakts ist UART, und der Kontakt kann auch als GPIO verwendet werden. Wenn CLIENT_ConnectIoPins aufgerufen wird, um den Kontakt für GPIO zu verbinden, sollte der Kontakt auf GPIO gemuxt werden, und in CLIENT_DisconnectIoPins sollte er wieder auf UART gemuxt werden. Im Allgemeinen sollten die Disconnect-Routinen Vorgänge rückgängig machen, die von Connect-Routinen durchgeführt wurden.

Unterstützen des Kontakt-Multiplexings in SpbCx- und SerCx-Controllertreibern

Ab Windows 10 Build 14327 enthalten die SpbCx- und SerCx-Frameworks integrierte Unterstützung für Kontakt-Multiplexing, die es den SpbCx- und SerCx-Controllertreibern ermöglicht, ohne Codeänderungen an den Controllertreibern selbst als Kontakt-Multiplexing-Clients zu fungieren. Durch Erweiterung löst jeder SpbCx-/SerCx-Peripherietreiber, der eine Verbindung mit einem Multiplexing-fähigen SpbCx-/SerCx-Controllertreiber herstellt, eine Kontakt-Multiplexing-Aktivität aus.

Das folgende Diagramm zeigt die Abhängigkeiten zwischen den einzelnen Komponenten. Wie Sie sehen können, führt das Kontakt-Multiplexing für den GPIO-Treiber, der in der Regel für das Multiplexing verantwortlich ist, zu einer Abhängigkeit von SerCx- und SpbCx-Controllertreibern.

Pin muxing dependency

Bei der Geräteinitialisierung analysieren die SpbCx- und SerCx-Frameworks alle MsftFunctionConfig()-Ressourcen, die als Hardwareressourcen für das Gerät bereitgestellt werden. SpbCx-/SerCx-Controllertreiber rufen dann bei Bedarf die Kontakt-Multiplexing-Ressourcen ab und geben sie frei.

SpbCx wendet die Kontakt-Multiplexing-Konfiguration im IRP_MJ_CREATE-Handler direkt vor dem Aufrufen des EvtSpbTargetConnect()-Rückrufs des Clienttreibers an. Wenn die Multiplexing-Konfiguration nicht angewendet werden konnte, wird der EvtSpbTargetConnect()-Rückruf des Controllertreibers nicht aufgerufen. Daher kann ein SPB-Controllertreiber davon ausgehen, dass Kontakte beim Aufruf von EvtSpbTargetConnect() auf die SPB-Funktion gemuxt werden.

SpbCx setzt die Kontakt-Multiplexing-Konfiguration im IRP_MJ_CLOSE-Handler direkt nach dem Aufrufen des EvtSpbTargetDisconnect()-Rückrufs des Controllertreibers zurück. Dies führt dazu, dass Kontakte jedes Mal auf die SPB-Funktion gemuxt werden, wenn ein Peripherietreiber ein Handle für den SPB-Controllertreiber öffnet, und diese Zuweisung per Multiplexing aufgehoben wird, wenn der Peripherietreiber das Handle schließt.

SerCx verhält sich ähnlich. SerCx ruft alle MsftFunctionConfig()-Ressourcen in seinem IRP_MJ_CREATE-Handler direkt vor dem Aufrufen des EvtSerCx2FileOpen()-Rückrufs des Controllertreibers ab, und gibt alle Ressourcen im IRP_MJ_CLOSE-Handler direkt nach dem Aufrufen des EvtSerCx2FileClose-Rückrufs des Controllertreibers frei.

Die Auswirkung des dynamischen Kontakt-Multiplexings für SerCx- und SpbCx-Controllertreiber besteht darin, dass sie in der Lage sein müssen, Kontakte zu tolerieren, deren Zuweisung zur SPB-/UART-Funktion per Multiplexing zu bestimmten Zeiten aufgehoben wird. Controllertreiber müssen davon ausgehen, dass Kontakte erst gemuxt werden, wenn EvtSpbTargetConnect() oder EvtSerCx2FileOpen() aufgerufen wird. Kontakte werden während der folgenden Rückrufe nicht zwingenderweise auf die SPB-/UART-Funktion gemuxt. Die folgende Liste ist nicht vollständig, stellt jedoch die am häufigsten von Controllertreibern implementierten PNP-Routinen dar.

  • DriverEntry
  • EvtDriverDeviceAdd
  • EvtDevicePrepareHardware/EvtDeviceReleaseHardware
  • EvtDeviceD0Entry/EvtDeviceD0Exit

Überprüfung

Wenn Sie RhProxy testen möchten, ist es hilfreich, die folgende Schritt-für-Schritt-Anweisung zu befolgen.

  1. Überprüfen Sie, ob alle SpbCx-, GpioClx- und SerCx-Controllertreiber ordnungsgemäß geladen und ausgeführt werden.
  2. Überprüfen Sie, ob rhproxy im System vorhanden ist. In einigen Editionen und Builds von Windows ist RhProxy nicht verfügbar.
  3. Kompilieren und laden Sie Ihren RhProxy-Knoten mit ACPITABL.dat.
  4. Überprüfen Sie, ob der rhproxy-Geräteknoten vorhanden ist.
  5. Überprüfen Sie, ob rhproxy geladen und gestartet wird.
  6. Überprüfen Sie, ob die erwarteten Geräte für den Benutzermodus verfügbar gemacht werden.
  7. Stellen Sie sicher, dass Sie über die Befehlszeile mit jedem Gerät interagieren können.
  8. Stellen Sie sicher, dass Sie über eine UWP-App (Universelle Windows-Plattform) mit jedem Gerät interagieren können.
  9. Führen Sie HLK-Tests (Windows Hardware Lab Kit) aus.

Überprüfen der Controllertreiber

RhProxy macht andere Geräte im System für den Benutzermodus verfügbar und funktioniert daher nur, wenn diese Geräte bereits in Betrieb sind. Als Erstes überprüfen Sie daher, ob die Geräte (I2C-, SPI- und GPIO-Controller), die Sie verfügbar machen möchten, bereits funktionieren.

Führen Sie an der Eingabeaufforderung den folgenden Befehl aus:

devcon status *

Sehen Sie sich die Ausgabe an, und stellen Sie sicher, dass alle relevanten Geräte gestartet wurden. Wenn für ein Gerät ein Problemcode angezeigt wird, müssen Sie eine Problembehandlung ausführen und herausfinden, weshalb das Gerät nicht geladen wird. Alle Geräte sollten während der ersten Aktivierung der Plattform aktiviert worden sein. Die Problembehandlung für SpbCx-, GpioClx- oder SerCx-Controllertreiber geht über den Rahmen dieses Dokuments hinaus.

Überprüfen, ob RhProxy im System vorhanden ist

Vergewissern Sie sich, dass der rhproxy-Dienst im System vorhanden ist.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Wenn der Registrierungsschlüssel nicht vorhanden ist, ist RhProxy nicht in Ihrem System vorhanden. RhProxy ist in allen IoT Core-Builds und Windows Enterprise Build 15063 und höher vorhanden.

Kompilieren und Laden der ASL-Datei mit „ACPITABL.dat“

Nachdem Sie einen RhProxy-ASL-Knoten erstellt haben, ist es an der Zeit, ihn zu kompilieren und zu laden. Sie können den RhProxy-Knoten in eine eigenständige AML-Datei kompilieren, die an die ACPI-Systemtabellen angefügt werden kann. Wenn Sie Zugriff auf die ACPI-Quellen Ihres Systems haben, können Sie alternativ den RhProxy-Knoten direkt in die ACPI-Tabellen Ihrer Plattform einfügen. Bei der ersten Aktivierung kann es jedoch einfacher sein, ACPITABL.dat zu verwenden.

  1. Erstellen Sie eine Datei namens „yourboard.asl“, und fügen Sie den RHPX-Geräteknoten in einem DefinitionBlock ein:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Laden Sie das Windows-Treiberkit (WDK) herunter, und suchen Sie unter C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify nach asl.exe.

  3. Führen Sie den folgenden Befehl aus, um „ACPITABL.dat“ zu generieren:

    asl.exe yourboard.asl
    
  4. Kopieren Sie die resultierende Datei „ACPITABL.dat“ auf Ihrem getesteten System in „c:\windows\system32“.

  5. Aktivieren Sie „testsigning“ auf dem getesteten System:

    bcdedit /set testsigning on
    
  6. Starten Sie das getestete System neu. Das System fügt die in „ACPITABL.dat“ definierten ACPI-Tabellen an die Systemfirmwaretabellen an.

Überprüfen, ob der RhProxy-Geräteknoten vorhanden ist

Führen Sie den folgenden Befehl aus, um den RhProxy-Geräteknoten aufzulisten.

devcon status *msft8000

Die Ausgabe von „devcon“ sollte angeben, dass das Gerät vorhanden ist. Wenn der Geräteknoten nicht vorhanden ist, wurden die ACPI-Tabellen nicht erfolgreich zum System hinzugefügt.

Überprüfen Sie, ob RhProxy geladen und gestartet wird.

Überprüfen des Status von RhProxy:

devcon status *msft8000

Wenn RhProxy laut Ausgabe gestartet wurde, wurde RhProxy geladen und erfolgreich gestartet. Wenn ein Problemcode angezeigt wird, müssen Sie diesen untersuchen. Im Folgenden finden Sie einige häufige Problemcodes:

  • Problem 51 – CM_PROB_WAITING_ON_DEPENDENCY: Das System startet RhProxy nicht, da eine der Abhängigkeiten nicht geladen werden konnte. Dies bedeutet, dass entweder die an RhProxy übergebenen Ressourcen auf ungültige ACPI-Knoten verweisen, oder die Zielgeräte werden nicht gestartet. Überprüfen Sie zunächst, ob alle Geräte erfolgreich ausgeführt werden (siehe obigen Abschnitt „Überprüfen der Controllertreiber“). Überprüfen Sie dann Ihre ASL, und stellen Sie sicher, dass alle Ressourcenpfade (z. B. \_SB.I2C1) korrekt sind und auf gültige Knoten in Ihrer DSDT (Differentiated System Description Table) verweisen.
  • Problem 10 – CM_PROB_FAILED_START: RhProxy konnte nicht gestartet werden, wahrscheinlich aufgrund eines Problems mit der Ressourcenanalyse. Navigieren Sie zu Ihrer ASL-Datei, überprüfen Sie die Ressourcenindizes in den gerätespezifischen Daten (DSD), und vergewissern Sie sich, dass GPIO-Ressourcen in aufsteigender Reihenfolge angegeben sind.

Überprüfen Sie, ob die erwarteten Geräte für den Benutzermodus verfügbar gemacht werden.

Da RhProxy nun ausgeführt wird, sollten Geräteschnittstellen erstellt worden sein, auf die über den Benutzermodus zugegriffen werden kann. Wir verwenden mehrere Befehlszeilentools, um Geräte aufzulisten und zu überprüfen, ob sie vorhanden sind.

Klonen Sie das Repository https://github.com/ms-iot/samples, und erstellen Sie die Beispiele GpioTestTool, I2cTestTool, SpiTestTool und Mincomm. Kopieren Sie die Tools auf Ihr getestetes Gerät, und verwenden Sie die folgenden Befehle, um Geräte aufzulisten.

I2cTestTool.exe -list
SpiTestTool.exe -list
GpioTestTool.exe -list
MinComm.exe -list

Ihre Geräte und Anzeigenamen sollten aufgeführt sein. Falls die richtigen Geräte und Anzeigenamen nicht angezeigt werden, überprüfen Sie Ihre ASL-Datei.

Überprüfen jedes Geräts in der Befehlszeile

Im nächsten Schritt werden die Befehlszeilentools verwendet, um die Geräte zu öffnen und mit ihnen zu interagieren.

I2CTestTool-Beispiel:

I2cTestTool.exe 0x55 I2C1
> write {1 2 3}
> read 3
> writeread {1 2 3} 3

SpiTestTool-Beispiel:

SpiTestTool.exe -n SPI1
> write {1 2 3}
> read 3

GpioTestTool-Beispiel:

GpioTestTool.exe 12
> setdrivemode output
> write 0
> write 1
> setdrivemode input
> read
> interrupt on
> interrupt off

MinComm-Beispiel (seriell). Verbinden Sie vor der Ausführung Rx mit Tx>:

MinComm "\\?\ACPI#FSCL0007#3#{86e0d1e0-8089-11d0-9ce4-08003e301f73}\0000000000000006"
(type characters and see them echoed back)

Überprüfen jedes Geräts über eine UWP-App

Verwenden Sie die folgenden Beispiele, um zu überprüfen, ob Geräte über UWP funktionieren.

Ausführen der HLK-Tests

Laden Sie das Hardware Lab Kit (HLK) herunter. Die folgenden Tests sind verfügbar:

Wenn Sie den RhProxy-Geräteknoten im HLK-Manager auswählen, werden die entsprechenden Tests automatisch ausgewählt.

Wählen Sie im HLK-Manager die Option „Ressourcenhub-Proxygerät“ aus:

Screenshot of the Windows Hardware Lab Kit showing the Selection tab with the Resource Hub proxy device option selected.

Klicken Sie dann auf die Registerkarte „Tests“, und wählen Sie I2C WinRT-, Gpio WinRT- und Spi WinRT-Tests aus.

Screenshot of the Windows Hardware Lab Kit showing the Tests tab with the G P I O Win R T Functional and Stress Tests option selected.

Klicken Sie auf Ausgewählte ausführen. Sie können weitere Dokumentation zu jedem Test anzeigen, indem Sie mit der rechten Maustaste auf den Test klicken und dann „Testbeschreibung“ auswählen.

Ressourcen

Anhang

Anhang A: Raspberry Pi-ASL-Eintrag

Siehe auch Raspberry Pi 2- und 3-Kontaktzuordnungen

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{

    Scope (\_SB)
    {
        //
        // RHProxy Device Node to enable WinRT API
        //
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE0  - GPIO 8  - Pin 24
                    0,                     // Device selection (CE0)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 1
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE1  - GPIO 7  - Pin 26
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 2
                SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                                           // MOSI - GPIO 20 - Pin 38
                                           // MISO - GPIO 19 - Pin 35
                                           // CE1  - GPIO 17 - Pin 11
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data
                // Index 3
                I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
                    0xFFFF,                // SlaveAddress: placeholder
                    ,                      // SlaveMode: default to ControllerInitiated
                    0,                     // ConnectionSpeed: placeholder
                    ,                      // Addressing Mode: placeholder
                    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
                    ,
                    ,
                    )                      // VendorData

                // Index 4 - GPIO 4 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 4 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 4 }
                // Index 6 - GPIO 5 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 5 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 5 }
                // Index 8 - GPIO 6 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 6 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 6 }
                // Index 10 - GPIO 12 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 12 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 12 }
                // Index 12 - GPIO 13 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 13 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 13 }
                // Index 14 - GPIO 16 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 16 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 16 }
                // Index 16 - GPIO 18 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 18 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 18 }
                // Index 18 - GPIO 22 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 22 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 22 }
                // Index 20 - GPIO 23 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 23 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 23 }
                // Index 22 - GPIO 24 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 24 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 24 }
                // Index 24 - GPIO 25 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 25 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 25 }
                // Index 26 - GPIO 26 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 26 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 26 }
                // Index 28 - GPIO 27 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 27 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 27 }
                // Index 30 - GPIO 35 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 35 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 35 }
                // Index 32 - GPIO 47 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 47 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 47 }
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // Reference http://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
                    // SPI 0
                    Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},                       // Index 0 & 1
                    Package(2) { "SPI0-MinClockInHz", 7629 },                               // 7629 Hz
                    Package(2) { "SPI0-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // SPI 1
                    Package(2) { "bus-SPI-SPI1", Package() { 2 }},                          // Index 2
                    Package(2) { "SPI1-MinClockInHz", 30518 },                              // 30518 Hz
                    Package(2) { "SPI1-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI1-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // I2C1
                    Package(2) { "bus-I2C-I2C1", Package() { 3 }},
                    // GPIO Pin Count and supported drive modes
                    Package (2) { "GPIO-PinCount", 54 },
                    Package (2) { "GPIO-UseDescriptorPinNumbers", 1 },
                    Package (2) { "GPIO-SupportedDriveModes", 0xf },                        // InputHighImpedance, InputPullUp, InputPullDown, OutputCmos
                }
            })
        }
    }
}

Anhang B: MinnowBoardMax-ASL-Eintrag

Siehe auch MinnowBoard Max Pin Mappings (Maximale MinnowBoard-Kontaktzuordnungen)

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
    Scope (\_SB)
    {
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(            // Pin 5, 7, 9 , 11 of JP1 for SIO_SPI
                    1,                     // Device selection
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    8,                     // databit len
                    ControllerInitiated,   // slave mode
                    8000000,               // Connection speed
                    ClockPolarityLow,      // Clock polarity
                    ClockPhaseSecond,      // clock phase
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                    ResourceConsumer,      // Resource usage
                    JSPI,                  // DescriptorName: creates name for offset of resource descriptor
                    )                      // Vendor Data

                // Index 1
                I2CSerialBus(            // Pin 13, 15 of JP1, for SIO_I2C5 (signal)
                    0xFF,                  // SlaveAddress: bus address
                    ,                      // SlaveMode: default to ControllerInitiated
                    400000,                // ConnectionSpeed: in Hz
                    ,                      // Addressing Mode: default to 7 bit
                    "\\_SB.I2C6",          // ResourceSource: I2C bus controller name (For MinnowBoard Max, hardware I2C5(0-based) is reported as ACPI I2C6(1-based))
                    ,
                    ,
                    JI2C,                  // Descriptor Name: creates name for offset of resource descriptor
                    )                      // VendorData

                // Index 2
                UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    ,                      // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT2",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR2,                  // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 3
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {0}  // Pin 21 of JP1 (GPIO_S5[00])
                // Index 4
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {0}

                // Index 5
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {1}  // Pin 23 of JP1 (GPIO_S5[01])
                // Index 6
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {1}

                // Index 7
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {2}  // Pin 25 of JP1 (GPIO_S5[02])
                // Index 8
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {2}

                // Index 9
                UARTSerialBus(           // Pin 6, 8, 10, 12 of JP1, for SIO_UART1
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    FlowControlHardware,   // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT1",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR1,              // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 10
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {62}  // Pin 14 of JP1 (GPIO_SC[62])
                // Index 11
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {62}

                // Index 12
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {63}  // Pin 16 of JP1 (GPIO_SC[63])
                // Index 13
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {63}

                // Index 14
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {65}  // Pin 18 of JP1 (GPIO_SC[65])
                // Index 15
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {65}

                // Index 16
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {64}  // Pin 20 of JP1 (GPIO_SC[64])
                // Index 17
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {64}

                // Index 18
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {94}  // Pin 22 of JP1 (GPIO_SC[94])
                // Index 19
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {94}

                // Index 20
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {95}  // Pin 24 of JP1 (GPIO_SC[95])
                // Index 21
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {95}

                // Index 22
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {54}  // Pin 26 of JP1 (GPIO_SC[54])
                // Index 23
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {54}
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // SPI Mapping
                    Package(2) { "bus-SPI-SPI0", Package() { 0 }},

                    Package(2) { "SPI0-MinClockInHz", 100000 },
                    Package(2) { "SPI0-MaxClockInHz", 15000000 },
                    // SupportedDataBitLengths takes a list of support data bit length
                    // Example : Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8, 7, 16 }},
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }},
                     // I2C Mapping
                    Package(2) { "bus-I2C-I2C5", Package() { 1 }},
                    // UART Mapping
                    Package(2) { "bus-UART-UART2", Package() { 2 }},
                    Package(2) { "bus-UART-UART1", Package() { 9 }},
                }
            })
        }
    }
}

Anhang C: PowerShell-Beispielskript zum Generieren von GPIO-Ressourcen

Das folgende Skript kann verwendet werden, um die GPIO-Ressourcendeklarationen für Raspberry Pi zu generieren:

$pins = @(
    @{PinNumber=4;PullConfig='PullUp'},
    @{PinNumber=5;PullConfig='PullUp'},
    @{PinNumber=6;PullConfig='PullUp'},
    @{PinNumber=12;PullConfig='PullDown'},
    @{PinNumber=13;PullConfig='PullDown'},
    @{PinNumber=16;PullConfig='PullDown'},
    @{PinNumber=18;PullConfig='PullDown'},
    @{PinNumber=22;PullConfig='PullDown'},
    @{PinNumber=23;PullConfig='PullDown'},
    @{PinNumber=24;PullConfig='PullDown'},
    @{PinNumber=25;PullConfig='PullDown'},
    @{PinNumber=26;PullConfig='PullDown'},
    @{PinNumber=27;PullConfig='PullDown'},
    @{PinNumber=35;PullConfig='PullUp'},
    @{PinNumber=47;PullConfig='PullUp'})

# generate the resources
$FIRST_RESOURCE_INDEX = 4
$resourceIndex = $FIRST_RESOURCE_INDEX
$pins | % {
    $a = @"
// Index $resourceIndex - GPIO $($_.PinNumber) - $($_.Name)
GpioIO(Shared, $($_.PullConfig), , , , "\\_SB.GPI0", , , , ) { $($_.PinNumber) }
GpioInt(Edge, ActiveBoth, Shared, $($_.PullConfig), 0, "\\_SB.GPI0",) { $($_.PinNumber) }
"@
    Write-Host $a
    $resourceIndex += 2;
}