Compartilhar via


Habilitar o acesso ao modo de usuário para GPIO, I2C e SPI

No Windows 10 e posterior, as APIs são fornecidas com acesso direto do modo de usuário ao GPIO (entrada/saída de uso geral), I2C (Circuito de Inter-Integrated), SPI (Interface Periférica Serial) e UART (receptor-transmissor assíncrono universal). Placas de desenvolvimento como o Raspberry Pi 2 expõem um subconjunto dessas conexões, que permitem estender um módulo de computação base com circuitos personalizados para abordar um aplicativo específico. Esses barramentos de baixo nível geralmente são compartilhados com outras funções críticas integradas, com apenas um subconjunto de pinos GPIO e barramentos expostos em cabeçalhos. Para preservar a estabilidade do sistema, é necessário especificar quais pinos e barramentos são seguros para modificação por aplicativos em modo de usuário.

Este documento descreve como especificar essa configuração no ACPI (Advanced Configuration and Power Interface) e fornece ferramentas para validar se a configuração foi especificada corretamente.

Importante

O público-alvo deste documento é a UEFI (Unified Extensible Firmware Interface) e os desenvolvedores de ACPI. Alguma familiaridade com ACPI, criação de ASL (ACPI Source Language) e SpbCx/GpioClx é assumida.

O acesso do modo de usuário a barramentos de nível baixo no Windows é inserido nas estruturas de GpioClx e SpbCx existentes. Um novo driver chamado RhProxy, disponível no Windows IoT Core e no Windows Enterprise, expõe recursos de GpioClx e SpbCx ao modo de usuário. Para habilitar as APIs, um nó de dispositivo para rhproxy deve ser declarado em suas tabelas ACPI com cada um dos recursos GPIO e SPB que devem ser expostos ao modo de usuário. Este documento explica a criação e a verificação do ASL.

ASL por exemplo

Vamos percorrer a declaração do nó do dispositivo rhproxy no Raspberry Pi 2. Primeiro, crie a declaração do dispositivo ACPI no escopo \_SB.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID – ID de hardware. Defina-a como uma ID de hardware específica do fornecedor.
  • _CID – ID compatível. Deve ser "MSFT8000".
  • _UID – ID única. Defina para 1.

Em seguida, declaramos cada um dos recursos GPIO e SPB que devem ser expostos ao modo de usuário. A ordem na qual os recursos são declarados é importante porque os índices de recursos são usados para associar propriedades aos recursos. Se houver vários barramentos I2C ou SPI expostos, o primeiro barramento declarado será considerado o barramento "padrão" para esse tipo e será a instância retornada pelos métodos GetDefaultAsync() de Windows.Devices.I2c.I2cController e Windows.Devices.Spi.SpiController.

SPI

Raspberry Pi tem dois barramentos SPI expostos. SPI0 tem duas linhas de seleção de chip de hardware e SPI1 tem uma linha de seleção de chip de hardware. Uma declaração de recurso SPISerialBus() é necessária para cada linha de seleção de chips para cada barramento. As duas declarações de recurso SPISerialBus a seguir são para as duas linhas de seleção de chip no SPI0. O campo DeviceSelection contém um valor exclusivo que o driver interpreta como um identificador de linha de seleção de chip de hardware. O valor exato que você coloca no campo DeviceSelection depende de como o driver interpreta esse campo do descritor de conexão ACPI.

Observação

Este artigo contém referências ao termo escravo — um termo que a Microsoft não tolera e parou de usar em novos produtos e documentação. Quando o termo for removido do software, o removeremos deste artigo.

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

Como o software sabe que esses dois recursos devem ser associados ao mesmo barramento? O mapeamento entre o nome amigável do barramento e o índice de recursos é especificado no DSD:

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

Isso cria um barramento chamado "SPI0" com duas linhas de seleção de chip – índices de recursos 0 e 1. Várias outras propriedades são necessárias para declarar as capacidades do barramento SPI.

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

As propriedades MinClockInHz e MaxClockInHz especificam as velocidades mínimas e máximas do relógio compatíveis com o controlador. A API impedirá que os usuários especifiquem valores fora desse intervalo. A velocidade do relógio é passada para o driver SPB no campo _SPE do descritor de conexão (seção ACPI 6.4.3.8.2.2).

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

A propriedade SupportedDataBitLengths lista os comprimentos de bits de dados suportados pelo controlador. Vários valores podem ser especificados em uma lista separada por vírgulas. A API impedirá que os usuários especifiquem valores fora dessa lista. O comprimento do bit de dados é passado para o driver SPB no campo _LEN do descritor de conexão (seção ACPI 6.4.3.8.2.2).

Você pode pensar nessas declarações de recurso como "modelos". Alguns dos campos são corrigidos na inicialização do sistema, enquanto outros são especificados dinamicamente em runtime. Os seguintes campos do descritor SPISerialBus são corrigidos:

  • Seleção de Dispositivos
  • Polaridade de Seleção de Dispositivo
  • WireMode
  • Modo Escravo
  • Fonte de Recursos

Os campos a seguir são espaços reservados para valores especificados pelo usuário em runtime:

  • DataBitLength
  • Velocidade de Conexão
  • Polaridade do Clock
  • ClockPhase

