Partager via


Activer l’accès en mode utilisateur à GPIO, I2C et SPI

Dans Windows 10 et versions ultérieures, les API sont fournies avec un accès direct du mode utilisateur à l’entrée/sortie à usage général (GPIO), Inter-Integrated Circuit (I2C), SPI (Serial Peripheral Interface) et UART (Universal Receiver-Transmitter- Transmitter). Les cartes de développement telles que Raspberry Pi 2 exposent un sous-ensemble de ces connexions, ce qui vous permet d’étendre un module de calcul de base avec des circuits personnalisés pour traiter une application particulière. Ces bus de bas niveau sont généralement partagés avec d'autres fonctions critiques embarquées, avec seulement un sous-ensemble de broches GPIO et de bus exposés sur les en-têtes. Pour préserver la stabilité du système, il est nécessaire de spécifier quelles broches et bus sont sûrs pour la modification par les applications en mode utilisateur.

Ce document explique comment spécifier cette configuration dans Advanced Configuration and Power Interface (ACPI) et fournit des outils pour vérifier que la configuration a été spécifiée correctement.

Important

Le public de ce document est constitué de développeurs UEFI (Unified Extensible Firmware Interface) et ACPI. Certaines connaissances en matière d'ACPI, de rédaction en langage source ACPI (ASL) et de SpbCx/GpioClx sont supposées.

L'accès en mode utilisateur aux bus de bas niveau sur Windows est acheminé à travers les infrastructures GpioClx et SpbCx existantes. Un nouveau pilote appelé RhProxy, disponible sur Windows IoT Core et Windows Enterprise, expose les ressources GpioClx et SpbCx au mode utilisateur. Pour activer les API, un nœud d’appareil pour rhproxy doit être déclaré dans vos tables ACPI avec chacune des ressources GPIO et SPB qui doivent être exposées au mode utilisateur. Ce document décrit la création et la vérification de l’ASL.

ASL par exemple

Examinons la déclaration de nœud d’appareil rhproxy sur Raspberry Pi 2. Tout d’abord, créez la déclaration d’appareil ACPI dans l’étendue \_SB.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID : ID matériel. Définissez-le sur un ID matériel spécifique au fournisseur.
  • _CID – ID compatible. Doit être « MSFT8000 ».
  • _UID : ID unique. Défini sur 1.

Ensuite, nous déclarons chacune des ressources GPIO et SPB qui doivent être exposées au mode utilisateur. L’ordre dans lequel les ressources sont déclarées est important, car les index de ressources sont utilisés pour associer des propriétés aux ressources. S’il existe plusieurs bus I2C ou SPI exposés, le premier bus déclaré est considéré comme le bus « par défaut » pour ce type et sera l’instance retournée par les méthodes GetDefaultAsync() de Windows.Devices.I2c.I2cController et Windows.Devices.Spi.SpiController.

SPI

Raspberry Pi a deux bus SPI exposés. SPI0 a deux lignes de sélection de puce matérielle et SPI1 a une ligne de sélection de puce matérielle. Une déclaration de ressource SPISerialBus() est requise pour chaque ligne de sélection de puce pour chaque bus. Les deux déclarations de ressources SPISerialBus suivantes concernent les deux lignes de sélection de puce sur SPI0. Le champ DeviceSelection contient une valeur unique que le pilote interprète comme identificateur de ligne de sélection de puce matériel. La valeur exacte que vous avez placée dans le champ DeviceSelection dépend de la façon dont votre pilote interprète ce champ du descripteur de connexion ACPI.

Remarque

Cet article contient des références au terme esclave , terme que Microsoft ne condone pas, et a cessé d’utiliser de nouveaux produits et de la documentation. Lorsque le terme est supprimé du logiciel, nous le supprimerons de cet article.

// 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

Comment les logiciels savent-ils que ces deux ressources doivent être associées au même bus ? Le mappage entre le nom convivial du bus et l’index de ressource est spécifié dans le DSD :

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

Cela crée un bus nommé « SPI0 » avec deux lignes de sélection de puce : les index de ressources 0 et 1. Plusieurs propriétés supplémentaires sont requises pour déclarer les fonctionnalités du bus SPI.

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

Les propriétés MinClockInHz et MaxClockInHz spécifient les vitesses d’horloge minimales et maximales prises en charge par le contrôleur. L’API empêche les utilisateurs de spécifier des valeurs en dehors de cette plage. La vitesse de l'horloge est transmise à votre pilote SPB dans le champ _SPE du descripteur de connexion (section ACPI 6.4.3.8.2.2).

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

La propriété SupportedDataBitLengths répertorie les longueurs de bits de données prises en charge par le contrôleur. Plusieurs valeurs peuvent être spécifiées dans une liste séparée par des virgules. L’API empêche les utilisateurs de spécifier des valeurs en dehors de cette liste. La longueur du bit de données est transmise à votre pilote SPB dans le champ _LEN du descripteur de connexion (section ACPI 6.4.3.8.2.2).

Vous pouvez considérer ces déclarations de ressources comme des « modèles ». Certains des champs sont corrigés au démarrage du système, tandis que d’autres sont spécifiés dynamiquement au moment de l’exécution. Les champs suivants du descripteur SPISerialBus sont fixes :

  • Sélection de l'appareil
  • PolaritéDeSélectionD'Appareil
  • WireMode
  • SlaveMode
  • Source de Ressources

Les champs suivants sont des espaces réservés pour les valeurs spécifiées par l’utilisateur au moment de l’exécution :

  • LongueurDesBitsDeDonnées
  • Vitesse de connexion
  • Polarité de l'Horloge
  • ClockPhase

