Abilitare l'accesso in modalità utente a GPIO, I2C e SPI

In Windows 10 e versioni successive, le API vengono fornite con accesso diretto dalla modalità utente all'input/output per utilizzo generico (GPIO), Inter-Integrated Circuit (I2C), Serial Peripheral Interface (SPI) e UART (Universal Asynchronous Receiver-Transmitter). Le schede di sviluppo, ad esempio Raspberry Pi 2, espongono un subset di queste connessioni, che consentono di estendere un modulo di calcolo di base con circuiti personalizzati per gestire una determinata applicazione. Questi bus a basso livello sono generalmente condivisi con altre funzioni critiche di bordo, e solo un sottoinsieme dei pin e dei bus GPIO è esposto sui connettori. Per mantenere la stabilità del sistema, è necessario specificare quali pin e bus sono sicuri per la modifica da parte delle applicazioni in modalità utente.

Questo documento descrive come specificare questa configurazione in Advanced Configuration and Power Interface (ACPI) e fornisce strumenti per verificare che la configurazione sia stata specificata correttamente.

Important

I destinatari di questo documento sono gli sviluppatori UEFI (Unified Extensible Firmware Interface) e ACPI. Si presuppone una certa familiarità con ACPI, con la scrittura in ACPI Source Language (ASL) e con SpbCx/GpioClx.

L'accesso in modalità utente ai bus a basso livello in Windows è veicolato attraverso i framework esistenti GpioClx e SpbCx. Un nuovo driver denominato RhProxy, disponibile in Windows IoT Core e Windows Enterprise, espone le risorse GpioClx e SpbCx alla modalità utente. Per abilitare le API, è necessario dichiarare un nodo del dispositivo per rhproxy nelle tabelle ACPI con ognuna delle risorse GPIO e SPB che devono essere esposte alla modalità utente. Questo documento illustra la creazione e la verifica dell'ASL.

ASL per esempio

Vediamo la dichiarazione del nodo dispositivo rhproxy su Raspberry Pi 2. Creare prima di tutto la dichiarazione del dispositivo ACPI nell'ambito \_SB.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID : ID hardware. Impostare questa opzione su un ID hardware specifico del fornitore.
  • _CID: ID compatibile. Deve essere "MSFT8000".
  • _UID : ID univoco. Impostare su 1.

Successivamente dichiariamo ognuna delle risorse GPIO e SPB che devono essere esposte alla modalità utente. L'ordine in cui le risorse vengono dichiarate è importante perché gli indici delle risorse vengono usati per associare le proprietà alle risorse. Se sono esposti più bus I2C o SPI, il primo bus dichiarato viene considerato il bus "predefinito" per tale tipo e sarà l'istanza restituita dai metodi GetDefaultAsync() di Windows. Devices.I2c.I2cController e Windows. Devices.Spi.SpiController.

SPI

Raspberry Pi ha due bus SPI esposti. SPI0 ha due linee di selezione chip hardware e SPI1 ha una linea di selezione chip hardware. Per ogni linea di selezione chip per ogni bus è necessaria una dichiarazione di risorsa SPISerialBus(). Le due dichiarazioni di risorse SPISerialBus seguenti sono per le due linee di selezione chip in SPI0. Il campo DeviceSelection contiene un valore univoco che il driver interpreta come identificatore di riga di selezione chip hardware. Il valore esatto inserito nel campo DeviceSelection dipende dal modo in cui il driver interpreta questo campo del descrittore di connessione ACPI.

Note

Questo articolo contiene riferimenti alla terminologia legacy nelle specifiche delle interfacce hardware che Microsoft non usa più nei nuovi prodotti e nella nuova documentazione. Quando il termine verrà rimosso dal software, verrà rimosso anche dall'articolo.

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

In che modo il software sa che queste due risorse devono essere associate allo stesso bus? Il mapping tra il nome descrittivo del bus e l'indice delle risorse viene specificato nel DSD:

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

In questo modo viene creato un bus denominato "SPI0" con due righe di selezione chip: gli indici delle risorse 0 e 1. Sono necessarie diverse altre proprietà per dichiarare le funzionalità del bus SPI.

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

Le proprietà MinClockInHz e MaxClockInHz specificano le velocità di clock minime e massime supportate dal controller. L'API impedirà agli utenti di specificare valori esterni a questo intervallo. La velocità dell'orologio viene passata al driver SPB nel campo _SPE del descrittore di connessione (sezione ACPI 6.4.3.8.2.2).

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

La proprietà SupportedDataBitLengths elenca le lunghezze dei bit di dati supportate dal controller. È possibile specificare più valori in un elenco delimitato da virgole. L'API impedirà agli utenti di specificare valori esterni a questo elenco. La lunghezza del bit di dati viene passata al driver SPB nel campo _LEN del descrittore di connessione (sezione ACPI 6.4.3.8.2.2).

È possibile considerare queste dichiarazioni di risorse come "modelli". Alcuni campi vengono corretti all'avvio del sistema, mentre altri vengono specificati in modo dinamico in fase di esecuzione. I campi seguenti del descrittore SPISerialBus sono corretti:

  • Selezione del dispositivo
  • Polarità di selezione del dispositivo
  • WireMode
  • SlaveMode
  • ResourceSource

