Condividi tramite


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 generico (GPIO), Circuito Inter-Integrated (I2C), Interfaccia seriale periferica (SPI) e ricevitore-trasmettitore asincrono universale (UART). 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 di basso livello sono in genere condivisi con altre funzioni di bordo critiche, con solo un sottoinsieme di pin GPIO e bus esposti su header. 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.

Importante

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

L'accesso in modalità utente ai bus di basso livello su Windows passa attraverso i framework esistenti di GpioClx e SpbCx. Un nuovo driver denominato RhProxy, disponibile in Windows IoT Core e Windows Enterprise, espone GpioClx e SpbCx risorse 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

Passiamo in rassegna la dichiarazione del nodo del dispositivo rhproxy sul 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. Impostato 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 quel tipo e sarà l'istanza restituita dai GetDefaultAsync() metodi 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.

Annotazioni

Questo articolo contiene riferimenti al termine slave, un termine che Microsoft non condona e ha smesso di usare nei nuovi prodotti e nella 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,   // 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

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 in bit dei dati è trasmessa 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 seguenti campi del descrittore SPISerialBus sono fissi:

  • SelezioneDispositivo
  • Polarità di Selezione del Dispositivo
  • WireMode
  • SlaveMode
  • FonteRisorse

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

  • DataBitLength
  • VelocitàConnessione
  • Polarità dell'Orologio
  • 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,   // 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 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 di SPI di MITT
  • Deve supportare la velocità di clock a 4MHz
  • 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 del nome amichevole associata – che è 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 campi seguenti del descrittore I2CSerialBus() sono fissi:

  • SlaveMode
  • FonteRisorse

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

  • SlaveAddress
  • VelocitàConnessione
  • Modalità di indirizzamento

Requisiti del driver I2C

  • Deve usare SpbCx o essere compatibile con SpbCx
  • Deve aver superato i test MITT I2C
  • 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:

  • Dichiarare tutti i pin sui connettori esposti.
  • Dichiara i pin connessi a funzioni utili integrate, ad esempio pulsanti e LED.
  • Non dichiarare pin riservati per le funzioni di sistema o che non sono collegati a nulla.

Il blocco seguente di ASL dichiara due pin: GPIO4 e GPIO5. Gli altri pin non sono visualizzati qui per semplicità. 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
  • Il campo PinConfig
    • Deve essere lo stesso nei descrittori GpioIO e GpioInt
    • Deve essere uno di PullUp, PullDown o PullNone. Non può essere PullDefault.
    • La configurazione pull deve corrispondere allo stato di accensione del pin. L'inserimento del pin nella modalità pull specificata dallo stato di accensione non deve modificare lo stato del pin. Ad esempio, se il foglio dati specifica che il pin viene visualizzato con un pull up, specificare PinConfig come PullUp.

Durante l'avvio, il codice di inizializzazione di firmware, UEFI e driver non deve modificare lo stato di un pin rispetto al suo stato di alimentazione. 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 guida supportate

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

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

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

Valore segnale Modalità guida Descrizione
0x1 InputHighImpedance Il pin supporta l'input 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 include un resistore pull-down integrato, che corrisponde al valore "PullDown" in ACPI.
0x8 OutputCmos Il pin supporta la generazione di alti e bassi forti (anziché ad uscita aperta).

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 convertitore di livello prima di raggiungere un'intestazione esposta, dichiarare i modi di pilotaggio supportati dal SOC, anche se il modo di pilotaggio non sarebbe osservabile nell'intestazione esterna. Ad esempio, se un pin passa attraverso un convertitore di livello bidirezionale che fa apparire il pin come drain aperto con pull up resistivo, non si noterà mai uno stato ad alta impedenza sul connettore esposto, anche se il pin è configurato come input ad alta impedenza. È comunque necessario dichiarare che il pin supporta input ad alta impedenza.

Numerazione Pin

Windows supporta due schemi di numerazione dei pin:

  • Numerazione dei pin sequenziale: gli utenti vedono 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 dei pin. Se la proprietà UseDescriptorPinNumbers non è specificata o il relativo valore è zero, Windows imposterà la numerazione sequenziale dei pin come impostazione predefinita.

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 nel callback CLIENT_QueryControllerBasicInformation del driver GpioClx.

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 dei pin, sequenziale o nativa, deve mirare a ridurre la confusione tra gli sviluppatori.