Étant donné que SPI1 ne contient qu’une seule ligne de sélection de puce, une seule ressource SPISerialBus() est déclarée :

// 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

La déclaration de nom convivial associée , qui est requise, est spécifiée dans le DSD et fait référence à l’index de cette déclaration de ressource.

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

Cela crée un bus nommé « SPI1 » et l’associe à l’index de ressource 2.

Exigences du pilote SPI

  • Doit utiliser SpbCx ou être compatible avec SpbCx
  • Doit avoir réussi les tests MITT SPI
  • Doit prendre en charge la vitesse d’horloge de 4 MHz
  • Doit prendre en charge la longueur des données 8 bits
  • Doit prendre en charge tous les modes SPI : 0, 1, 2, 3

I2C

Ensuite, nous déclarons les ressources I2C. Raspberry Pi expose un seul bus I2C sur les broches 3 et 5.

// 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

La déclaration de nom convivial associée , qui est requise, est spécifiée dans le DSD :

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

Cela déclare un bus I2C portant le nom convivial « I2C1 » qui fait référence à l’index de ressource 3, qui est l’index de la ressource I2CSerialBus() que nous avons déclarée ci-dessus.

Les champs suivants du descripteur I2CSerialBus() sont fixes :

  • SlaveMode
  • Source de Ressources

Les champs suivants sont des espaces réservés pour les valeurs spécifiées par l’utilisateur au moment de l’exécution.

  • SlaveAddress
  • Vitesse de connexion
  • Mode d'Adressage

Exigences du pilote I2C

  • Doit utiliser SpbCx ou être compatible avec SpbCx
  • Doit avoir réussi les tests MITT I2C
  • Doit prendre en charge l’adressage 7 bits
  • Doit prendre en charge la vitesse d’horloge de 100kHz
  • Doit prendre en charge la vitesse d’horloge de 400 kHz

GPIO

Ensuite, nous déclarons toutes les broches GPIO exposées au mode utilisateur. Nous vous proposons les conseils suivants pour déterminer les broches à exposer :

  • Déclarez toutes les broches sur les en-têtes exposés.
  • Déclarez les broches connectées à des fonctions intégrées utiles telles que les boutons et les LED.
  • Ne déclarez pas les broches réservées aux fonctions système ou qui ne sont pas connectées à quoi que ce soit.

Le bloc suivant d'ASL déclare deux broches GPIO : GPIO4 et GPIO5. Les autres broches ne sont pas affichées ici pour des raisons de concision. L’annexe C contient un exemple de script PowerShell qui peut être utilisé pour générer les ressources GPIO.

// 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 }

Les exigences suivantes doivent être observées lors de la déclaration des broches GPIO :

  • Seuls les contrôleurs GPIO mappés en mémoire sont pris en charge. Les contrôleurs GPIO interfacenés sur I2C/SPI ne sont pas pris en charge. Le pilote du contrôleur est un contrôleur mappé en mémoire s’il définit l’indicateur MemoryMappedController dans la structure CLIENT_CONTROLLER_BASIC_INFORMATION en réponse au rappel CLIENT_QueryControllerBasicInformation.
  • Chaque broche nécessite à la fois une ressource de type GpioIO et une ressource de type GpioInt. La ressource GpioInt doit immédiatement suivre la ressource GpioIO et doit faire référence au même numéro de broche.
  • Les ressources GPIO doivent être ordonnées par numéro de broche croissant.
  • Chaque ressource GpioIO et GpioInt doit contenir exactement un numéro de broche dans la liste des broches.
  • Le champ ShareType des deux descripteurs doit être Partagé
  • Le champ EdgeLevel du descripteur GpioInt doit être Edge
  • Le champ ActiveLevel du descripteur GpioInt doit être ActiveBoth
  • Champ PinConfig
    • Doit être identique dans les descripteurs GpioIO et GpioInt
    • Doit être l’un des PullUp, PullDown ou PullNone. Il ne peut pas s’agir de PullDefault.
    • La configuration de tirage doit correspondre à l'état de mise sous tension de la broche. Le fait de placer la broche dans le mode de tirage spécifié à partir de l'état sous tension ne doit pas modifier l'état de la broche. Par exemple, si la feuille de données spécifie que la broche est fournie avec une résistance de tirage, spécifiez PinConfig comme PullUp.

Le code d’initialisation du microprogramme, UEFI et pilote ne doit pas changer l’état d’une broche à partir de son état d’alimentation pendant le démarrage. Seul l’utilisateur sait ce qui est attaché à une broche et, par conséquent, quelles transitions d’état sont sécurisées. L’état d’alimentation de chaque broche doit être documenté afin que les utilisateurs puissent concevoir du matériel qui s’interface correctement avec une broche. Une broche ne doit pas changer d’état de façon inattendue pendant le démarrage.

Modes de disque pris en charge

Si votre contrôleur GPIO prend en charge les résistances intégrées pull-up et pull down en plus de l’entrée d’impedance élevée et de la sortie CMOS, vous devez spécifier cela avec la propriété SupportedDriveModes facultative.

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

La propriété SupportedDriveModes indique quels modes de fonctionnement sont pris en charge par le contrôleur GPIO. Dans l’exemple ci-dessus, tous les modes de conduite suivants sont pris en charge. La propriété est un masque de bits des valeurs suivantes :