I campi seguenti sono segnaposto per i valori specificati dall'utente in fase di esecuzione:

  • DataBitLength
  • Velocità di connessione
  • Polarità del clock
  • ClockPhase

Poiché SPI1 contiene solo una singola riga di selezione chip, viene dichiarata una singola SPISerialBus() risorsa:

// 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,   // target 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 dichiarazione di nome descrittivo associata, obbligatoria, viene specificata nel DSD e fa riferimento all'indice di questa dichiarazione di risorsa.

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

Viene creato un bus denominato "SPI1" e lo associa all'indice delle risorse 2.

Requisiti del driver SPI

  • Deve usare SpbCx o essere compatibile con SpbCx
  • Deve aver superato i test SPI MITT
  • Deve supportare una velocità di clock di 4 MHz
  • Deve supportare la lunghezza dei dati a 8 bit
  • Deve supportare tutte le modalità SPI: 0, 1, 2, 3

I2C

Successivamente, dichiariamo le risorse I2C. Raspberry Pi espone un singolo bus I2C sui pin 3 e 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 dichiarazione di nome descrittivo associata, obbligatoria, è specificata nel DSD:

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

In questo modo viene dichiarato un bus I2C con nome descrittivo "I2C1" che fa riferimento all'indice delle risorse 3, che è l'indice della risorsa I2CSerialBus() dichiarata in precedenza.

I seguenti campi del descrittore I2CSerialBus() sono fissi:

  • SlaveMode
  • ResourceSource

I campi seguenti sono segnaposto per i valori specificati dall'utente in fase di esecuzione.

  • SlaveAddress
  • Velocità di connessione
  • Modalità di indirizzamento

Requisiti del driver I2C

  • Deve usare SpbCx o essere compatibile con SpbCx
  • Deve aver superato i test I2C MITT
  • Deve supportare l'indirizzamento a 7 bit
  • Deve supportare la velocità di clock di 100 kHz
  • Deve supportare la velocità di clock di 400 kHz

GPIO

Dichiariamo quindi tutti i pin GPIO esposti alla modalità utente. Sono disponibili le indicazioni seguenti per decidere quali pin esporre:

  • Definire tutti i pin sui connettori esposti.
  • Dichiara i pin collegati a utili funzioni presenti sulla scheda, ad esempio pulsanti e LED.
  • Non dichiarare i pin riservati alle funzioni di sistema o non connessi a nulla.

Il blocco seguente di ASL dichiara due pin: GPIO4 e GPIO5. Gli altri pin non sono visualizzati qui per brevità. L'Appendice C contiene uno script powershell di esempio che può essere usato per generare le risorse 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 }

Quando si dichiarano pin GPIO, è necessario rispettare i requisiti seguenti:

  • Sono supportati solo i controller GPIO mappati alla memoria. I controller GPIO interfacciati su I2C/SPI non sono supportati. Il driver del controller è un controller mappato alla memoria se imposta il flag MemoryMappedController nella struttura CLIENT_CONTROLLER_BASIC_INFORMATION in risposta al callback CLIENT_QueryControllerBasicInformation .
  • Ogni pin richiede sia un GpioIO che una risorsa GpioInt. La risorsa GpioInt deve seguire immediatamente la risorsa GpioIO e deve fare riferimento allo stesso numero di pin.
  • Le risorse GPIO devono essere ordinate aumentando il numero di pin.
  • Ogni risorsa GpioIO e GpioInt deve contenere esattamente un numero di pin nell'elenco dei pin.
  • Il campo ShareType di entrambi i descrittori deve essere Condiviso
  • Il campo EdgeLevel del descrittore GpioInt deve essere Edge
  • Il campo ActiveLevel del descrittore GpioInt deve essere ActiveBoth
  • Campo PinConfig
    • Deve essere lo stesso nei descrittori GpioIO e GpioInt
    • Deve essere uno tra PullUp, PullDown e PullNone. Non può essere PullDefault.
    • La configurazione pull deve corrispondere allo stato di accensione del pin. L'impostazione del pin nella modalità di pull specificata a partire dallo stato di accensione non deve modificarne lo stato. Ad esempio, se il foglio dati specifica che il pin viene visualizzato con un pull up, specificare PinConfig come PullUp.

Il codice di inizializzazione firmware, UEFI e driver non deve modificare lo stato di un pin dallo stato di accensione durante l'avvio. Solo l'utente sa cosa è collegato a un pin e quindi quali transizioni di stato sono sicure. Lo stato di accensione di ogni pin deve essere documentato in modo che gli utenti possano progettare hardware che si interfaccia correttamente con un pin. Un pin non deve modificare lo stato in modo imprevisto durante l'avvio.

Modalità di funzionamento dell'unità supportate

Se il controller GPIO supporta resistori pull-up e pull-down integrati, oltre all'ingresso ad alta impedenza e all'uscita CMOS, è necessario specificarlo con la proprietà facoltativa SupportedDriveModes.

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

La proprietà SupportedDriveModes indica quali modalità di pilotaggio sono supportate dal controller GPIO. Nell'esempio precedente sono supportate tutte le modalità di unità seguenti. La proprietà è una maschera di bit dei valori seguenti:

Valore del flag Modalità disco Descrzione
0x1 Ingresso ad alta impedenza Il pin supporta l’ingresso ad alta impedenza, che corrisponde al valore “PullNone” in ACPI.
0x2 InputPullUp Il pin supporta un resistore pull-up predefinito, che corrisponde al valore "PullUp" in ACPI.
0x4 InputPullDown Il pin dispone di una resistenza di pull-down integrata, che corrisponde al valore “PullDown” in ACPI.
0x8 OutputCmos Il pin può generare sia livelli alti forti sia livelli bassi forti (a differenza dell'open-drain).

InputHighImpedance e OutputCmos sono supportati da quasi tutti i controller GPIO. Se la proprietà SupportedDriveModes non è specificata, questa è l'impostazione predefinita.

Se un segnale GPIO passa attraverso un traslatore di livello prima di raggiungere un connettore esposto, dichiarare le modalità di pilotaggio supportate dal SoC, anche se la modalità di pilotaggio non fosse rilevabile sul connettore esterno. Ad esempio, se un pin passa attraverso un traslatore di livello bidirezionale che fa apparire il pin come a drain aperto con pull-up resistivo, non si osserverà mai uno stato di alta impedenza sul connettore accessibile, anche se il pin è configurato come ingresso ad alta impedenza. È comunque necessario dichiarare che il pin supporta input ad alta impedance.

Numerazione dei pin

Windows supporta due schemi di numerazione dei pin:

  • Numerazione puntina sequenziale: gli utenti visualizzano numeri come 0, 1, 2... fino al numero di pin esposti. 0 è la prima risorsa GpioIo dichiarata in ASL, 1 è la seconda risorsa GpioIo dichiarata in ASL e così via.
  • Numerazione pin nativa: gli utenti visualizzano i numeri di pin specificati nei descrittori GpioIo, ad esempio 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

La proprietà UseDescriptorPinNumbers indica a Windows di usare la numerazione dei pin nativa anziché la numerazione sequenziale. Se la proprietà UseDescriptorPinNumbers non è specificata o il relativo valore è zero, Windows userà per impostazione predefinita la numerazione sequenziale dei pin.

Se viene utilizzata la numerazione dei pin nativa, è necessario specificare anche la proprietà PinCount .

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

La proprietà PinCount deve corrispondere al valore restituito tramite la proprietà TotalPins nella CLIENT_QueryControllerBasicInformation callback del GpioClx driver.

Scegliere lo schema di numerazione più compatibile con la documentazione pubblicata esistente per la scheda. Ad esempio, Raspberry Pi usa la numerazione dei pin nativa perché molti diagrammi pinout esistenti usano i numeri di pin BCM2835. MinnowBoardMax usa la numerazione sequenziale dei pin perché sono presenti pochi diagrammi pinout esistenti e la numerazione sequenziale dei pin semplifica l'esperienza di sviluppo perché solo 10 pin vengono esposti su più di 200 pin. La decisione di usare la numerazione sequenziale o nativa deve mirare a ridurre la confusione dello sviluppatore.

Requisiti del driver GPIO

  • Deve usare GpioClx
  • Deve essere mappata la memoria on-SOC
  • È necessario utilizzare la gestione emulata degli interrupt ActiveBoth

UART

Se il driver UART usa SerCx o SerCx2, è possibile usare rhproxy per esporre il driver alla modalità utente. I driver UART che creano un'interfaccia di dispositivo di tipo GUID_DEVINTERFACE_COMPORT non devono usare rhproxy. Il driver posta in arrivo Serial.sys è uno di questi casi.

Per esporre in modalità utente una UART di tipo SerCx, dichiarare una risorsa UARTSerialBus come segue.

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

Solo il campo ResourceSource è fisso mentre tutti gli altri campi sono segnaposto per i valori specificati in fase di esecuzione dall'utente.

La dichiarazione di nome descrittivo associata è:

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

In questo modo viene assegnato il nome descrittivo "UART2" al controller, ovvero l'identificatore che gli utenti useranno per accedere al bus dalla modalità utente.

Multiplexing dei pin in fase di esecuzione

Il multiplexing dei pin è la possibilità di usare lo stesso pin fisico per funzioni diverse. Diverse periferiche su chip diverse, ad esempio un controller I2C, un controller SPI e un controller GPIO, potrebbero essere indirizzate allo stesso pin fisico su un SOC. Il blocco mux determina quale funzione è attiva sul pin in un dato momento. Tradizionalmente, il firmware è responsabile della definizione delle assegnazioni di funzione all'avvio e questa assegnazione rimane statica tramite la sessione di avvio. Il multiplexing dei pin di runtime aggiunge la possibilità di riconfigurare le assegnazioni di funzioni pin in fase di esecuzione. Consentire agli utenti di scegliere la funzione di un pin alla velocità di sviluppo in fase di esecuzione consentendo agli utenti di riconfigurare rapidamente i pin di una scheda e consente all'hardware di supportare un'ampia gamma di applicazioni rispetto a una configurazione statica.

Gli utenti usano il supporto del multiplexing per GPIO, I2C, SPI e UART senza scrivere codice aggiuntivo. Quando un utente apre un GPIO o un bus usando OpenPin() o FromIdAsync(), i pin fisici sottostanti vengono automaticamente configurati tramite multiplexing per la funzione richiesta. Se i pin sono già in uso da una funzione diversa, la chiamata OpenPin() o FromIdAsync() avrà esito negativo. Quando l'utente chiude il dispositivo eliminando l'oggetto GpioPin, I2cDevice, SpiDevice o SerialDevice, i pin vengono rilasciati, consentendo di aprirli in un secondo momento per una funzione diversa.

Windows include il supporto integrato per il multiplexing dei pin nei framework GpioClx, SpbCx e SerCx. Questi framework interagiscono per cambiare automaticamente un pin alla funzione corretta quando si accede a un pin GPIO o a un bus. L'accesso ai pin èbitrato per evitare conflitti tra più client. Oltre a questo supporto predefinito, le interfacce e i protocolli per il multiplexing dei pin sono per uso generico e possono essere estesi per supportare altri dispositivi e scenari.

Questo documento descrive innanzitutto le interfacce e i protocolli sottostanti coinvolti nel multiplexing dei pin e quindi descrive come aggiungere il supporto per il multiplexing dei pin ai driver del controller GpioClx, SpbCx e SerCx.

Architettura di multiplexing dei pin

Questa sezione descrive le interfacce e i protocolli di base coinvolti nel multiplexing dei pin. La conoscenza dei protocolli sottostanti non è necessariamente necessaria per supportare il multiplexing dei pin con driver GpioClx/SpbCx/SerCx. Per informazioni dettagliate su come supportare il multiplexing dei pin con i driver GpioCls/SpbCx/SerCx, vedere Implementazione del supporto per il multiplexing dei pin nei driver client GpioClx e Uso del supporto del muxing nei driver del controller SpbCx e SerCx.

Il multiplexing dei pin è realizzato grazie alla collaborazione di diversi componenti.

  • Server di multiplexing dei pin: sono driver che controllano il blocco di controllo del multiplexing dei pin. I server di multiplazione dei pin ricevono dai client richieste di multiplazione dei pin tramite richieste di riserva delle risorse di multiplazione (tramite IRP_MJ_CREATE) e richieste di modifica della funzione di un pin (tramite *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-). Il server di multiplexing dei pin è in genere il driver GPIO, poiché il blocco di multiplexing a volte fa parte del blocco GPIO. Anche se il blocco di muxing è una periferica separata, il driver GPIO è il punto naturale in cui collocare la funzionalità di muxing.
  • Client di multiplexing dei pin: si tratta di driver che utilizzano il multiplexing dei pin. I client di multiplexazione dei pin ricevono risorse di multiplexazione dei pin dal firmware ACPI. Le risorse di multiplexing dei pin costituiscono un tipo di risorsa di connessione e sono gestite dall'hub delle risorse. I client di multiplexing dei pin riservano risorse di multiplexing dei pin aprendo un handle alla risorsa. Per effettuare una modifica hardware, i client devono confermare la configurazione inviando una richiesta IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. I client rilasciano le risorse di multiplexing dei pin chiudendo l'handle; a quel punto, la configurazione di multiplexing viene riportata allo stato predefinito.
  • Firmware ACPI – specifica la configurazione di multiplexing con risorse MsftFunctionConfig(). Le risorse MsftFunctionConfig specificano quali pin, in quale configurazione di multiplexing, sono richiesti da un client. Le risorse MsftFunctionConfig contengono il numero di funzione, la configurazione pull e l'elenco di numeri di pin. Le risorse MsftFunctionConfig vengono fornite ai client di pin multiplexing come risorse hardware, che vengono ricevute dai driver nel callback PrepareHardware analogamente alle risorse di connessione GPIO e SPB. I clienti ricevono un ID dell'hub delle risorse che può essere utilizzato per ottenere un handle alla risorsa.

È necessario passare l'opzione della /MsftInternal riga di comando a asl.exe per compilare i file ASL contenenti MsftFunctionConfig() descrittori poiché questi descrittori sono attualmente sottoposti a revisione dal comitato di lavoro ACPI. Ad esempio: asl.exe /MsftInternal dsdt.asl

Di seguito è illustrata la sequenza di operazioni coinvolte nel multiplexing dei pin.

Interazione del server client di multiplexing dei pin

  1. Il client riceve le risorse MsftFunctionConfig dal firmware ACPI nel callback EvtDevicePrepareHardware().
  2. Il client usa la funzione helper dell'hub delle risorse RESOURCE_HUB_CREATE_PATH_FROM_ID() per creare un percorso dall'ID della risorsa, quindi apre un handle per il percorso (usando ZwCreateFile(), IoGetDeviceObjectPointer() o WdfIoTargetOpen()).
  3. Il server estrae l'ID dell'hub risorse dal percorso del file usando le funzioni RESOURCE_HUB_ID_FROM_FILE_NAME()helper dell'hub risorse , quindi esegue una query sull'hub risorse per ottenere il descrittore di risorse.
  4. Il server esegue l'arbitraggio della condivisione per ciascun pin del descrittore e completa la richiesta IRP_MJ_CREATE.
  5. Il client emette una richiesta IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS tramite l'handle ricevuto.
  6. In risposta a IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, il server esegue l'operazione di multiplexing hardware rendendo attiva la funzione specificata su ogni pin.
  7. Il client procede con le operazioni che dipendono dalla configurazione di multiplexing dei pin richiesta.
  8. Quando il client non richiede più il multiplexing dei pin, chiude l'handle.
  9. Alla chiusura dell'handle, il server riporta i pin al loro stato iniziale.

Descrizione del protocollo per i client di multiplexing dei pin

Questa sezione descrive come un client utilizza la funzionalità di multiplexing dei pin. Ciò non si applica ai SerCx driver del controller e SpbCx poiché i framework implementano questo protocollo per conto dei driver del controller.

Analisi delle risorse

Un driver WDF riceve le MsftFunctionConfig() risorse nella routine EvtDevicePrepareHardware(). Le risorse MsftFunctionConfig possono essere identificate dai campi seguenti:

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

Una EvtDevicePrepareHardware() routine potrebbe estrarre le risorse MsftFunctionConfig come indicato di seguito:

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

Prenotazione e allocazione delle risorse

Quando un client vuole configurare il multiplexing dei pin, riserva e conferma la risorsa MsftFunctionConfig. L'esempio seguente illustra come un client potrebbe riservare ed eseguire il commit delle risorse 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;
}