Como o SPI1 contém apenas uma única linha de seleção de chip, um único recurso de SPISerialBus() é declarado:

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

A declaração de nome amigável que acompanha – que é necessária – é especificada no DSD e refere-se ao índice dessa declaração de recurso.

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

Isso cria um barramento chamado "SPI1" e o associa ao índice de recursos 2.

Requisitos do Controlador SPI

  • Deve usar SpbCx ou ser compatível com SpbCx
  • Deve ter sido aprovado nos testes de SPI do MITT
  • Deve dar suporte à velocidade do relógio de 4Mhz
  • Deve dar suporte ao comprimento dos dados de 8 bits
  • Deve dar suporte a todos os modos spi: 0, 1, 2, 3

I2C

Em seguida, declaramos os recursos de I2C. Raspberry Pi expõe um único barramento I2C nos pinos 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

A declaração de nome amigável que acompanha – que é necessária – é especificada no DSD:

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

Isso declara um barramento I2C com o nome amigável "I2C1" que se refere ao índice de recursos 3, que é o índice do recurso I2CSerialBus() que declaramos acima.

Os seguintes campos do descritor I2CSerialBus() são fixos:

  • Modo Escravo
  • Fonte de Recursos

Os campos a seguir são espaços reservados para valores especificados pelo usuário em runtime.

  • Endereço do Escravo
  • Velocidade de Conexão
  • Modo de Endereçamento

Requisitos do controlador I2C

  • Deve usar spbCx ou ser compatível com SpbCx
  • Deve ter passado nos testes do MITT I2C
  • Deve dar suporte ao endereçamento de 7 bits
  • Deve dar suporte à velocidade do relógio de 100kHz
  • Deve dar suporte à velocidade do relógio de 400kHz

GPIO

Em seguida, declaramos todos os pinos GPIO expostos ao modo de usuário. Oferecemos as seguintes diretrizes para decidir quais pinos expor:

  • Declare todos os pinos em cabeçalhos expostos.
  • Declare os pinos que estão conectados a funções úteis integradas, como botões e LEDs.
  • Não declare pinos reservados para funções do sistema ou que não estejam conectados a nada.

O bloco a seguir de ASL declara dois pinos – GPIO4 e GPIO5. Os outros pinos não são mostrados aqui para simplificar. O apêndice C contém um script de exemplo do PowerShell que pode ser usado para gerar os recursos de 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 }

Os seguintes requisitos devem ser observados ao declarar pinos GPIO:

  • Há suporte apenas para controladores GPIO mapeados por memória. Não há suporte para controladores GPIO interfaciados por I2C/SPI. O driver do controlador é um controlador mapeado de memória se ele definir o sinalizador MemoryMappedController na estrutura CLIENT_CONTROLLER_BASIC_INFORMATION em resposta ao retorno de chamada CLIENT_QueryControllerBasicInformation.
  • Cada pin requer um gpioIO e um recurso GpioInt. O recurso GpioInt deve seguir imediatamente o recurso GpioIO e se referir ao mesmo número de pino.
  • Os recursos de GPIO devem ser ordenados aumentando o número de pinos.
  • Cada recurso GpioIO e GpioInt deve conter exatamente um número de pino na lista de pinos.
  • O campo ShareType de ambos os descritores deve ser Compartilhado
  • O campo EdgeLevel do descritor GpioInt deve ser Edge
  • O campo ActiveLevel do descritor GpioInt deve ser ActiveBoth
  • O campo PinConfig
    • Deve ser o mesmo nos descritores GpioIO e GpioInt
    • Deve ser um de PullUp, PullDown ou PullNone. Não pode ser PullDefault.
    • A configuração de pull deve corresponder ao estado de ativação do pino. Ao configurar o pino no modo de resistência configurado a partir do estado inicial, o estado do pino não deve ser alterado. Por exemplo, se a folha de dados especificar que o pino aparece com um pull up, especifique PinConfig como PullUp.

O código de inicialização de firmware, UEFI e driver não deve alterar o estado de um pino de seu estado de ativação durante a inicialização. Somente o usuário sabe o que está anexado a um pino e, portanto, quais transições de estado são seguras. O estado de ativação de cada pino deve ser documentado para que os usuários possam projetar hardware que interfaces corretamente com um pino. Um pino não deve mudar de estado inesperadamente durante a inicialização.

Modos de unidade de disco com suporte

Se o controlador GPIO oferecer suporte a resistores internos de pull-up e pull-down, além de entrada de alta impedância e saída CMOS, você deve especificar isso com a propriedade opcional SupportedDriveModes.

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

A propriedade SupportedDriveModes indica quais modos de acionamento têm suporte do controlador GPIO. No exemplo acima, todos os modos de condução a seguir têm suporte. A propriedade é uma máscara de bits dos seguintes valores:

Valor do sinalizador Modo de Condução Descrição
0x1 Entrada de Alta Impedância O pino suporta entrada de alta impedância, que corresponde ao valor "PullNone" no ACPI.
0x2 EntradaPullUp O pino dá suporte a um resistor de pull-up interno, que corresponde ao valor "PullUp" no ACPI.
0x4 InputPullDown O pino dá suporte a um resistor de pull-down interno, que corresponde ao valor "PullDown" no ACPI.
0x8 OutputCmos O pino dá suporte à geração de altas fortes e baixos fortes (em vez de ralo aberto).