Valeur de l’indicateur Mode de conduite Descriptif
0x1 Entrée à haute impédance La broche prend en charge l’entrée à haute impédance, qui correspond à la valeur « PullNone » dans ACPI.
0x2 EntréePullUp La broche prend en charge une résistance de tirage intégrée, qui correspond à la valeur « PullUp » dans ACPI.
0x4 Menu déroulant d'entrée La broche prend en charge une résistance pull-down intégrée, qui correspond à la valeur « PullDown » dans ACPI.
0x8 OutputCmos La broche prend en charge la génération de niveaux forts élevés et faibles (par opposition à une sortie à drain ouvert).

InputHighImpedance et OutputCmos sont pris en charge par presque tous les contrôleurs GPIO. Si la propriété SupportedDriveModes n’est pas spécifiée, il s’agit de la valeur par défaut.

Si un signal GPIO passe par un convertisseur de niveau avant d’atteindre un en-tête exposé, déclarez les modes de pilotage pris en charge par le SOC, même si le mode de pilotage ne serait pas observable sur l’en-tête externe. Par exemple, si une broche passe par un shifteur de niveau bidirectionnel qui fait apparaître une broche comme un drain ouvert avec une extraction résistive vers le haut, vous n’observerez jamais un état d’impedance élevé sur l’en-tête exposé même si la broche est configurée comme une entrée d’impedance élevée. Vous devez toujours déclarer que l’épingle prend en charge l’entrée d’impedance élevée.

Numérotation des broches

Windows prend en charge deux schémas de numérotation de broches.

  • Numérotation de broche séquentielle : les utilisateurs voient des nombres tels que 0, 1, 2... jusqu’au nombre de broches exposées. 0 est la première ressource GpioIo déclarée dans ASL, 1 est la deuxième ressource GpioIo déclarée dans ASL, et ainsi de suite.
  • Numérotation de broche native : les utilisateurs voient les numéros de broche spécifiés dans les descripteurs GpioIo, par exemple, 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

La propriété UseDescriptorPinNumbers indique à Windows d’utiliser la numérotation de broche native au lieu de la numérotation séquentielle. Si la propriété UseDescriptorPinNumbers n’est pas spécifiée ou que sa valeur est égale à zéro, Windows utilise par défaut la numérotation de broche séquentielle.

Lorsque la numérotation de broche native est utilisée, vous devez également spécifier la propriété PinCount.

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

La propriété PinCount doit correspondre à la valeur retournée par la propriété TotalPins dans le rappel CLIENT_QueryControllerBasicInformation du pilote GpioClx.

Choisissez le schéma de numérotation le plus compatible avec la documentation déjà publiée pour votre carte de circuit. Par exemple, Raspberry Pi utilise la numérotation de pin native, car de nombreux diagrammes de pin existants utilisent les numéros de pin BCM2835. MinnowBoardMax utilise la numérotation séquentielle des broches, car il existe peu de diagrammes de broches existants, et la numérotation séquentielle des broches simplifie l’expérience du développeur, car seules 10 des plus de 200 broches sont exposées. La décision d’utiliser la numérotation de broche séquentielle ou native doit viser à réduire la confusion des développeurs.

Configuration requise du pilote GPIO

  • Doit utiliser GpioClx
  • Doit être mappé sur la mémoire SOC
  • Il faut utiliser la gestion des interruptions ActiveBoth émulée

UART (Récepteur-Transmetteur Asynchrone Universel)

Si votre pilote UART utilise SerCx ou SerCx2, vous pouvez utiliser rhproxy pour exposer le pilote en mode utilisateur. Les pilotes UART qui créent une interface de périphérique de type GUID_DEVINTERFACE_COMPORT n’ont pas besoin d’utiliser rhproxy. La boîte de réception Serial.sys pilote est l’un de ces cas.

Pour exposer un UART de style SerCxen mode utilisateur, déclarez une ressource UARTSerialBus comme suit.

// 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
    ,
    ,
    ,
    )

Seul le champ ResourceSource est fixe, tandis que tous les autres champs sont des espaces réservés pour les valeurs spécifiées au moment de l'exécution par l'utilisateur.

La déclaration de nom convivial associée est la suivante :

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

Cela affecte le nom convivial « UART2 » au contrôleur, qui est l’identificateur que les utilisateurs utiliseront pour accéder au bus à partir du mode utilisateur.

Multiplexage de broche en temps réel

Le multiplexage des broches consiste à utiliser la même broche physique pour différentes fonctions. Plusieurs périphériques à puce différents, tels qu’un contrôleur I2C, un contrôleur SPI et un contrôleur GPIO, peuvent être routés vers la même broche physique sur un SOC. Le bloc mux contrôle quelle fonction est active sur la broche à un moment donné. Traditionnellement, le microprogramme est chargé d’établir des attributions de fonction au démarrage, et cette affectation reste statique par le biais de la session de démarrage. Le multiplexage en temps réel des broches ajoute la capacité de reconfigurer les attributions de fonctions des broches lors de l’exécution. Permettre aux utilisateurs de choisir la fonction d'un pin en temps réel accélère le développement en permettant aux utilisateurs de reconfigurer rapidement les pins d'une carte, et permet au matériel de prendre en charge un plus large éventail d'applications qu'une configuration statique.

Les utilisateurs bénéficient de la prise en charge du multiplexage pour GPIO, I2C, SPI et UART sans avoir à écrire de code supplémentaire. Lorsqu’un utilisateur ouvre un gpIO ou un bus à l’aide de OpenPin() ou FromIdAsync(), les broches physiques sous-jacentes sont automatiquement muxées à la fonction demandée. Si les broches sont déjà utilisées par une autre fonction, l’appel OpenPin() ou FromIdAsync() échouera. Lorsque l’utilisateur ferme l’appareil en supprimant l’objet GpioPin, I2cDevice, SpiDeviceou l’objet SerialDevice , les broches sont libérées, ce qui permet de les ouvrir ultérieurement pour une autre fonction.

