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), al circuito integrato (I2C), all'interfaccia SPI (Serial Peripheral Interface) e al trasmettitore ricevitore 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 base con circuiti personalizzati per gestire una determinata applicazione. Questi bus di basso livello sono in genere condivisi con altre funzioni di onboarding critiche, con solo un subset di pin GPIO e bus esposti su intestazioni. 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 presuppone una certa familiarità con ACPI, LAL (ACPI Source Language) e SpbCx/GpioClx.

L'accesso in modalità utente agli autobus di basso livello in Windows viene eseguito tramite 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

Verrà ora illustrata la dichiarazione del nodo del dispositivo rhproxy in 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 quello "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 di ciascun 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 della 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.

Nota

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 usando 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:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

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

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • 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 associata del nome descrittivo, la quale è 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 si associa all'indice delle risorse 2.

Requisiti per i driver SPI

  • Deve usare SpbCx o essere compatibile con SpbCx
  • Deve aver superato i test SPI MITT
  • Deve supportare la velocità di clock a 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 associata del nome descrittivo, che è obbligatoria, viene 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 corretti:

  • SlaveMode
  • ResourceSource

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

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

Requisiti per i 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

Dopo questo, dichiariamo tutti i pin GPIO esposti alla modalità utente. Sono disponibili le indicazioni seguenti per decidere quali pin esporre:

  • Dichiarare tutti i pin nelle intestazioni esposte.
  • Dichiarare i pin connessi a funzioni utili di onboarding, ad esempio pulsanti e LED.
  • Non dichiarare pin riservati per le funzioni di sistema o non connessi ad alcun elemento.

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 .
  • Ciascun 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 corrispondente.
  • Il campo ShareType di entrambi i descrittori deve risultare Condiviso
  • Il campo EdgeLevel del descrittore GpioInt deve risultare Edge
  • Il campo ActiveLevel del descrittore GpioInt deve risultare 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.

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 unità 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à SupportedDriveModes facoltativa.

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

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