InputHighImpedance e OutputCmos são compatíveis com quase todos os controladores GPIO. Se a propriedade SupportedDriveModes não for especificada, esse será o padrão.

Se um sinal GPIO passar por um level shifter antes de chegar a um cabeçalho exposto, declare os modos de operação compatíveis com o SoC, mesmo que o drive mode não seja observável no cabeçalho externo. Por exemplo, se um pino passar por um shifter de nível bidirecional que faz com que um pino apareça como um dreno aberto com pull resistivo para cima, você nunca observará um estado de alta impedância no cabeçalho exposto mesmo se o pino estiver configurado como uma entrada de alta impedância. Você ainda deve declarar que o pin dá suporte à entrada de alta impedância.

Numeração de Pinos

O Windows dá suporte a dois esquemas de numeração de pinos:

  • Numeração sequencial de pinos – os usuários veem números como 0, 1, 2... até o número de pinos expostos. 0 é o primeiro recurso GpioIo declarado em ASL, 1 é o segundo recurso GpioIo declarado em ASL e assim por diante.
  • Numeração de Pino Nativo – Os usuários veem os números de pino especificados nos descritores GpioIo, por exemplo, 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

A propriedade UseDescriptorPinNumbers informa ao Windows a usar a numeração de pinos nativa em vez de numeração de pinos sequencial. Se a propriedade UseDescriptorPinNumbers não for especificada ou seu valor for zero, o Windows usará como padrão a numeração de pino sequencial.

Se a numeração nativa de pino for usada, você também deverá especificar a propriedade PinCount.

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

A propriedade PinCount deve corresponder ao valor retornado por meio da propriedade TotalPins na callback CLIENT_QueryControllerBasicInformation do driver GpioClx.

Escolha o esquema de numeração mais compatível com a documentação publicada existente para sua placa. Por exemplo, Raspberry Pi usa numeração de pinos nativa porque muitos diagramas de disposição de pinos existentes usam números de pino de BCM2835. MinnowBoardMax utiliza numeração sequencial de pinos porque há poucos diagramas de pinagem existentes, e essa numeração facilita a experiência do desenvolvedor, já que apenas 10 pinos são expostos de um total de mais de 200 pinos. A decisão de usar a numeração de pino sequencial ou nativa deve ter como objetivo reduzir a confusão do desenvolvedor.

Requisitos do Driver GPIO

  • Deve usar GpioClx
  • Deve estar mapeado em memória no SOC
  • Deve usar tratamento de interrupção emulado do ActiveBoth

UART (Receptor-Transmissor Assíncrono Universal)

Se o driver UART usar SerCx ou SerCx2, você poderá usar rhproxy para expor o driver ao modo de usuário. Os drivers UART que criam uma interface de dispositivo do tipo GUID_DEVINTERFACE_COMPORT não precisam usar rhproxy. O driver da caixa de entrada Serial.sys é um desses casos.

Para expor um UART de estilo SerCxao modo de usuário, declare um recurso de UARTSerialBus da seguinte maneira.

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

Somente o campo ResourceSource é corrigido enquanto todos os outros campos são espaços reservados para valores especificados em runtime pelo usuário.

A declaração de nome amigável que acompanha é:

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

Isso atribui o nome amigável "UART2" ao controlador, que é o identificador que os usuários usarão para acessar o barramento em modo de usuário.

Configuração de Pinos em Tempo de Execução

O pin muxing é a capacidade de usar o mesmo pin físico para funções diferentes. Vários periféricos diferentes no chip, como um controlador I2C, um controlador SPI e um controlador GPIO, podem ser roteados para o mesmo pino físico em um SOC. O bloco mux controla qual função está ativa no pino em um determinado momento. Tradicionalmente, o firmware é responsável por estabelecer atribuições de função na inicialização e essa atribuição permanece estática durante a sessão de inicialização. O pin muxing em tempo de execução possibilita reconfigurar as atribuições de função dos pinos durante o tempo de execução. Permitir que os usuários escolham a função de um pin em runtime acelera o desenvolvimento, permitindo que os usuários reconfigurem rapidamente os pinos de uma placa e permite que o hardware dê suporte a uma gama mais ampla de aplicativos do que uma configuração estática.

Os usuários consomem suporte de muxing para GPIO, I2C, SPI e UART sem escrever nenhum código adicional. Quando um usuário abre um GPIO ou um barramento usando OpenPin() ou FromIdAsync(), os pinos físicos subjacentes são automaticamente multiplexados para a função solicitada. Se os pinos já estiverem em uso por uma função diferente, a chamada OpenPin() ou FromIdAsync() falhará. Quando o usuário fecha o dispositivo descartando o GpioPin, I2cDevice, SpiDevice ou objeto serialDevice, os pinos são liberados, permitindo que eles sejam abertos posteriormente para uma função diferente.