Il driver deve archiviare WDFIOTARGET in una delle aree di contesto in modo che possa essere chiuso in un secondo momento. Quando il driver è pronto per rilasciare la configurazione di multiplexing, deve chiudere l'handle di risorsa chiamando WdfObjectDelete()o WdfIoTargetClose() se si intende riutilizzare WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Quando il client chiude l'handle della risorsa, i pin vengono riconfigurati nuovamente nel loro stato iniziale e possono quindi essere acquisiti da un client diverso.

Descrizione del protocollo per il multiplexing dei pin sui server

Questa sezione descrive come un server di multiplazione dei pin espone le proprie funzionalità ai client. Ciò non si applica ai GpioClx driver miniport, poiché il framework implementa questo protocollo per conto dei driver client. Per informazioni dettagliate su come supportare il multiplexing dei pin nei GpioClx driver client, vedere Implementazione del supporto del multiplexing nei driver client GpioClx.

Gestione delle richieste di IRP_MJ_CREATE

I client aprono un handle a una risorsa quando vogliono riservare una risorsa di multiplexing dei pin. Un server di multiplazione dei pin riceve richieste IRP_MJ_CREATE tramite un'operazione di reparse dall'hub delle risorse. Il componente finale del percorso della richiesta IRP_MJ_CREATE contiene l'ID dell'hub di risorse, ossia un numero intero a 64 bit in formato esadecimale. Il server deve estrarre l'ID dell'hub risorse dal nome file usando RESOURCE_HUB_ID_FROM_FILE_NAME() da reshub.h e inviare IOCTL_RH_QUERY_CONNECTION_PROPERTIES all'hub risorse per ottenere il MsftFunctionConfig() descrittore.