Windows contient une prise en charge native pour le multiplexage de broches dans les frameworks GpioClx, SpbCxet SerCx. Ces frameworks fonctionnent ensemble pour changer automatiquement une broche vers la fonction correcte lorsqu'une broche ou un bus GPIO est accédé. L’accès aux broches est arbitré pour éviter les conflits entre plusieurs clients. Outre cette prise en charge intégrée, les interfaces et les protocoles pour le multiplexage de broches sont à usage général et peuvent être étendus pour prendre en charge d’autres appareils et scénarios.

Ce document décrit d’abord les interfaces et protocoles sous-jacents impliqués dans le multiplexage de broche, puis explique comment ajouter la prise en charge du muxing de broche aux pilotes de contrôleur GpioClx, SpbCx et SerCx.

Architecture de multiplexage de broches

Cette section décrit les interfaces et protocoles sous-jacents impliqués dans le multiplexage de broche. La connaissance des protocoles sous-jacents n’est pas nécessairement nécessaire pour prendre en charge le multiplexage de broche avec les pilotes GpioClx/SpbCx/SerCx. Pour plus d'informations sur la prise en charge du multiplexage des broches avec les pilotes GpioCls/SpbCx/SerCx, consultez Implémentation de la prise en charge du multiplexage des broches dans les pilotes clients GpioClx et Utilisation de la prise en charge du multiplexage dans les pilotes de contrôleur SpbCx et SerCx.

Le multiplexage de broches est réalisé par la coopération de plusieurs composants.

  • Serveurs de multiplexage de broches : ce sont des pilotes qui contrôlent le module de contrôle du multiplexage de broches. Les serveurs de multiplexage de broches reçoivent des demandes de multiplexage de broches provenant de clients via des requêtes pour réserver des ressources de multiplexage (via IRP_MJ_CREATE) et des demandes pour modifier la fonction d'une broche (via des requêtes *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS). Le serveur de multiplexage de broche est généralement le pilote GPIO, car le bloc de multiplexage fait parfois partie du bloc GPIO. Même si le bloc de multiplexage est un périphérique distinct, le pilote GPIO est un emplacement logique pour intégrer la fonctionnalité de multiplexage.
  • Clients de multiplexage de broches : il s’agit de pilotes qui consomment le multiplexage de broches. Les clients de multiplexage de broche reçoivent des ressources de multiplexage de broche à partir du microprogramme ACPI. Les ressources de multiplexage de broche sont un type de ressource de connexion et sont gérées par le hub de ressources. Les clients de multiplexage de broche réservent des ressources de multiplexage de broche en ouvrant un descripteur pour la ressource. Pour effectuer une modification matérielle, les clients doivent valider la configuration en envoyant une demande de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Les clients libèrent les ressources de multiplexage des broches en fermant le handle, auquel point la configuration du multiplexage est rétablie à son état par défaut.
  • Micrologiciel ACPI : spécifie la configuration de multiplexage avec les ressources MsftFunctionConfig(). Les ressources MsftFunctionConfig décrivent les broches et la configuration de multiplexage requises par un client. Les ressources MsftFunctionConfig contiennent le numéro de fonction, la configuration de tirage et la liste des numéros de broches. Les ressources MsftFunctionConfig sont fournies aux clients de multiplexage de broches en tant que ressources matérielles, qui sont reçues par les pilotes dans leur rappel PrepareHardware de la même manière que les ressources de connexion GPIO et SPB. Les clients reçoivent un ID de hub de ressources qui peut être utilisé pour ouvrir un handle à la ressource.

Vous devez passer le commutateur de ligne de commande /MsftInternal à asl.exe pour compiler des fichiers ASL contenant MsftFunctionConfig() descripteurs, car ces descripteurs sont actuellement examinés par le comité de travail ACPI. Par exemple : asl.exe /MsftInternal dsdt.asl

La séquence d’opérations impliquées dans le multiplexage des broches est illustrée ci-dessous.

interaction client-serveur de multiplexage de pins

  1. Le client reçoit les ressources MsftFunctionConfig du microprogramme ACPI dans son rappel EvtDevicePrepareHardware().
  2. Le client utilise la fonction d’assistance du hub de ressources RESOURCE_HUB_CREATE_PATH_FROM_ID() pour créer un chemin à partir de l’ID de ressource, puis ouvre un handle vers le chemin (à l’aide de ZwCreateFile(), IoGetDeviceObjectPointer()ou WdfIoTargetOpen()).
  3. Le serveur extrait l’ID du hub de ressources à partir du chemin d’accès au fichier à l’aide des fonctions d’assistance du hub de ressources RESOURCE_HUB_ID_FROM_FILE_NAME(), puis interroge le hub de ressources pour obtenir le descripteur de ressource.
  4. Le serveur effectue un arbitrage de partage pour chaque broche du descripteur et termine la demande IRP_MJ_CREATE.
  5. Le client émet une demande de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS sur le handle reçu.
  6. En réponse à IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, le serveur effectue l’opération de multiplexage matériel en rendant la fonction spécifiée active sur chaque broche.
  7. Le client poursuit les opérations qui dépendent de la configuration de multiplexage de broches demandée.
  8. Lorsque le client n’a plus besoin que les broches soient muxées, elle ferme la poignée.
  9. En réponse à la fermeture du handle, le serveur rétablit les broches à leur état initial.