O Windows contém suporte interno para pin muxing nas estruturas GpioClx, estruturas SpbCxe estruturas SerCx. Esses frameworks funcionam em conjunto para alternar automaticamente um pino para sua função correta quando um pino GPIO ou barramento é acessado. O acesso aos pinos é arbitrado para evitar conflitos entre vários clientes. Além desse suporte interno, as interfaces e os protocolos para o pin muxing são de uso geral e podem ser estendidos para dar suporte a dispositivos e cenários adicionais.

Este documento descreve primeiro as interfaces e protocolos subjacentes envolvidos no pin muxing e, em seguida, descreve como adicionar suporte para o pin muxing para drivers de controlador GpioClx, SpbCx e SerCx.

Arquitetura do Pin Muxing

Esta seção descreve as interfaces subjacentes e os protocolos envolvidos no pin muxing. O conhecimento dos protocolos subjacentes não é necessariamente necessário para dar suporte ao pin muxing com drivers GpioClx/SpbCx/SerCx. Para obter detalhes sobre como dar suporte ao pin muxing com drivers GpioCls/SpbCx/SerCx, consulte Implementando o suporte a pin muxing em drivers de cliente GpioClx e Consumindo suporte a muxing em drivers de controlador SpbCx e SerCx.

A multiplexação de pinos é feita através da cooperação de vários componentes.

  • Pinagem de servidores de multiplexação – esses são drivers que controlam o bloco de controle de multiplexação de pinos. Os servidores pin muxing recebem solicitações de pin muxing de clientes por meio de solicitações para reservar recursos de muxing (por meio de IRP_MJ_CREATE) solicitações e solicitações para alternar a função de um pin (por meio de solicitações *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS). O servidor de multiplexação de pinos geralmente é o driver GPIO, já que o bloco de multiplexação às vezes faz parte do bloco GPIO. Mesmo que o bloco de muxing seja um periférico separado, o driver GPIO é um local lógico para colocar a funcionalidade de muxing.
  • Clientes de multiplexação de pinos – estes são drivers que consomem multiplexação de pinos. Os clientes de multiplexação de pinos recebem recursos de multiplexação de pinos do firmware ACPI. Os recursos de multiplexação de pinos são um tipo de recurso de interligação e são gerenciados pelo hub de recursos. Os clientes reservam recursos de muxing de pinos ao abrir um identificador para o recurso. Para realizar uma alteração de hardware, os clientes devem confirmar a configuração enviando uma solicitação IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Os clientes liberam recursos de multiplexação de pinos ao fechar o identificador, momento em que a configuração de multiplexação retorna ao seu estado padrão.
  • Firmware ACPI – especifica configuração de multiplexagem com recursos de MsftFunctionConfig(). Os recursos MsftFunctionConfig definem quais pinos, na configuração de muxing em que são exigidos pelo cliente. Os recursos MsftFunctionConfig contêm número de função, configuração de pull e lista de números de pinos. Os recursos MsftFunctionConfig são fornecidos como recursos de hardware para clientes de muxing, sendo recebidos por drivers em seu callback PrepareHardware, de forma semelhante aos recursos de conexão GPIO e SPB. Os clientes recebem uma ID de hub de recurso que pode ser usada para abrir um acesso ao recurso.

Você deve passar a opção de linha de comando /MsftInternal para asl.exe para compilar arquivos ASL que contêm descritores MsftFunctionConfig(), já que esses descritores estão atualmente sob análise pelo comitê de trabalho do ACPI. Por exemplo: asl.exe /MsftInternal dsdt.asl

A sequência de operações envolvidas no pin muxing é mostrada abaixo.

Interação entre cliente e servidor de muxing de pinos

  1. O cliente recebe recursos MsftFunctionConfig do firmware ACPI em seu EvtDevicePrepareHardware() retorno de chamada.
  2. O cliente usa a função auxiliar do hub de recursos RESOURCE_HUB_CREATE_PATH_FROM_ID() para criar um caminho a partir da ID do recurso e, em seguida, abre um identificador para o caminho (usando ZwCreateFile(), IoGetDeviceObjectPointer()ou WdfIoTargetOpen()).
  3. O servidor extrai a ID do hub de recursos do caminho do arquivo usando funções auxiliares do hub de recursos RESOURCE_HUB_ID_FROM_FILE_NAME()e consulta o hub de recursos para obter o descritor de recursos.
  4. O servidor executa a arbitragem de compartilhamento para cada pin no descritor e conclui a solicitação de IRP_MJ_CREATE.
  5. O cliente emite uma solicitação IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS no identificador recebido.
  6. Em resposta a IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, o servidor executa a operação de muxing de hardware, tornando a função especificada ativa em cada pin.
  7. O cliente prossegue com operações que dependem da configuração solicitada de multiplexação de pinos.
  8. Quando o cliente não exige mais que os pinos sejam multiplexados, ele fecha o manipulador.
  9. Em resposta ao identificador que é fechado, o servidor reverte os pinos ao seu estado inicial.

Descrição do protocolo para clientes de pin muxing

Esta seção descreve como um cliente consome a funcionalidade de pin muxing. Isso não se aplica a drivers de controlador SerCx e SpbCx, uma vez que as estruturas implementam esse protocolo em nome dos drivers do controlador.

Analisando recursos