Il server deve convalidare il descrittore ed estrarre la modalità di condivisione e l'elenco di pin dal descrittore. Deve quindi eseguire l'arbitraggio di condivisione per i pin e, se ha esito positivo, contrassegnare i pin come riservati prima di completare la richiesta.

L'arbitraggio di condivisione ha esito positivo complessivamente se l'arbitraggio di condivisione ha esito positivo per ogni pin nella lista dei pin. Ogni pin deve essere arbitrato come segue:

  • Se il pin non è già riservato, l'arbitrato di condivisione ha esito positivo.
  • Se il pin è già riservato in modalità esclusiva, la procedura di arbitraggio della condivisione fallisce.
  • Se il pin è già riservato come condiviso,
    • e la richiesta in ingresso è condivisa, l'arbitraggio di condivisione ha esito positivo.
    • e la richiesta in ingresso è esclusiva, l'arbitrato di condivisione ha esito negativo.

Se l'arbitrato di condivisione ha esito negativo, la richiesta deve essere completata con STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Se l'arbitrato di condivisione ha esito positivo, la richiesta deve essere completata con STATUS_SUCCESS.

Si noti che la modalità di condivisione della richiesta in ingresso deve essere ricavata dal descrittore MsftFunctionConfig, non IrpSp-Parameters.Create.ShareAccess>.

Gestione delle richieste di IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

Dopo che il client ha riservato correttamente una risorsa MsftFunctionConfig aprendo un handle, può inviare IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS per richiedere al server di eseguire l'operazione di multiplexing hardware effettiva. Quando il server riceve IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, per ogni pin nell'elenco dei pin dovrebbe

  • Imposta il modo di pull specificato nel membro PinConfiguration della struttura PNP_FUNCTION_CONFIG_DESCRIPTOR nell'hardware.
  • Mux il pin alla funzione specificata dal membro FunctionNumber della struttura PNP_FUNCTION_CONFIG_DESCRIPTOR.

Il server deve quindi completare la richiesta con STATUS_SUCCESS.

Il significato di FunctionNumber è definito dal server e si è capito che il descrittore MsftFunctionConfig è stato creato con conoscenza del modo in cui il server interpreta questo campo.

Tenere presente che quando l'handle viene chiuso, il server dovrà ripristinare i pin alla configurazione in cui si trovava quando è stata ricevuta IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, quindi il server potrebbe dover salvare lo stato dei pin prima di modificarli.

Gestione delle richieste di IRP_MJ_CLOSE