Requisiti del driver GPIO

  • Deve usare GpioClx
  • Deve essere mappata la memoria on-SOC
  • Deve usare 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. La posta in arrivo Serial.sys driver è uno di questi casi.

Per esporre una UART in stile SerCx alla modalità utente, dichiarare una risorsa UARTSerialBus come indicato di seguito.

// 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 del nome amichevole associato è:

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

Ciò assegna il nome descrittivo "UART2" al controller, ovvero l'identificatore che gli utenti useranno per accedere al bus in modalità utente.

Multiplexazione dei Pin in Tempo di Esecuzione

Il multiplexing dei pin si riferisce alla capacità 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 controlla 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 muxing dei pin a tempo di esecuzione aggiunge la possibilità di riconfigurare le assegnazioni di funzioni dei pin durante l'esecuzione. Consentire agli utenti di scegliere la funzione di un pin durante l'esecuzione permette la rapida riconfigurazione dei pin di una scheda e consente all'hardware di supportare una gamma più ampia di applicazioni rispetto a quanto farebbe 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 multiplexati alla 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 il GpioPin, I2cDevice, SpiDeviceo oggetto SerialDevice, i pin vengono rilasciati, consentendo l'apertura successiva per una funzione diversa.

Windows contiene il supporto predefinito per il multiplexing dei pin nei framework di GpioClx, SpbCxe 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 del multiplexaggio dei pin

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

Il mux dei pin viene realizzato grazie alla collaborazione di più componenti.

  • Server di pin muxing: si tratta di driver che controllano il blocco di controllo del muxing dei pin. I server di multiplexing pin ricevono dai client richieste di riservare risorse di multiplexing (tramite IRP_MJ_CREATE) e di cambiare la funzione di un pin (tramite *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS). Il server di multiplexaggio dei pin è in genere il driver GPIO, perché il blocco di multiplexing a volte fa parte del blocco GPIO. Anche se il blocco di multiplexing è una periferica separata, il driver GPIO è un luogo logico per integrare la funzionalità di multiplexing.
  • Client di multiplexing dei pin: si tratta di driver che utilizzano il multiplexing dei pin. I client di pin muxing ricevono le risorse di pin muxing 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 multiplexaggio dei pin riservano risorse aprendo un identificatore alla risorsa. Per apportare una modifica hardware, i client devono eseguire il commit della configurazione inviando una richiesta di IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. I clienti rilasciano le risorse di muxing dei pin chiudendo l'handle, a quel punto la configurazione viene ripristinata allo stato predefinito.
  • Firmware ACPI: specifica la configurazione di muxing delle risorse MsftFunctionConfig(). Le risorse MsftFunctionConfig indicano quali pin, nella configurazione di multiplexing richiesta, sono necessari per 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 multiplexaggio dei pin come risorse tecniche, ricevute dai driver nel loro callback PrepareHardware in modo analogo alle risorse di connessione GPIO e SPB. I client ricevono un ID hub risorse che può essere usato per aprire un handle per la risorsa.

È necessario passare l'opzione della riga di comando /MsftInternal 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 client-server per il multiplexing dei pin

  1. Il client riceve risorse MsftFunctionConfig dal firmware ACPI nel suo callback EvtDevicePrepareHardware().
  2. Il client usa la funzione helper dell'hub risorse RESOURCE_HUB_CREATE_PATH_FROM_ID() per creare un percorso dall'ID risorsa, quindi apre un handle al 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'arbitrato di condivisione per ogni pin nel descrittore e completa la richiesta di IRP_MJ_CREATE.
  5. Il client invia una richiesta di IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS sull'handle ricevuto.
  6. In risposta a IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, il server esegue l'operazione di muxing hardware attivando la funzione specificata su ogni pin.
  7. Il cliente procede con le operazioni che dipendono dalla configurazione di pin muxing richiesta.
  8. Quando il client non richiede più il multiplexing dei pin, chiude l'handle.
  9. In risposta alla chiusura dell'handle, il server riporta i pin al loro stato iniziale.

Descrizione del protocollo per i client di pin muxing