Um driver WDF recebe recursos MsftFunctionConfig() na rotina EvtDevicePrepareHardware(). Os recursos msftFunctionConfig podem ser identificados pelos seguintes campos:

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

Uma rotina de EvtDevicePrepareHardware() pode extrair recursos MsftFunctionConfig da seguinte maneira:

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

Reservando e confirmando recursos

Quando um cliente deseja fazer multiplexação de pinos, ele reserva e confirma o recurso MsftFunctionConfig. O exemplo a seguir mostra como um cliente pode reservar e confirmar recursos 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;
}

O driver deve armazenar o WDFIOTARGET em uma de suas áreas de contexto para que ele possa ser fechado posteriormente. Quando o driver estiver pronto para liberar a configuração de muxing, deverá fechar o identificador de recurso chamando WdfObjectDelete()ou WdfIoTargetClose() se pretende-se reutilizar o WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Quando o cliente fecha seu identificador de recurso, os pinos são restaurados ao estado inicial, e agora podem ser adquiridos por um cliente diferente.

Descrição do protocolo para servidores de multiplexação de pinos

Esta seção descreve como um servidor de pin muxing expõe sua funcionalidade aos clientes. Isso não se aplica aos miniport drivers GpioClx, pois a framework implementa esse protocolo em nome dos drivers cliente. Para obter detalhes sobre como suportar a multiplexação de pinos em drivers de cliente GpioClx, consulte Implementando o suporte a multiplexação em drivers de cliente GpioClx.

Tratamento de solicitações IRP_MJ_CREATE

Os clientes abrem um identificador para um recurso quando desejam reservar um recurso de pin muxing. Um servidor de configuração de pinos (pin muxing) recebe IRP_MJ_CREATE solicitações por meio de uma operação de reinterpretação do hub de recursos. O componente de caminho final da solicitação IRP_MJ_CREATE contém a ID do hub de recursos, que é um inteiro de 64 bits no formato hexadecimal. O servidor deve extrair o ID do hub de recursos do nome do arquivo usando RESOURCE_HUB_ID_FROM_FILE_NAME() do reshub.h e enviar IOCTL_RH_QUERY_CONNECTION_PROPERTIES ao hub de recursos para obter o descritor MsftFunctionConfig().

O servidor deve validar o descritor e extrair o modo de compartilhamento e a lista de pinos do descritor. Em seguida, ele deve executar a arbitragem de compartilhamento para os pinos e, caso seja bem-sucedido, marcar os pinos como reservados antes de concluir a solicitação.

A arbitragem de compartilhamento terá êxito geral se a arbitragem de compartilhamento for bem-sucedida para cada pino na lista de pinos. Cada pino deve ser arbitrado da seguinte maneira:

  • Se o pino ainda não estiver reservado, a arbitragem de compartilhamento terá êxito.
  • Se o pino já estiver reservado como exclusivo, a arbitragem de compartilhamento falhará.
  • Se o pino já estiver reservado como compartilhado,
    • e a solicitação de entrada é compartilhada, a arbitragem de compartilhamento é bem-sucedida.
    • e a solicitação de entrada é exclusiva, a arbitragem de compartilhamento falha.

Se a arbitragem de compartilhamento falhar, a solicitação deverá ser concluída com STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Se a arbitragem de compartilhamento tiver sucesso, a solicitação deve ser concluída com STATUS_SUCCESS.

Observe que o modo de compartilhamento da solicitação de entrada deve ser obtido do descritor MsftFunctionConfig, e não de IrpSp->Parameters.Create.ShareAccess.

Manipulando solicitações de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

Depois que o cliente tiver reservado com êxito um recurso MsftFunctionConfig abrindo um identificador, ele poderá enviar IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS para solicitar que o servidor execute a operação de muxing de hardware real. Quando o servidor recebe IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, para cada pino na lista de pinos, ele deve

  • Defina o modo de pull especificado no membro PinConfiguration da estrutura PNP_FUNCTION_CONFIG_DESCRIPTOR em hardware.
  • Multiplexe o pino na função especificada pelo membro FunctionNumber da estrutura PNP_FUNCTION_CONFIG_DESCRIPTOR.

Em seguida, o servidor deve concluir a solicitação com STATUS_SUCCESS.

O significado de FunctionNumber é definido pelo servidor e entende-se que o descritor MsftFunctionConfig foi criado com conhecimento de como o servidor interpreta esse campo.

Lembre-se de que, quando a alça for fechada, o servidor terá que restaurar a configuração dos pinos para como estavam quando IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS foi recebido, de modo que o servidor talvez precise salvar o estado dos pinos antes de modificá-los.

Tratamento de solicitações IRP_MJ_CLOSE

Quando um cliente não precisa mais de um recurso de muxing, ele fecha o identificador. Quando um servidor recebe uma solicitação IRP_MJ_CLOSE, ele deve reverter os pinos para o estado em que estavam quando IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS foi recebido. Se o cliente nunca enviou um IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, nenhuma ação será necessária. Em seguida, o servidor deve marcar os pinos como disponíveis em relação à arbitragem de compartilhamento e concluir a solicitação com STATUS_SUCCESS. Certifique-se de sincronizar corretamente o tratamento de IRP_MJ_CLOSE com o de IRP_MJ_CREATE.