Description du protocole pour les clients de multiplexage de broches

Cette section décrit comment un client utilise la fonctionnalité de multiplexage des broches. Cela ne s’applique pas aux contrôleurs pilotes SerCx et SpbCx, car les frameworks implémentent ce protocole pour le compte des pilotes de contrôleur.

Analyse des ressources

Un pilote WDF reçoit des ressources MsftFunctionConfig() dans sa routine EvtDevicePrepareHardware(). Les ressources MsftFunctionConfig peuvent être identifiées par les champs suivants :

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

Une routine EvtDevicePrepareHardware() peut extraire les ressources MsftFunctionConfig comme suit :

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;
}

Réserver et engager des ressources

Lorsqu’un client souhaite configurer les broches, il réserve et valide la ressource MsftFunctionConfig. L’exemple suivant montre comment un client peut réserver et valider des ressources MsftFunctionConfig.

_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;
}

Le pilote doit stocker le WDFIOTARGET dans l’une de ses zones de contexte afin qu’il puisse être fermé ultérieurement. Lorsque le pilote est prêt à libérer la configuration de muxing, il doit fermer le handle de ressource en appelant WdfObjectDelete()ou WdfIoTargetClose() si vous envisagez de réutiliser WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Lorsque le client ferme son gestionnaire de ressources, les broches sont multiplexées pour revenir à leur état initial et peuvent maintenant être acquises par un autre client.

Description du protocole pour le multiplexage des broches sur les serveurs

Cette section décrit comment un serveur de multiplexage de broches expose sa fonctionnalité aux clients. Cela ne s’applique pas aux pilotes miniport GpioClx, car cette infrastructure implémente ce protocole pour le compte des pilotes clients. Pour des détails sur la manière de prendre en charge le multiplexage des broches dans les pilotes clients GpioClx, consultez Implémentation du support de multiplexage dans les pilotes clients GpioClx.

Gestion des demandes de IRP_MJ_CREATE

Les clients ouvrent un handle vers une ressource lorsqu’ils souhaitent réserver une ressource de multiplexage de broche. Un serveur de muxage de broche reçoit IRP_MJ_CREATE demandes par le biais d’une opération de reparsage à partir du hub de ressources. Le dernier composant du chemin d’accès de la requête IRP_MJ_CREATE contient l’ID du hub de ressources, qui est un entier 64 bits au format hexadécimal. Le serveur doit extraire l’ID du hub de ressources à partir du nom de fichier à l’aide de RESOURCE_HUB_ID_FROM_FILE_NAME() de reshub.h et envoyer IOCTL_RH_QUERY_CONNECTION_PROPERTIES au hub de ressources pour obtenir le descripteur MsftFunctionConfig().

Le serveur doit valider le descripteur et extraire le mode de partage et la liste d’épingles du descripteur. Il doit ensuite effectuer l’arbitrage de partage pour les broches, et s’il réussit, marquer les broches comme réservées avant de terminer la requête.

L'arbitrage de partage réussit globalement si l'arbitrage de partage réussit pour chaque broche dans la liste des broches. La manière d'arbitrer chaque broche doit être la suivante :

  • Si la broche n’est pas déjà réservée, l’arbitrage de partage réussit.
  • Si la broche est déjà réservée à titre exclusif, l’arbitrage de partage échoue.
  • Si la broche est déjà réservée pour un usage partagé,
    • et la demande entrante est partagée, l’arbitrage de partage réussit.
    • et la demande entrante est exclusive, l’arbitrage de partage échoue.

En cas d’échec de l’arbitrage de partage, la demande doit être effectuée avec STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Si l’arbitrage de partage réussit, la requête doit être complétée avec STATUS_SUCCESS.

Notez que le mode de partage de la requête entrante doit être extrait du descripteur MsftFunctionConfig, et non IrpSp->Parameters.Create.ShareAccess.

Gestion des requêtes pour IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

Une fois que le client a correctement réservé une ressource MsftFunctionConfig en ouvrant un handle, il peut envoyer IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS pour demander au serveur de réaliser l'opération de muxage matériel. Lorsque le serveur reçoit IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, pour chaque broche de la liste, il doit

  • Définissez le mode pull spécifié dans le membre PinConfiguration de la structure PNP_FUNCTION_CONFIG_DESCRIPTOR dans le matériel.
  • Configurez la broche à la fonction spécifiée par le membre FunctionNumber de la structure PNP_FUNCTION_CONFIG_DESCRIPTOR.

Le serveur doit ensuite terminer la requête avec STATUS_SUCCESS.

La signification de FunctionNumber est définie par le serveur, et il est compris que le descripteur MsftFunctionConfig a été créé avec connaissance de la façon dont le serveur interprète ce champ.

N’oubliez pas que lorsque le handle est fermé, le serveur doit rétablir les broches dans la configuration dans laquelle elles se trouvaient lorsque IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS a été reçue, de sorte que le serveur peut avoir besoin d’enregistrer l’état des broches avant de les modifier.

Gestion des demandes de IRP_MJ_CLOSE

Lorsqu’un client n’a plus besoin d’une ressource de multiplexage, il ferme son descripteur. Lorsqu’un serveur reçoit une requête IRP_MJ_CLOSE, il doit rétablir les broches à l’état dans lequel elles se trouvaient lorsque IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS a été reçu. Si le client n'a jamais envoyé IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, aucune action n'est nécessaire. Le serveur doit ensuite marquer les broches comme disponibles en ce qui concerne l’arbitrage de partage, et terminer la demande avec STATUS_SUCCESS. Veillez à bien synchroniser la gestion de IRP_MJ_CLOSE avec la gestion de IRP_MJ_CREATE.