Questa sezione descrive come un client utilizza la funzionalità di pin muxing. 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 risorse MsftFunctionConfig() durante la 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 multiplexare i pin, riserva e conferma la risorsa MsftFunctionConfig. L'esempio seguente illustra come un client potrebbe riservare e impegnare le 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 muxing, deve chiudere l'handle della risorsa chiamando WdfObjectDelete(), o WdfIoTargetClose() se si intende riutilizzare il WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Quando il client chiude l'handle di risorsa, i pin vengono riportati allo stato iniziale tramite multiplexer e possono ora essere acquisiti da un client diverso.

Descrizione del protocollo per i server di multiplexaggio dei pin

Questa sezione descrive come un server di pin muxing espone la propria 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 driver client GpioClx, 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 multiplexaggio pin riceve richieste IRP_MJ_CREATE tramite un'operazione di reparse dall'hub delle risorse. Il componente del percorso finale della richiesta di IRP_MJ_CREATE contiene l'ID dell'hub delle risorse, ovvero 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'arbitrato di condivisione ha esito positivo nel complesso se l'arbitraggio di condivisione ha esito positivo per ogni pin nell'elenco 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 modo esclusivo, l'arbitrato per la condivisione fallisce.
  • Se il pin è già riservato come condiviso,
    • e la richiesta in ingresso è condivisa, l'arbitraggio di condivisione ha esito positivo.
    • Quando la richiesta in ingresso è esclusiva, l'arbitrato di condivisione fallisce.

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 il muxing hardware reale. Quando il server riceve IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, per ogni pin nell'elenco dei pin deve

  • Impostare la modalità pull specificata nel membro PinConfiguration della struttura PNP_FUNCTION_CONFIG_DESCRIPTOR a livello hardware.
  • Imposta il pin alla funzione specificata dal membro FunctionNumber all'interno 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 di IRP_MJ_CLOSE, dovrebbe ripristinare i pin allo stato in cui si trovavano quando è stato ricevuto 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'arbitrato di condivisione e completare la richiesta con STATUS_SUCCESS. Assicurarsi di sincronizzare correttamente la gestione delle IRP_MJ_CLOSE con quella delle IRP_MJ_CREATE.

Linee guida per la creazione di tabelle ACPI

Questa sezione descrive come disponibilizzare risorse di muxing ai driver client. Si noti che sarà necessaria la build 14327 o successiva del compilatore Microsoft ASL per compilare tabelle contenenti risorse MsftFunctionConfig(). MsftFunctionConfig() risorse vengono fornite ai client di multiplexaggio dei pin come risorse hardware. MsftFunctionConfig() risorse devono essere fornite ai driver che richiedono modifiche di multiplexing dei pin, che sono in genere driver SPB e driver del controller seriale, ma non devono essere fornite ai driver SPB e ai driver delle periferiche seriali, poiché il driver del controller gestisce la configurazione di multiplexing. 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 resistenza 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: percorso del namespace ACPI del server di multiplexazione dei pin
  • ResourceSourceIndex: impostare questo su 0
  • ResourceConsumer/ResourceProducer: impostare questo valore su ResourceConsumer
  • VendorData – dati binari facoltativi il cui significato è definito dal server di multiplexaggio 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 muxing 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 interrupt richieste in genere da un driver controller, viene specificata anche una risorsa MsftFunctionConfig(). Questa risorsa consente al driver del controller I2C di impostare i pin 2 e 3 gestiti dal nodo del dispositivo in \_SB.GPIO0 nella modalità 4 con resistore pull-up abilitato.

Abilitazione del supporto al multiplexing nei driver client di GpioClx

GpioClx ha il supporto integrato per il pin muxing. I driver miniport GpioClx (noti anche come "driver client GpioClx") controllano l'hardware del controller GPIO. A partire da Windows 10 build 14327, i driver miniport GpioClx possono aggiungere il supporto per il multiplexing dei pin implementando due nuovi DDI:

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

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

Oltre a questi due nuovi DDI, è necessario verificare le DDI esistenti per la compatibilità con il multiplexing dei pin.

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt: CLIENT_ConnectIoPins viene chiamato da GpioClx per comandare il driver miniport per configurare un set di pin come input o output GPIO. GPIO si escludono a vicenda con MsftFunctionConfig, ovvero un pin non verrà mai connesso contemporaneamente per GPIO e MsftFunctionConfig. Poiché la funzione predefinita di un pin non deve essere GPIO, un pin potrebbe non essere necessariamente associato a GPIO quando viene chiamato ConnectIoPins. ConnectIoPins è necessario per eseguire tutte le operazioni necessarie per rendere il pin pronto per le operazioni di 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 CLIENT_ConnectIoPins viene chiamato per connettere il pin a **GPIO**, deve multiplexare il pin a **GPIO** e, in CLIENT_DisconnectIoPins, dovrebbe multiplexare il pin a **UART**. In generale, le routine Disconnect devono annullare le operazioni eseguite dalle routine Connect.