Diretrizes de criação para tabelas ACPI

Esta seção descreve como fornecer recursos de muxing para drivers cliente. Observe que você precisará do compilador do Microsoft ASL build 14327 ou posterior para compilar tabelas que contêm recursos MsftFunctionConfig(). MsftFunctionConfig() recursos são fornecidos aos clientes de muxing como recursos de hardware. Os recursos MsftFunctionConfig() devem ser fornecidos a drivers que exigem alterações de multiplexação de pinos, que normalmente são drivers de controlador SPB e serial. No entanto, não devem ser fornecidos a drivers de periféricos SPB e serial, já que o driver do controlador gerencia a configuração de multiplexação. A macro ACPI MsftFunctionConfig() é definida da seguinte maneira:

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

  • Compartilhado/Exclusivo – se exclusivo, esse pin pode ser adquirido por um único cliente de cada vez. Se compartilhado, vários clientes compartilhados poderão adquirir o recurso. Sempre defina isso como exclusivo, pois permitir que vários clientes não codificados acessem um recurso mutável pode levar a corridas de dados e, portanto, resultados imprevisíveis.
  • PinPullConfig – um dos
    • PullDefault – use a configuração de pull padrão do power-on definida pelo SOC
    • PullUp – habilitar o resistor de pull-up
    • PullDown – habilitar o resistor de pull-down
    • PullNone – desabilitar todos os resistores de pull
  • FunctionNumber – o número da função a ser programada no mux.
  • ResourceSource – O caminho do namespace ACPI do servidor de multiplexação de pino
  • ResourceSourceIndex – defina-o como 0
  • ResourceConsumer/ResourceProducer – defina-o como ResourceConsumer
  • VendorData – dados binários opcionais cujo significado é definido pelo servidor de pin muxing. Isso geralmente deve ser deixado em branco
  • Lista de pinos – uma lista separada por vírgulas de números de pino aos quais a configuração se aplica. Quando o servidor de pin muxing é um driver GpioClx, estes são números de pinos GPIO e têm o mesmo significado que os números de pinos em um descritor GpioIo.

O exemplo a seguir mostra como é possível fornecer um recurso MsftFunctionConfig() a um driver de controlador 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)
    }
}

Além dos recursos de memória e interrupção normalmente exigidos por um driver de controlador, um recurso de MsftFunctionConfig() também é especificado. Esse recurso permite que o driver do controlador I2C coloque os pinos 2 e 3 – gerenciados pelo nó do dispositivo em \_SB. GPIO0 – na função 4 com o resistor de pull-up habilitado.

Suporte a muxing em drivers de cliente GpioClx

GpioClx tem suporte interno para a multiplexação de pinos. Os drivers de miniporto GpioClx (também conhecidos como "drivers cliente GpioClx") operam o hardware do controlador GPIO. A partir do Windows 10 build 14327, os drivers de miniporto GpioClx podem adicionar suporte para o pin muxing implementando dois novos DDIs:

  • CLIENT_ConnectFunctionConfigPins – chamado por GpioClx para comandar o driver de miniporto para aplicar a configuração de muxing especificada.
  • CLIENT_DisconnectFunctionConfigPins – chamado por GpioClx para instruir o miniport driver a reverter a configuração de muxing.

Consulte as funções de retorno de chamada de evento GpioClx para obter uma descrição dessas rotinas.

Além desses dois novos DDIs, os DDIs existentes devem ser auditados para compatibilidade com pin muxing.

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins é chamado pelo GpioClx para comandar o driver de miniporto a configurar um conjunto de pinos para entrada ou saída GPIO. GPIO e MsftFunctionConfig são mutuamente exclusivos, o que significa que um pino nunca será conectado para GPIO e MsftFunctionConfig ao mesmo tempo. Como a função padrão de um pino pode não ser GPIO, um pino pode não estar necessariamente configurado como GPIO quando ConnectIoPins é chamado. ConnectIoPins é necessário para realizar todas as operações necessárias para preparar o pino para entrada/saída do GPIO, incluindo operações de multiplexação. CLIENT_ConnectInterrupt deve se comportar de maneira similar, pois as interrupções podem ser consideradas como um caso especial de entrada GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt – essa rotina deve retornar pinos ao estado em que estavam quando CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt foi chamado, a menos que o sinalizador PreserveConfiguration seja especificado. Além de reverter a direção dos pinos para seu estado padrão, o miniporto também deve reverter o estado de multiplexação de cada pino para o estado em que estava quando a rotina _Connect foi chamada.

Por exemplo, suponha que a configuração de muxing padrão de um pino seja UART e que o pino também possa ser usado como GPIO. Quando CLIENT_ConnectIoPins é chamado para conectar o pino para GPIO, ele deve mux o pino para GPIO e, em CLIENT_DisconnectIoPins, ele deve mux o pino de volta para UART. Em geral, as rotinas de desconexão devem desfazer as operações feitas pelas rotinas do Connect.

Dar suporte ao muxing em drivers de controlador SpbCx e SerCx