Instructions de création pour les tables ACPI

Cette section explique comment fournir des ressources de multiplexage aux pilotes clients. Notez que vous aurez besoin du compilateur Microsoft ASL build 14327 ou version ultérieure pour compiler des tables contenant des ressources MsftFunctionConfig(). Les ressources MsftFunctionConfig() sont fournies aux clients de multiplexer de broches comme ressources matérielles. Ressources MsftFunctionConfig() doivent être fournies aux pilotes nécessitant des modifications de multiplexage de broches, typiquement les pilotes SPB et de contrôleur série, mais ne doivent pas être fournies aux pilotes SPB et aux périphériques connectés au bus série, car le pilote du contrôleur gère la configuration du multiplexage. La macro ACPI MsftFunctionConfig() est définie comme suit :

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

  • Partagé/exclusif : s’il est exclusif, cette broche peut être acquise par un seul client à la fois. Si elle est partagée, plusieurs clients partagés peuvent acquérir la ressource. Définissez toujours cette option de façon exclusive, car permettre à plusieurs clients non coordonnés d’accéder à une ressource mutable peut entraîner des courses de données et donc des résultats imprévisibles.
  • PinPullConfig – l’une des
    • PullDefault : utiliser la configuration de résistance de pull par défaut définie par le SOC
    • PullUp : activer la résistance de tirage
    • PullDown : activer la résistance pull-down
    • PullNone : désactiver toutes les résistances de tirage
  • FunctionNumber : numéro de fonction à programmer dans le mux.
  • ResourceSource – chemin d'espace de noms ACPI du serveur de multiplexage de broches
  • ResourceSourceIndex : définissez cette valeur sur 0
  • ResourceConsumer/ResourceProducer : définissez-le sur ResourceConsumer
  • VendorData : données binaires facultatives dont la signification est définie par le serveur de multiplexage de broches. Cela doit généralement être laissé vide
  • Liste d’épingles : liste séparée par des virgules de numéros d’épingle auxquels la configuration s’applique. Lorsque le serveur de multiplexage de broche est un pilote GpioClx, il s’agit de numéros de broche GPIO et ont la même signification que les numéros de broche dans un descripteur GpioIo.

L’exemple suivant montre comment fournir une ressource MsftFunctionConfig() à un pilote de contrôleur I2C.

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)
    }
}

En plus des ressources de mémoire et d’interruption requises par un pilote de contrôleur, une ressource MsftFunctionConfig() est également spécifiée. Cette ressource permet au pilote du contrôleur I2C de placer les broches 2 et 3 - gérées par le nœud d’appareil \_SB.GPIO0 - en fonction 4 avec la résistance pull-up activée.

Prise en charge du multiplexage dans les pilotes du client GpioClx

GpioClx a une prise en charge intégrée du muxing de broche. Les pilotes miniport GpioClx (également appelés « pilotes clients GpioClx ») pilotent le matériel du contrôleur GPIO. À compter de la build 14327 de Windows 10, les pilotes miniport GpioClx peuvent ajouter la prise en charge du multiplexage des broches en implémentant deux nouvelles interfaces DDI :

  • CLIENT_ConnectFunctionConfigPins : appelée par GpioClx pour commander le pilote miniport afin d'appliquer la configuration de multiplexage spécifiée.
  • CLIENT_DisconnectFunctionConfigPins – appelée par GpioClx pour ordonner au miniport driver de rétablir la configuration de multiplexage.

Consultez fonctions de rappel d’événements GpioClx pour obtenir une description de ces routines.

En plus de ces deux nouvelles DDI, les DDI existants doivent être audités pour la compatibilité de multiplexage des broches :

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt. CLIENT_ConnectIoPins est appelé par GpioClx pour commander le pilote miniport afin de configurer un ensemble de broches pour l’entrée ou la sortie GPIO. GPIO s’exclue mutuellement avec MsftFunctionConfig, ce qui signifie qu’une broche ne sera jamais connectée pour GPIO et MsftFunctionConfig en même temps. Étant donné que la fonction par défaut d’une broche n'est pas forcément GPIO, il est possible qu’une broche ne soit pas multiplexée en GPIO lorsque la fonction ConnectIoPins est appelée. ConnectIoPins est nécessaire pour effectuer toutes les opérations nécessaires pour préparer la broche pour les E/S GPIO, y compris les opérations de multiplexage. CLIENT_ConnectInterrupt doit se comporter de la même façon, car les interruptions peuvent être considérées comme un cas particulier d’entrée GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt : ces routines doivent retourner des épingles à l’état dans lequel elles se trouvaient quand CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt a été appelée, sauf si l’indicateur PreserveConfiguration est spécifié. En plus de rétablir la direction des broches à leur état par défaut, le miniport doit également rétablir l'état de multiplexage de chaque broche à l'état où elle était au moment où la routine _Connect a été appelée.

Par exemple, supposons que la configuration de multiplexage par défaut d’une broche est UART et que la broche peut également être utilisée comme GPIO. Quand CLIENT_ConnectIoPins est appelée pour connecter la broche à GPIO, elle doit configurer cette broche pour l'utiliser avec GPIO. Dans le cas de CLIENT_DisconnectIoPins, elle doit reconfigurer la broche pour revenir à UART. En général, les routines De déconnexion doivent annuler les opérations effectuées par les routines Connect.