Quando un client non richiede più una risorsa di multiplexing, chiude l'handle. Quando un server riceve una richiesta IRP_MJ_CLOSE, dovrebbe riportare i pin allo stato in cui si trovavano al momento della ricezione di IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Se il client non ha mai inviato un IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, non è necessaria alcuna azione. Il server deve quindi contrassegnare i pin come disponibili per quanto riguarda l'arbitraggio della condivisione e completare la richiesta con STATUS_SUCCESS. Assicurarsi di sincronizzare correttamente la gestione delle IRP_MJ_CLOSE con la gestione IRP_MJ_CREATE .

Linee guida per la creazione di tabelle ACPI

Questa sezione descrive come fornire risorse di multiplexing ai driver client. Si noti che sarà necessario Microsoft compilatore ASL build 14327 o versione successiva per compilare tabelle contenenti risorse MsftFunctionConfig(). MsftFunctionConfig() le risorse vengono fornite ai client di pin muxing in qualità di risorse hardware. MsftFunctionConfig() Le risorse devono essere fornite ai driver che richiedono modifiche alla multiplexazione dei pin, in genere i driver SPB e dei controller seriali, ma non devono essere fornite ai driver SPB e delle periferiche seriali, poiché il driver del controller gestisce la configurazione della multiplexazione. La MsftFunctionConfig() macro ACPI è definita come segue:

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

  • Condiviso/Esclusivo: se esclusivo, questo pin può essere acquisito da un singolo client alla volta. Se condiviso, più client condivisi possono acquisire la risorsa. Impostare sempre questa opzione su esclusiva perché consentire a più client non coordinati di accedere a una risorsa modificabile può causare race di dati e quindi risultati imprevedibili.
  • PinPullConfig : uno dei
    • PullDefault – usare la configurazione di pull predefinita all’accensione definita dal SoC
    • PullUp: abilitare il resistore pull-up
    • PullDown: abilitare il resistore pull-down
    • PullNone: disabilitare tutti i resistori pull
  • FunctionNumber: numero di funzione da programmare nel mux.
  • ResourceSource – Il percorso dello spazio dei nomi ACPI del server di multiplexing dei pin
  • ResourceSourceIndex: impostare su 0
  • ResourceConsumer/ResourceProducer: impostare questo valore su ResourceConsumer
  • VendorData: dati binari facoltativi il cui significato è definito dal server di multiplexing dei pin. Questo in genere deve essere lasciato vuoto
  • Elenco pin: elenco delimitato da virgole di numeri di pin a cui si applica la configurazione. Quando il server di multiplexing dei pin è un driver GpioClx, questi sono numeri di pin GPIO e hanno lo stesso significato dei numeri di pin in un descrittore GpioIo.

L'esempio seguente illustra come fornire una risorsa MsftFunctionConfig() a un driver di controller 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)
    }
}

Oltre alle risorse di memoria e di interrupt tipicamente richieste da un driver di controller, viene specificata anche una risorsa MsftFunctionConfig(). Questa risorsa consente al driver del controller I2C di configurare i pin 2 e 3, gestiti dal nodo di dispositivo in \_SB.GPIO0, sulla funzione 4 con la resistenza di pull-up abilitata.

Supporto per il multiplexing nei driver client GpioClx

GpioClx dispone del supporto integrato per il multiplexing dei pin. I driver miniport GpioClx (detti anche "driver client GpioClx") pilotano l'hardware del controller GPIO. A partire da Windows 10 build 14327, i driver miniport GpioClx possono aggiungere supporto per il multiplexing dei pin implementando due nuove DDI:

  • CLIENT_ConnectFunctionConfigPins : chiamato da GpioClx per eseguire il comando del driver miniport per applicare la configurazione di multiplexing specificata.
  • CLIENT_DisconnectFunctionConfigPins – chiamato da GpioClx per comandare al driver miniport di ripristinare la configurazione di multiplexing.

Per una descrizione di queste routine, vedere Funzioni di callback degli eventi GpioClx .

Oltre a questi due nuovi DDI, i DDI esistenti devono essere verificati per la compatibilità con il multiplexing dei pin:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt: CLIENT_ConnectIoPins viene chiamato da GpioClx per il comando del driver miniport per configurare un set di pin per l'input o l'output GPIO. GPIO è mutuamente esclusivo rispetto a MsftFunctionConfig, il che significa che un pin non sarà mai configurato contemporaneamente per GPIO e per MsftFunctionConfig. Poiché non è necessario che la funzione predefinita di un pin sia GPIO, quando viene chiamato ConnectIoPins non è detto che il pin sia configurato come GPIO. ConnectIoPins è necessario per eseguire tutte le operazioni necessarie per preparare il pin per L'I/O GPIO, incluse le operazioni di multiplexing. CLIENT_ConnectInterrupt dovrebbe comportarsi in modo analogo, poiché gli interrupt possono essere considerati come un caso speciale di input GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt: questa routine deve restituire i pin allo stato in cui si trovava quando è stato chiamato CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt, a meno che non venga specificato il flag PreserveConfiguration. Oltre a ripristinare la direzione dei pin allo stato predefinito, il miniport dovrebbe anche ripristinare lo stato di multiplexing di ogni pin allo stato in cui si trovava quando è stata chiamata la routine _Connect.

Si supponga, ad esempio, che la configurazione di multiplexing predefinita di un pin sia UART e che il pin possa essere usato anche come GPIO. Quando viene chiamato CLIENT_ConnectIoPins per configurare il pin per GPIO, il multiplexing del pin dovrebbe essere impostato su GPIO e, in CLIENT_DisconnectIoPins, il pin dovrebbe essere reimpostato su UART. In generale, le routine Disconnect devono annullare le operazioni eseguite dalle routine Connect.