A partir do Windows 10 build 14327, as estruturas SpbCx e SerCx contêm suporte interno para multiplexação de pinos, o que permite que drivers de controlador SpbCx e SerCx sejam clientes de multiplexação de pinos sem alterações de código nos drivers de controlador. Por extensão, qualquer driver periférico SpbCx/SerCx que se conecta a um driver de controlador SpbCx/SerCx compatível com multiplexação de pinos iniciará a ativação de multiplexação de pinos.

O diagrama a seguir mostra as dependências entre cada um desses componentes. Como você pode ver, o pin muxing introduz uma dependência dos drivers de controlador SerCx e SpbCx em relação ao driver GPIO, que geralmente é responsável pelo muxing.

Dependência de muxagem de pinos

No momento da inicialização do dispositivo, as estruturas SpbCx e SerCx analisam todos os recursos MsftFunctionConfig() fornecidos como recursos de hardware para o dispositivo. Em seguida, o SpbCx/SerCx adquire e solta os recursos de multiplexação de pinos sob demanda.

SpbCx aplica a configuração de pin muxing em seu manipulador de IRP_MJ_CREATE, pouco antes de chamar a função de callback EvtSpbTargetConnect() do driver cliente . Se a configuração de muxing não puder ser aplicada, a função de retorno EvtSpbTargetConnect() do driver do controlador não será chamada. Portanto, um driver do controlador SPB pode assumir que os pinos estão multiplexados para a função SPB no momento em que o EvtSpbTargetConnect() é chamado.

SpbCx reverte a configuração de pin muxing em seu manipulador de IRP_MJ_CLOSE, logo após invocar o EvtSpbTargetDisconnect() do driver do controlador retorno de chamada. O resultado é que os pinos são multiplexados para a função SPB sempre que um driver periférico abre um identificador para o driver do controlador SPB e são desconectados quando o driver periférico fecha o identificador.

SerCx se comporta da mesma forma. SerCx adquire todos os recursos MsftFunctionConfig() em seu manipulador de IRP_MJ_CREATE pouco antes de invocar o do driver do controlador EvtSerCx2FileOpen() retorno de chamada e libera todos os recursos em seu manipulador de IRP_MJ_CLOSE, logo após invocar o driver do controlador EvtSerCx2FileClose retorno de chamada.

A implicação da multiplexação dinâmica de pinos para os drivers dos controladores SerCx e SpbCx é que eles devem ser capazes de tolerar que os pinos sejam desviados da função SPB/UART em determinados momentos. Os drivers do controlador precisam assumir que os pinos não serão multiplexados até que EvtSpbTargetConnect() ou EvtSerCx2FileOpen() sejam chamados. Os pinos não precisam ser multiplexados para a função SPB/UART durante as seguintes chamadas de retorno. A seguir não é uma lista completa, mas representa as rotinas PNP mais comuns implementadas por drivers de controlador.

  • DriverEntry
  • EvtDriverDeviceAdd
  • EvtDevicePrepareHardware/EvtDeviceReleaseHardware (prepara/libera hardware do dispositivo)
  • EvtDeviceD0Entry/EvtDeviceD0Exit

Verificação

Quando você estiver pronto para testar o rhproxy, é útil usar o procedimento passo a passo a seguir.

  1. Verifique se o driver de controlador de cada SpbCx, GpioClxe SerCx está carregando e operando corretamente.
  2. Verifique se rhproxy está presente no sistema. Algumas edições e builds do Windows não o têm.
  3. Compile e carregue seu nó rhproxy usando ACPITABL.dat
  4. Verifique se o nó do dispositivo rhproxy existe
  5. Verifique se rhproxy está carregando e iniciando
  6. Verifique se os dispositivos esperados estão expostos ao modo de usuário
  7. Verifique se você pode interagir com cada dispositivo na linha de comando
  8. Verifique se você pode interagir com cada dispositivo de um aplicativo UWP
  9. Executar testes HLK

Verificar drivers do controlador

Como o rhproxy expõe outros dispositivos no sistema ao modo de usuário, ele só funciona se esses dispositivos já estiverem funcionando. A primeira etapa é verificar se esses dispositivos - os controladores I2C, SPI, GPIO que você deseja expor - já estão funcionando.

No prompt de comando, execute

devcon status *

Examine a saída e verifique se todos os dispositivos de interesse foram iniciados. Se um dispositivo tiver um código de problema, você precisará solucionar por que esse dispositivo não está sendo carregado. Todos os dispositivos devem ter sido habilitados durante a inicialização da plataforma. A solução de problemas de drivers de controlador SpbCx, GpioClxou SerCx está além do escopo deste documento.

Verifique se a rhproxy está presente no sistema

Verifique se o serviço rhproxy está presente no sistema.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Se a chave reg não estiver presente, a rhproxy não existirá em seu sistema. O Rhproxy está presente em todos os builds do IoT Core e do Windows Enterprise build 15063 e posteriores.

Compilar e carregar ASL com ACPITABL.dat