Prise en charge du multiplexage dans les pilotes de contrôleur SpbCx et SerCx

À compter de la build 14327 de Windows 10, les frameworks SpbCx et SerCx intègrent une prise en charge intégrée du multiplexage de broches, permettant ainsi aux pilotes contrôleurs SpbCx et SerCx d'être des clients de multiplexage sans nécessiter aucune modification du code de ces pilotes eux-mêmes. Par extension, tout pilote de périphérique SpbCx/SerCx qui se connecte à un pilote de contrôleur SpbCx/SerCx prenant en charge le muxage déclenchera l’activité de muxage de broche.

Le diagramme suivant montre les dépendances entre chacun de ces composants. Comme vous pouvez le voir, le multiplexage de broches introduit une dépendance des pilotes de contrôleurs SerCx et SpbCx au pilote GPIO, qui est généralement responsable du multiplexage.

dépendance de multiplexage de broche

Au moment de l’initialisation de l’appareil, les infrastructures SpbCx et SerCx analysent toutes les ressources MsftFunctionConfig() fournies en tant que ressources matérielles à l’appareil. SpbCx/SerCx acquiert et libère les ressources de multiplexage de broche à la demande.

SpbCx applique la configuration du muxing de broche dans le gestionnaire IRP_MJ_CREATE, juste avant d’appeler le rappel EvtSpbTargetConnect() du pilote client. Si la configuration du multiplexage n’a pas pu être appliquée, la fonction de rappel EvtSpbTargetConnect() du pilote du contrôleur ne sera pas appelée. Par conséquent, un pilote de contrôleur SPB peut supposer que les broches sont muxées à la fonction SPB au moment où EvtSpbTargetConnect() est appelée.

SpbCx rétablit la configuration de muxing de broche dans le gestionnaire IRP_MJ_CLOSE, juste après avoir appelé la fonction de rappel EvtSpbTargetDisconnect() du contrôleur du pilote. Le résultat est que les broches sont muxées à la fonction SPB chaque fois qu’un pilote périphérique ouvre une poignée au pilote du contrôleur SPB et sont muxés lorsque le pilote périphérique ferme sa poignée.

SerCx se comporte de la même façon. SerCx acquiert toutes les ressources MsftFunctionConfig() dans son gestionnaire IRP_MJ_CREATE juste avant d’appeler le pilote de contrôleur EvtSerCx2FileOpen() et libère toutes les ressources dans son gestionnaire IRP_MJ_CLOSE, juste après avoir appelé le rappel du pilote de contrôleur EvtSerCx2FileClose.

L’implication du multiplexage dynamique des broches pour les pilotes de contrôleur SerCx et SpbCx est qu’ils doivent être en mesure de tolérer que les broches soient réaffectées de la fonction SPB/UART à certains moments. Les pilotes de contrôleur doivent supposer que les broches ne seront pas muxées avant l'appel de EvtSpbTargetConnect() ou EvtSerCx2FileOpen(). Les broches ne sont pas configurées pour la fonction SPB/UART lors des appels de rappel suivants. La liste suivante n’est pas complète, mais représente les routines PNP les plus courantes implémentées par les pilotes de contrôleur.

  • Entrée du pilote
  • EvtDriverDeviceAdd
  • EvtDevicePrepareHardware/EvtDeviceReleaseHardware
  • EvtDeviceD0Entry/EvtDeviceD0Exit

Vérification

Lorsque vous êtes prêt à tester rhproxy, il est utile d’utiliser la procédure pas à pas suivante.

  1. Vérifiez que le pilote de contrôleur pour chaque SpbCx, GpioClxet SerCx se charge et fonctionne correctement.
  2. Vérifiez que rhproxy est présent sur le système. Certaines éditions et builds de Windows ne l’ont pas.
  3. Compiler et charger votre nœud rhproxy à l’aide de ACPITABL.dat
  4. Vérifiez que le nœud d’appareil rhproxy existe
  5. Vérifiez que rhproxy est en cours de chargement et de démarrage
  6. Vérifier que les appareils attendus sont exposés au mode utilisateur
  7. Vérifiez que vous pouvez interagir avec chaque appareil à partir de la ligne de commande
  8. Vérifier que vous pouvez interagir avec chaque appareil à partir d’une application UWP
  9. Exécuter des tests HLK

Vérifier les pilotes du contrôleur

Étant donné que rhproxy expose d’autres appareils sur le système en mode utilisateur, il fonctionne uniquement si ces appareils fonctionnent déjà. La première étape consiste à vérifier que ces appareils ( les contrôleurs I2C, SPI, GPIO que vous souhaitez exposer) fonctionnent déjà.

À l’invite de commandes, exécutez

devcon status *

Examinez la sortie et vérifiez que tous les appareils intéressés sont démarrés. Si un appareil a un code problématique, vous devez résoudre les problèmes de la raison pour laquelle cet appareil ne se charge pas. Tous les appareils doivent avoir été activés lors de la mise à jour initiale de la plateforme. La résolution des problèmes des pilotes des contrôleurs SpbCx, GpioClxou SerCx dépasse la portée de ce document.

Vérifier que rhproxy est présent sur le système

Vérifiez que le service rhproxy est présent sur le système.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Si la clé reg n’est pas présente, rhproxy n’existe pas sur votre système. Rhproxy est présent sur toutes les versions d’IoT Core et de Windows Enterprise, build 15063 et versions ultérieures.

Compiler et charger ASL avec ACPITABL.dat