Supporto per il multiplexing nei driver di controller SpbCx e SerCx

A partire dalla build 14327 di Windows 10, i framework SpbCx e SerCx includono il supporto integrato per il multiplexing dei pin, che consente ai driver del controller SpbCx e SerCx di fungere da client di multiplexing dei pin senza apportare alcuna modifica al codice dei driver del controller stessi. Di conseguenza, qualsiasi driver di periferica SpbCx/SerCx che si collega a un driver di controller SpbCx/SerCx con multiplexing dei pin abilitato attiverà l'attività di multiplexing dei pin.

Il diagramma seguente illustra le dipendenze tra ognuno di questi componenti. Come si può notare, il multiplexing dei pin introduce una dipendenza dai driver del controller SerCx e SpbCx al driver GPIO, che in genere è responsabile del muxing.

Dipendenza di multiplexing dei pin

Al momento dell'inizializzazione del dispositivo, i SpbCx framework e SerCx analizzano tutte le MsftFunctionConfig() risorse fornite come risorse hardware nel dispositivo. SpbCx/SerCx acquisiscono quindi e rilasciano su richiesta le risorse di multiplexing dei pin.

SpbCx applica la configurazione di multiplexing dei pin nel gestore IRP_MJ_CREATE, subito prima di chiamare il callback EvtSpbTargetConnect() del driver client. Se non è stato possibile applicare la configurazione di multiplexing, il callback del driver del controller EvtSpbTargetConnect() non verrà chiamato. Pertanto, un driver del controller SPB può presupporre che, al momento della chiamata di EvtSpbTargetConnect(), i pin siano già assegnati alla funzione SPB.

SpbCx ripristina la configurazione di multiplexing dei pin nel gestore IRP_MJ_CLOSE, subito dopo aver invocato il callback EvtSpbTargetDisconnect() del driver del controller. Il risultato è che i pin vengono commutati sulla funzione SPB ogni volta che un driver di periferica apre un handle al driver del controller SPB, e vengono disassociati da tale funzione quando il driver di periferica chiude il proprio handle.

SerCx si comporta in modo analogo. SerCx acquisisce tutte le MsftFunctionConfig() risorse nel gestore IRP_MJ_CREATE appena prima di richiamare il callback EvtSerCx2FileOpen() del driver controller e rilascia tutte le risorse nel gestore IRP_MJ_CLOSE, subito dopo aver richiamato il callback evtSerCx2FileClose del driver controller.

La conseguenza del multiplexing dinamico dei pin per i driver del controller SerCx e SpbCx è che devono essere in grado di tollerare che i pin siano commutati dalla funzione SPB/UART ad altre funzioni in determinati momenti. I driver del controller devono presumere che i pin non vengano multiplexati finché non viene chiamato EvtSpbTargetConnect() o EvtSerCx2FileOpen(). Non è necessario che i pin siano multiplexati sulla funzione SPB/UART durante i callback seguenti. Di seguito non è riportato un elenco completo, ma rappresenta le routine PNP più comuni implementate dai driver del controller.

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

Verifica

Quando si è pronti per testare rhproxy, è utile usare la procedura dettagliata seguente.

  1. Verificare che ciascun driver di controller SpbCx, GpioClx e SerCx sia caricato e funzioni correttamente
  2. Verificare che rhproxy sia presente nel sistema. Alcune edizioni e compilazioni di Windows non ce l'hanno.
  3. Compilare e caricare il nodo rhproxy usando ACPITABL.dat
  4. Verificare che il nodo del rhproxy dispositivo esista
  5. Verificare che rhproxy sia in corso il caricamento e l'avvio
  6. Verificare che i dispositivi previsti siano esposti alla modalità utente
  7. Verificare che sia possibile interagire con ogni dispositivo dalla riga di comando
  8. Verificare che sia possibile interagire con ogni dispositivo da un'app UWP
  9. Eseguire test HLK

Verificare i driver del controller

Poiché rhproxy espone altri dispositivi nel sistema alla modalità utente, funziona solo se questi dispositivi funzionano già. Il primo passaggio consiste nel verificare che tali dispositivi, ovvero i controller I2C, SPI, GPIO da esporre, funzionino già.

Al prompt dei comandi, eseguire

devcon status *

Esaminare l'output e verificare che tutti i dispositivi di interesse siano avviati. Se un dispositivo presenta un codice di problema, è necessario risolvere il motivo per cui il dispositivo non viene caricato. Tutti i dispositivi avrebbero dovuto essere abilitati durante la fase iniziale di avvio della piattaforma. La risoluzione dei problemi dei driver del controller SpbCx, GpioClx o SerCx esula dallo scopo di questo documento.

Verificare che rhproxy sia presente nel sistema

Verificare che il rhproxy servizio sia presente nel sistema.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Se la chiave reg non è presente, rhproxy non esiste nel sistema. Rhproxy è presente in tutte le build di IoT Core e Windows Enterprise build 15063 e versioni successive.

Compilare e caricare ASL con ACPITABL.dat