Agora que você criou um nó ASL rhproxy, é hora de compilá-lo e carregá-lo. Você pode compilar o nó rhproxy em um arquivo AML autônomo que pode ser acrescentado às tabelas ACPI do sistema. Como alternativa, se você tiver acesso às origens ACPI do sistema, poderá inserir o nó rhproxy diretamente nas tabelas ACPI da sua plataforma. No entanto, durante a configuração inicial, pode ser mais fácil usar ACPITABL.dat.

  1. Crie um arquivo chamado yourboard.asl e coloque o nó do dispositivo RHPX dentro de um DefinitionBlock:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Baixe o do WDK e localize em

  3. Execute o seguinte comando para gerar ACPITABL.dat:

    asl.exe yourboard.asl
    
  4. Copie o arquivo de ACPITABL.dat resultante para c:\windows\system32 em seu sistema em teste.

  5. Ative o modo de assinatura de teste no sistema em teste:

    bcdedit /set testsigning on
    
  6. Reinicialize o sistema em teste. O sistema anexará as tabelas ACPI definidas em ACPITABL.dat às tabelas de firmware do sistema.

Verifique se o nó do dispositivo rhproxy existe

Execute o seguinte comando para enumerar o nó do dispositivo rhproxy.

devcon status *msft8000

A saída do devcon deve indicar que o dispositivo está presente. Se o nó do dispositivo não estiver presente, as tabelas ACPI não foram adicionadas com sucesso ao sistema.

Verificar se a rhproxy está carregando e iniciando

Verifique o status da rhproxy:

devcon status *msft8000

Se a saída indicar que o rhproxy foi iniciado, o rhproxy foi carregado e iniciado com êxito. Caso veja um código de problema, você precisará investigar. Alguns códigos de problema comuns são:

  • Problema 51 – CM_PROB_WAITING_ON_DEPENDENCY O sistema não está iniciando a rhproxy porque uma de suas dependências falhou ao carregar. Isso significa que os recursos passados para o rhproxy apontam para nós ACPI inválidos, ou os dispositivos de destino não estão iniciando. Primeiro, verifique se todos os dispositivos estão sendo executados com êxito (consulte 'Verificar drivers de controlador' acima). Em seguida, verifique duas vezes seu ASL e certifique-se de que todos os seus caminhos de recurso (por exemplo, \_SB.I2C1) estão corretos e apontem para nós válidos em seu DSDT.
  • Problema 10 – CM_PROB_FAILED_START – O Rhproxy não foi iniciado, provavelmente devido a um problema de análise de recursos. Revise seu ASL e verifique novamente os índices de recursos no DSD, e assegure-se de que os recursos GPIO estão especificados em ordem crescente de número de pinos.

Verifique se os dispositivos esperados estão expostos ao modo de usuário

Agora que o rhproxy está em execução, ele deve ter criado interfaces de dispositivos que podem ser acessadas pelo modo de usuário. Usaremos várias ferramentas de linha de comando para enumerar dispositivos e ver que eles estão presentes.

Clone o repositório https://github.com/ms-iot/samples e crie os exemplos GpioTestTool, I2cTestTool, SpiTestToole Mincomm. Copie as ferramentas para seu dispositivo em teste e use os comandos a seguir para enumerar dispositivos.

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

Você deve ver seus dispositivos e nomes amigáveis listados. Se você não vir os dispositivos corretos e os nomes amigáveis, verifique novamente sua ASL.

Verificar cada dispositivo na linha de comando

A próxima etapa é usar as ferramentas de linha de comando para abrir e interagir com os dispositivos.

Exemplo de I2CTestTool:

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

Exemplo de SpiTestTool:

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

Exemplo de GpioTestTool:

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

Exemplo de MinComm (serial). Conecte o Rx ao Tx antes de executar:

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

Verificar cada dispositivo de um aplicativo UWP

Use os exemplos a seguir para validar que os dispositivos funcionam da UWP.

  • IoT-GPIO
  • IoT-I2C
  • IoT-SPI
  • AcessoPersonalizadoAoDispositivoSerial

Executar os testes do HLK

Baixe odo Hardware Lab Kit (HLK). Os seguintes testes estão disponíveis:

Quando você seleciona o nó do dispositivo rhproxy no gerenciador do HLK, os testes aplicáveis serão selecionados automaticamente.

No gerenciador do HLK, selecione "Dispositivo proxy do Hub de Recursos":

Captura de tela do Kit do Laboratório de Hardware do Windows mostrando a guia Seleção com a opção de dispositivo proxy do Hub de Recursos selecionada.

Em seguida, clique na guia Testes e selecione os testes I2C WinRT, Gpio WinRT e Spi WinRT.

Captura de tela do Kit do Laboratório de Hardware do Windows mostrando a guia Testes com a opção GP I O Win R T Testes Funcionais e de Estresse selecionada.

Clique em Executar o Selecionado. A documentação adicional sobre cada teste está disponível clicando com o botão direito do mouse no teste e clicando em "Descrição do Teste".

Recursos

Apêndice

Apêndice A – Listagem asl do Raspberry Pi

Consulte também mapeamentos de pinos Raspberry Pi 2 &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
                }
            })
        }
    }
}

Apêndice B – Listagem ASL MinnowBoardMax

Consulte também Mapeamentos de Pinos do 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 }},
                }
            })
        }
    }
}

Apêndice C – Exemplo de script do Powershell para gerar recursos de GPIO

O script a seguir pode ser usado para gerar as declarações de recurso gpio para 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;
}