Valore flag Modalità unità Descrizione
0x1 InputHighImpedance Il pin supporta un 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 supporta un resistore pull-down predefinito, che corrisponde al valore "PullDown" in ACPI.
0x8 OutputCmos Il pin supporta la generazione di forti alti e bassi forti (al contrario 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 adattatore di allineamento prima di raggiungere un'intestazione esposta, dichiarare le modalità di unità supportate dal SOC, anche se la modalità di unità non sarebbe osservabile nell'intestazione esterna. Ad esempio, se un pin passa attraverso un adattatore di allineamento bidirezionale che rende un pin visualizzato come svuotamento aperto con pull up resistivo, non si noterà mai uno stato di impedenza elevata sull'intestazione esposta 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 pin 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 dei pin. Se la proprietà UseDescriptorPinNumbers non è specificata o il relativo valore è zero, per impostazione predefinita Windows imposterà 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 callback CLIENT_QueryControllerBasicInformation 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, poiché sono presenti pochi diagrammi pinout esistenti e la numerazione sequenziale dei pin semplifica l'esperienza di sviluppo dato che 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 per i driver GPIO

  • Deve usare GpioClx
  • Deve essere mappata la memoria on-SOC
  • Deve usare la gestione degli interrupt ActiveBoth emulati

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 una SerCxUART in stile utente alla modalità utente, dichiarare una UARTSerialBus risorsa 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 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 del pin di runtime

Il multiplexing dei pin consiste nella 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 controlla la funzione attiva sul pin in qualsiasi momento. Tradizionalmente, il firmware si occupa di definire le assegnazioni di funzione all'avvio e tali assegnazioni rimangono statiche 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. La possibilità di scegliere la funzione di un pin in fase di esecuzione accelera lo sviluppo consentendo agli utenti di riconfigurare rapidamente i pin di una scheda e permette all'hardware di supportare una gamma più ampia di applicazioni rispetto a una configurazione statica.

Gli utenti usano il supporto del multiplexing per GPIO, I2C, SPI e UART senza scrivere alcun codice aggiuntivo. Quando un utente apre un GPIO o un bus usando OpenPin() o FromIdAsync(), i pin fisici sottostanti vengono automaticamente sottoposti al multiplexing 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 l'oggetto GpioPin, I2cDevice, SpiDevice, o SerialDevice i pin vengono rilasciati, consentendone l'apertura in un secondo momento per una funzione diversa.

Windows contiene il supporto predefinito per il multiplexing dei pin nei framework GpioClx, SpbCx e SerCx . Tali 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 è arbitrario 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 multiplexing dei pin

Questa sezione descrive le interfacce e i protocolli sottostanti coinvolti nel multiplexing dei pin. La conoscenza dei protocolli sottostanti non è necessariamente richiesta 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 per il multiplexing nei driver del controller SpbCx e SerCx.

Il multiplexing dei pin viene eseguito dalla cooperazione di diversi componenti.

  • Server di multiplexing dei pin: si tratta di driver che controllano il blocco di controllo di multiplexing dei pin. I server di multiplexing pin ricevono richieste di multiplexing dei pin dai client tramite richieste per riservare le richieste di risorse multiplexing (tramite IRP_MJ_CREATE) e richieste di cambio della funzione di un pin (tramite richieste *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 multiplexing è una periferica separata, il driver GPIO è una posizione logica per mettere la funzionalità di multiplexing.
  • Client di multiplexing dei pin: si tratta di driver che utilizzano il multiplexing dei pin. I client di multiplexing pin ricevono risorse di multiplexing dei pin dal firmware ACPI. Le risorse di multiplexing dei pin sono un tipo di risorsa di connessione e vengono gestite dall'hub delle risorse. I client di multiplexing dei pin riservano risorse di multiplexing dei pin aprendo un handle alla risorsa. Per apportare una modifica hardware, i client devono eseguire il commit della configurazione inviando una richiesta IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS . I client rilasciano le risorse di multiplexing dei pin chiudendo l'handle, in cui la configurazione del multiplexing viene ripristinata allo stato predefinito.
  • Firmware ACPI: specifica la configurazione di multiplexing con le MsftFunctionConfig() risorse. Le risorse MsftFunctionConfig esprimono quali pin, e 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 multiplexing pin come risorse hardware, ricevute dai driver nel 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 /MsftInternal riga di comando a asl.exe per compilare i file ASL contenenti MsftFunctionConfig() descrittori poiché 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.

Pin muxing client server interaction

  1. Il client riceve le risorse MsftFunctionConfig dal firmware ACPI nel 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 helper dell'hub risorse RESOURCE_HUB_ID_FROM_FILE_NAME(), 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 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 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. In risposta all'handle chiuso, il server ripristina lo stato iniziale dei pin.

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 driver del controller SerCx 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 commit delle risorse

Quando un client vuole aggiungere dei pin di multiplexing, riserva ed esegue il commit della 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 dovrebbe 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, dovrebbe chiudere l'handle di risorsa richiamando WdfObjectDelete() o WdfIoTargetClose() se si intende riutilizzare WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Quando il client chiude l'handle di risorsa, i pin vengono disattivati allo stato iniziale e a quel punto possono essere acquisiti da un client diverso.

Descrizione del protocollo per i server di multiplexing pin

Questa sezione descrive come un server di multiplexing del pin espone le 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 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 multiplexing pin riceve richieste IRP_MJ_CREATE tramite un'operazione reparse dall'hub risorse. Il componente del percorso finale della richiesta IRP_MJ_CREATE contiene l'ID hub 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 descrittore MsftFunctionConfig().

Il server dovrebbe 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 nel complesso un esito positivo se l'arbitraggio di condivisione ha esito positivo per ogni pin nel corrispondente elenco. Ciascun pin deve essere arbitrato come segue:

  • Se il pin non è già riservato, l'arbitrato di condivisione ha esito positivo.
  • Se il pin è già riservato come esclusivo, l'arbitrato di condivisione ha esito negativo.
  • 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 dovrebbe essere ricavata dal descrittore MsftFunctionConfig e non da 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 dovrebbe

  • Impostare la modalità pull specificata nel membro PinConfiguration della struttura PNP_FUNCTION_CONFIG_DESCRIPTOR nell'hardware.
  • Eseguire il multiplexing del 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 resta inteso che il descrittore MsftFunctionConfig è stato creato con la 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_CLO edizione Standard

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 nello 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 di IRP_MJ_CLOSE con la gestione di 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à necessaria la build 14327 o successiva del compilatore Microsoft ASL per compilare tabelle contenenti MsftFunctionConfig() risorse. MsftFunctionConfig() le risorse vengono fornite ai client di multiplexing dei pin come risorse hardware. MsftFunctionConfig() Le risorse devono essere fornite ai driver che richiedono modifiche di multiplexing dei pin, che sono in genere SPB e driver del controller seriale, ma non devono essere fornite a SPB e driver di 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, poiché 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 pull predefinita definita da 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 multiplexer.
  • ResourceSource: percorso dello spazio nomi ACPI relativo al server di multiplexing del 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 interrupt richieste in genere da un driver controller, viene specificata anche una MsftFunctionConfig() risorsa. Questa risorsa consente al driver del controller I2C di inserire i pin 2 e 3 gestiti dal nodo del dispositivo in \_SB. GPIO0: nella funzione 4 con resistore pull-up abilitato.

Supporto del multiplexing nei driver client GpioClx

GpioClx include il supporto predefinito per il multiplexing dei pin. Driver miniport GpioClx (detti anche "driver client GpioClx"), unità 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 nuove DDI:

  • CLIENT_Connessione FunctionConfigPins: richiamato da GpioClx per eseguire il comando del driver miniport in modo da applicare la configurazione di multiplexing specificata.
  • CLIENT_DisconnectFunctionConfigPins: richiamato da GpioClx per eseguire il comando del driver miniport in modo da ripristinare la configurazione di multiplexing specificata.

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

Oltre a questi due nuovi DDI, è necessario controllare le DDI esistenti per la compatibilità del multiplexing dei pin:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins viene richiamato da GpioClx per eseguire il comando del driver miniport in modo da configurare un set di pin per l'input o l'output GPIO. GPIO si esclude a vicenda con MsftFunctionConfig, il che significa che un pin non verrà mai connesso contemporaneamente per GPIO e MsftFunctionConfig. Poiché la funzione predefinita di un pin non è necessaria per essere GPIO, un pin potrebbe non essere necessariamente impostato su GPIO quando si richiama ConnectIoPins. ConnectIoPins è necessario per eseguire tutte le operazioni necessarie in modo da 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 trovavano quando è stato richiamato 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 richiamata 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 richiamato per connettere il pin per GPIO, deve eseguire il multiplex del pin a GPIO e, in CLIENT_DisconnectIoPins, dovrebbe eseguire il multiplex del pin a UART. In generale, le routine Disconnect devono annullare le operazioni eseguite dalle routine Connessione.

Supporto del multiplexing nei driver del controller SpbCx e SerCx

A partire da Windows 10 build 14327, i framework SpbCx e SerCx contengono il supporto predefinito per il multiplexing dei pin che consente ai driver del controller SpbCx e SerCx di essere client di multiplexing dei pin senza apportare modifiche di codice ai driver del controller stessi. Per estensione, qualsiasi driver di periferica SpbCx/SerCx che si colleghi a un driver di controllore SpbCx/SerCx abilitato al multiplexing attiverà l'attività di mutliplexing 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 multiplexing.

Pin muxing dependency

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

SpbCx applica la configurazione del multiplexing dei pin nel gestore IRP_MJ_CREATE, subito prima di richiamare il callback EvtSpbTargetConnect() del driver client. Se non è stato possibile applicare la configurazione del multiplexing, il callback del driver del EvtSpbTargetConnect() controller non verrà chiamato. Pertanto, un driver del controller SPB può presupporre che i pin abbiano il multiplexing alla funzione SPB dal momento in cui viene richiamato EvtSpbTargetConnect().

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

SerCx si comporta in modo analogo. SerCx acquisisce tutte le MsftFunctionConfig() risorse nel gestore IRP_MJ_CREATE subito 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.

L'implicazione del multiplexing di pin dinamico per i driver controller SerCx e SpbCx è che devono essere in grado di tollerare la disattivazione dei pin dalla funzione SPB/UART in determinati momenti. I driver del controller devono presupporre che i pin non verranno disattivati fino a quando non viene richiamato EvtSpbTargetConnect() o EvtSerCx2FileOpen(). I pin non sono necessari tramite multiplexing alla 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 seguente procedura dettagliata.

  1. Verificare che ogni driver del controller SpbCx , GpioClx e SerCx stia caricando e funzionando correttamente
  2. Verificare che rhproxy il sistema sia accesso. Alcune edizioni e compilazioni di Windows non ne dispongono.
  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 di caricamento e 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. Esecuzione dei 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 la visualizzazione iniziale della piattaforma. La risoluzione dei problemi relativi ai driver di 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 compilazioni di IoT Core e Windows Enterprise build 15063 e versioni successive.

Compilare e caricare ASL con ACPITABL.dat

Dal momento 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 visualizzazione 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. Scaricare il WDK e trovare asl.exe all'indirizzo C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

  3. Per generare ACPITABL.dat, eseguire il comando seguente:

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

  5. Attivare il testsigning nel sistema sottoposto a 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 dovrebbe 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 corso 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 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 sopra "Verifica i driver del controller"). Controllare quindi l'ASL e assicurarsi 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. Passare all'ASL e controllare due indici di risorse nel DSD e verificare che le risorse GPIO siano specificate in ordine crescente di numeri di pin.

Verificare che i dispositivi previsti siano esposti alla modalità utente

Dal momento che rhproxy è in esecuzione, si presume che abbia 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.

Clonare il https://github.com/ms-iot/samples repository e compilare gli esempi GpioTestTool , I2cTestToolSpiTestTool, 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

Dovrebbero essere elencati i dispositivi e i nomi descrittivi. Se non vengono visualizzati i dispositivi corretti e i nomi descrittivi, verificare 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 minComm (seriale). Connessione da 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

Scaricare l'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 "Dispositivo proxy hub risorse":

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

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

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

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 Mapping dei pin Raspberry Pi 2 & e 3

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

Vedere anche Mapping dei pin max di MinnowBoard

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