Ora che è stato creato un nodo ASL rhproxy, è possibile compilarlo e caricarlo. È possibile compilare il nodo rhproxy in un file AML autonomo che può essere aggiunto alle tabelle ACPI di sistema. In alternativa, se si ha accesso alle origini ACPI del sistema, è possibile inserire il nodo rhproxy direttamente nelle tabelle ACPI della piattaforma. Tuttavia, durante la fase di avvio iniziale può essere più facile utilizzare ACPITABL.dat.

  1. Creare un file denominato yourboard.asl e inserire il nodo del dispositivo RHPX all'interno di un elemento DefinitionBlock:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Scarica il WDK e trova asl.exe in C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

  3. Eseguire il comando seguente per generare ACPITABL.dat:

    asl.exe yourboard.asl
    
  4. Copiare il file ACPITABL.dat risultante in c:\windows\system32 nel sistema sottoposto a test.

  5. Attiva la modalità di firma di test nel sistema in fase di test:

    bcdedit /set testsigning on
    
  6. Riavviare il sistema sottoposto a test. Il sistema aggiungerà le tabelle ACPI definite in ACPITABL.dat alle tabelle del firmware di sistema.

Verificare che il nodo del dispositivo rhproxy esista

Eseguire il comando seguente per enumerare il nodo del dispositivo rhproxy.

devcon status *msft8000

L'output di devcon deve indicare che il dispositivo è presente. Se il nodo del dispositivo non è presente, le tabelle ACPI non sono state aggiunte correttamente al sistema.

Verificare che rhproxy sia in fase di caricamento e avvio

Controllare lo stato di rhproxy:

devcon status *msft8000

Se l'output indica che rhproxy è stato avviato, rhproxy ha caricato e avviato correttamente. Se viene visualizzato un codice di problema, è necessario analizzare. Alcuni codici di problema comuni sono:

  • Problema 51 - CM_PROB_WAITING_ON_DEPENDENCY Il sistema non avvia rhproxy perché una delle relative dipendenze non è stata caricata. Ciò significa che le risorse passate a rhproxy puntano a nodi ACPI non validi o che i dispositivi di destinazione non vengono avviati. Prima di tutto, verificare che tutti i dispositivi siano in esecuzione correttamente (vedere "Verificare i driver del controller" sopra). Quindi, ricontrolla il tuo ASL e assicurati che tutti i percorsi delle risorse (ad esempio, \_SB.I2C1) siano corretti e puntino a nodi validi nel DSDT.
  • Problema 10 - CM_PROB_FAILED_START Rhproxy non è riuscito ad avviare, probabilmente a causa di un problema di analisi delle risorse. Esaminare l'ASL e ricontrollare gli indici delle risorse nel DSD, quindi verificare che le risorse GPIO siano specificate in ordine crescente di numero di pin.

Verificare che i dispositivi previsti siano esposti alla modalità utente

Ora che rhproxy è in esecuzione, dovrebbe aver creato interfacce di dispositivi a cui è possibile accedere tramite la modalità utente. Verranno usati diversi strumenti da riga di comando per enumerare i dispositivi e verificare che siano presenti.

Clonate il repository https://github.com/ms-iot/samples e compilate gli esempi GpioTestTool, I2cTestTool, SpiTestTool e Mincomm. Copiare gli strumenti nel dispositivo sottoposto a test e usare i comandi seguenti per enumerare i dispositivi.

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

Dovresti visualizzare i tuoi dispositivi e i relativi nomi intuitivi elencati. Se non vedi i dispositivi corretti e i nomi intuitivi, ricontrolla l'ASL.

Verificare ogni dispositivo nella riga di comando

Il passaggio successivo consiste nell'usare gli strumenti da riga di comando per aprire e interagire con i dispositivi.

Esempio di I2CTestTool:

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

Esempio di SpiTestTool:

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

Esempio di GpioTestTool:

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

Esempio di MinComm (seriale). Connettere Rx a Tx prima di eseguire:

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

Verificare ogni dispositivo da un'app UWP

Usare gli esempi seguenti per verificare che i dispositivi funzionino da UWP.

Eseguire i test HLK

Scarica il HLK (Hardware Lab Kit) (HLK). Sono disponibili i test seguenti:

Quando si seleziona il nodo del dispositivo rhproxy in Gestione HLK, i test applicabili verranno selezionati automaticamente.

In Gestione HLK selezionare il "dispositivo proxy di Resource Hub":

Schermata di Windows Hardware Lab Kit che mostra la scheda Selezione con l'opzione del dispositivo proxy di Resource Hub selezionata.

Fare quindi clic sulla scheda Test e selezionare I2C WinRT, Gpio WinRT e Spi WinRT test.

Schermata del Windows Hardware Lab Kit che mostra la scheda Test con l'opzione G P I O Win R T test funzionali e di stress selezionata.

Fare clic su Esegui selezionato. Per altre informazioni su ogni test, fare clic con il pulsante destro del mouse sul test e scegliere "Test Description".

Risorse

Appendice

Appendice A - Elenco ASL Raspberry Pi

Vedere anche Raspberry Pi 2 & 3 Pin Mappings

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

Appendice B - Elenco ASL MinnowBoardMax

Vedi anche Mappatura dei pin di 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,   // target 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 }},
                }
            })
        }
    }
}

Appendice C - Script di PowerShell di esempio per generare risorse GPIO

Lo script seguente può essere usato per generare le dichiarazioni di risorse GPIO per 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;
}