Supportare il multiplexing nei driver del controller SpbCx e SerCx

A partire da Windows 10 build 14327, i framework di SpbCx e SerCx contengono il supporto predefinito per il multiplexing dei pin che consente ai driver controller di SpbCx e SerCx di essere client per il multiplexing dei pin senza modifiche al codice nei driver controller stessi. Per estensione, qualsiasi driver di periferica SpbCx/SerCx che si connette a un driver di controller SpbCx/SerCx abilitato per il multiplexing dei pin attiverà l'attività di multiplexing dei pin.

Il diagramma seguente illustra le dipendenze tra ognuno di questi componenti. Come si può notare, il pin muxing introduce una dipendenza dai driver dei 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 e rilasciano le risorse di multiplexing dei pin su richiesta.

SpbCx applica la configurazione del pin muxing nel suo gestore IRP_MJ_CREATE, poco prima di chiamare il callback EvtSpbTargetConnect() del driver. Se non è stato possibile applicare la configurazione di muxing, il callback EvtSpbTargetConnect() del driver del controller non verrà chiamato. Pertanto, un driver del controller SPB può presupporre che i pin siano multiplexati alla funzione SPB dal momento in cui viene chiamato EvtSpbTargetConnect().

SpbCx ripristina la configurazione del multiplexing dei pin nel gestore IRP_MJ_CLOSE subito dopo aver richiamato la funzione di callback del driver del controller EvtSpbTargetDisconnect(). Il risultato è che i pin vengono multiplexati alla funzione SPB ogni volta che un driver periferico apre un handle al driver del controller SPB e i pin vengono disattivati quando il driver periferico chiude l'handle.

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

L'implicazione del multiplexing di pin dinamico per i driver di SerCx e SpbCx controller è che devono essere in grado di tollerare che i pin vengano disattivati dalla funzione SPB/UART in determinati momenti. I driver del controller devono presupporre che i pin non vengano commutati finché non viene chiamato EvtSpbTargetConnect() o EvtSerCx2FileOpen(). I pin non vengono multiplexati alla funzione SPB/UART durante i seguenti callback. 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 del controller SpbCx, GpioClxe SerCx sia caricato e funzioni correttamente.
  2. Verificare che rhproxy sia presente nel sistema. Alcune edizioni e build di Windows non lo hanno.
  3. Compilare e caricare il nodo rhproxy usando ACPITABL.dat
  4. Verificare che il nodo del rhproxy dispositivo esista
  5. Verificare che rhproxy stia caricando e avviando
  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 dovrebbero essere stati abilitati durante l'inizializzazione iniziale della piattaforma. La risoluzione dei problemi dei driver del controller SpbCx, GpioClx, o SerCx non rientra nell'ambito 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 configurazione iniziale può essere più facile usare 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 all'indirizzo 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. Attivare il testsigning sul 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, esso è stato 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 l'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. Rivedi il tuo ASL e ricontrolla gli indici delle risorse nel DSD, e verifica che le risorse GPIO siano specificate in ordine di numero di pin crescente.

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.

Clona il repository https://github.com/ms-iot/samples e builda i campioni GpioTestTool, I2cTestTool, SpiTestToole 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

Dovrebbero essere elencati i dispositivi e i nomi amichevoli. Se non vedi i dispositivi giusti e i nomi amichevoli, controlla di nuovo 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 (seriale) di MinComm. Connettere Rx a Tx prima di eseguire:

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

Controllare ogni dispositivo da un'app UWP

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

Eseguire i test HLK

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

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

Nel gestore HLK, selezionare "Dispositivo proxy del hub risorse":

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

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

Screenshot di Windows Hardware Lab Kit che mostra la Scheda Test con l'opzione Test Funzionali e di Stress G P I O Win R T 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,   // 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
                }
            })
        }
    }
}

Appendice B - Elenco ASL MinnowBoardMax

Vedi anche Mappature 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,   // 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 }},
                }
            })
        }
    }
}

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