Maintenant que vous avez créé un nœud ASL rhproxy, il est temps de compiler et de le charger. Vous pouvez compiler le nœud rhproxy dans un fichier AML autonome qui peut être ajouté aux tables ACPI système. Sinon, si vous avez accès aux sources ACPI de votre système, vous pouvez insérer le nœud rhproxy directement dans les tables ACPI de votre plateforme. Toutefois, lors de la mise en service initiale, il peut être plus facile d’utiliser ACPITABL.dat.

  1. Créez un fichier nommé yourboard.asl et placez le nœud d’appareil RHPX dans un DefinitionBlock :

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Téléchargez le WDK et recherchez asl.exe à C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

  3. Exécutez la commande suivante pour générer ACPITABL.dat :

    asl.exe yourboard.asl
    
  4. Copiez le fichier ACPITABL.dat obtenu dans c :\windows\system32 sur votre système sous test.

  5. Activez la signature de test sur votre système en cours de test :

    bcdedit /set testsigning on
    
  6. Redémarrez le système sous test. Le système ajoute les tables ACPI définies dans ACPITABL.dat aux tables du microprogramme système.

Vérifiez que le nœud d’appareil rhproxy existe

Exécutez la commande suivante pour énumérer le nœud de périphérique rhproxy.

devcon status *msft8000

La sortie du devcon doit indiquer que l’appareil est présent. Si le nœud de l’appareil n’est pas présent, les tables ACPI n’ont pas été correctement ajoutées au système.

Vérifier que rhproxy est en cours de chargement et de démarrage

Vérifiez l’état de rhproxy :

devcon status *msft8000

Si la sortie indique que rhproxy est démarré, rhproxy a chargé et démarré avec succès. Si vous voyez un code problématique, vous devez examiner. Voici quelques codes de problème courants :

  • Problème 51 - CM_PROB_WAITING_ON_DEPENDENCY - Le système ne démarre pas rhproxy, car l’une de ses dépendances n’a pas pu être chargée. Cela signifie que soit les ressources passées à rhproxy pointent vers des nœuds ACPI non valides, soit les appareils cibles ne démarrent pas. Tout d’abord, vérifiez que tous les appareils s’exécutent correctement (consultez « Vérifier les pilotes du contrôleur » ci-dessus). Ensuite, vérifiez votre ASL et vérifiez que tous vos chemins de ressources (par exemple, \_SB.I2C1) sont corrects et pointent vers des nœuds valides dans votre DSDT.
  • Problème 10 - CM_PROB_FAILED_START - Rhproxy n’a pas pu démarrer, probablement en raison d’un problème d’analyse des ressources. Passez en revue vos index de ressources ASL et revérifiez les index de ressources dans le DSD, puis vérifiez que les ressources GPIO sont spécifiées dans l’ordre croissant du numéro de broche.

Vérifier que les appareils attendus sont exposés au mode utilisateur

Maintenant que rhproxy est en cours d’exécution, il doit avoir créé des interfaces d’appareils accessibles en mode utilisateur. Nous allons utiliser plusieurs outils en ligne de commande pour énumérer les appareils et voir qu’ils sont présents.

Clonez le référentiel https://github.com/ms-iot/samples et générez les exemples GpioTestTool, I2cTestTool, SpiTestToolet Mincomm. Copiez les outils sur votre appareil sous test et utilisez les commandes suivantes pour énumérer les appareils.

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

Vous devriez voir vos appareils et leurs noms associés répertoriés. Si vous ne voyez pas les appareils appropriés et les noms conviviaux, vérifiez votre ASL.

Vérifier chaque appareil sur la ligne de commande

L’étape suivante consiste à utiliser les outils en ligne de commande pour ouvrir et interagir avec les appareils.

Exemple I2CTestTool :

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

Exemple SpiTestTool :

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

Exemple GpioTestTool :

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

Exemple MinComm (série). Connectez Rx à Tx avant d’exécuter :

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

Vérifier chaque appareil à partir d’une application UWP

Utilisez les exemples suivants pour valider que les appareils fonctionnent à partir d’UWP.

Exécuter les tests HLK

Téléchargez le Kit de Laboratoire Matériel (HLK). Les tests suivants sont disponibles :

Lorsque vous sélectionnez le nœud d’appareil rhproxy dans le gestionnaire HLK, les tests applicables sont automatiquement sélectionnés.

Dans le gestionnaire HLK, sélectionnez « Appareil proxy Du hub de ressources » :

Capture d’écran du Kit de laboratoire matériel Windows montrant l’onglet Sélection avec l’option d’appareil proxy Resource Hub sélectionnée.

Cliquez ensuite sur l’onglet Tests, puis sélectionnez I2C WinRT, Gpio WinRT et Spi WinRT tests.

Capture d’écran du Kit de laboratoire de matériel Windows montrant l’onglet Tests avec l’option G P I O Win R T Fonctionnel et Test de stress sélectionnée.

Cliquez sur Exécuter la sélection. Une documentation supplémentaire sur chaque test est disponible en cliquant avec le bouton droit sur le test et en cliquant sur « Description du test ».

Ressources

Appendice

Annexe A - Liste ASL Raspberry Pi

Voir aussi Raspberry Pi 2 &3 Mappages de broches

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
                }
            })
        }
    }
}

Annexe B - Liste ASL MinnowBoardMax

Voir également Mappages de broches MinnowBoard Max

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 }},
                }
            })
        }
    }
}

Annexe C - Exemple de script PowerShell pour générer des ressources GPIO

Le script suivant peut être utilisé pour générer les déclarations de ressources GPIO pour Raspberry Pi :

$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;
}