Habilitar o acesso de modo do 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 para entrada/saída de uso geral (GPIO), Circuito Integrado (I2C), Interface Periférica Serial (SPI) e receptor-transmissor assíncrono universal (UART). Placas de desenvolvimento como Raspberry Pi 2 expõem um subconjunto dessas conexões, que permitem estender um módulo de computação básico com circuitos personalizados para atender a 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 e barramentos do GPIO expostos nos cabeçalhos. Para preservar a estabilidade do sistema, é necessário especificar quais pinos e barramentos são seguros para modificação por aplicativos do modo de usuário.

Este documento descreve como especificar esta configuração na Interface de Energia e Configuração Avançada (ACPI) e fornece ferramentas para validar se a configuração foi especificada corretamente.

Importante

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

O acesso do modo de usuário a barramentos de baixo nível 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 o rhproxy deve ser declarado em suas tabelas de ACPI com cada um dos recursos GPIO e SPB que devem ser expostos ao modo de usuário. Esse documento explica a criação e a verificação da 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 isso como uma ID de hardware específica do fornecedor.
  • _CID – ID compatível. Deve ser "MSFT8000".
  • _UID – ID exclusiva. Definido como 1.

Em seguida, declaramos cada um dos recursos GPIO e SPB que devem ser expostos ao modo de usuário. A ordem em que os recursos são declarados é importante porque os índices de recursos são usados para associar propriedades a 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

O Raspberry Pi tem dois barramentos SPI expostos. O SPI0 tem duas linhas de seleção de chip de hardware e o SPI1 tem uma linha de seleção de chip de hardware. Uma declaração de recurso do SPISerialBus() é necessária para cada linha de seleção de chip de cada barramento. As duas declarações de recurso do 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 colocado no campo DeviceSelection depende de como o driver interpreta esse campo do descritor de conexão de ACPI.

Observação

Este artigo contém referências ao termo slave (escravo) – um termo que a Microsoft não tolera e que parou de usar em novos produtos e documentação. Quando o termo for removido do software, também 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 estar 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 os recursos do barramento SPI.

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

As propriedades do MinClockInHz e do MaxClockInHz especificam as velocidades mínima e máxima 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 de ACPI 6.4.3.8.2.2).

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

A propriedade SupportedDataBitLengths lista os comprimentos de bits de dados compatíveis com o 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 da 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 no runtime. Os seguintes campos do descritor do SPISerialBus são corrigidos:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

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

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • ClockPhase

Como o SPI1 contém apenas uma única linha de seleção de chip, um único recurso do 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 a acompanha – que é obrigatória – é especificada no DSD e refere-se ao índice dessa declaração de recursos.

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

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

Requisitos do Driver SPI

  • Deve usar o SpbCx ou ser compatível com o SpbCx
  • Deve ter passado nos Testes 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 do SPI: 0, 1, 2, 3

I2C

Em seguida, declaramos os recursos de I2C. O 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 a acompanha – que é obrigatória – é especificada no DSD:

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

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

Os campos a seguir do descritor I2CSerialBus() são corrigidos:

  • SlaveMode
  • ResourceSource

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

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

Requisitos do Driver I2C

  • Deve usar o SpbCx ou ser compatível com SpbCx
  • Deve ter passado nos Testes I2C do MITT
  • 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 de integração úteis, 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 do ASL declara dois pinos – GPIO4 e GPIO5. Os outros pinos não são mostrados aqui por questões de brevidade. O Apêndice C contém um exemplo de script do PowerShell que pode ser usado para gerar os recursos 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 adaptados por I2C/SPI. O driver do controlador será um controlador mapeado na memória se definir o sinalizador do MemoryMappedController na estrutura CLIENT_CONTROLLER_BASIC_INFORMATION em resposta ao retorno de chamada do CLIENT_QueryControllerBasicInformation.
  • Cada pino requer um GpioIO e um recurso GpioInt. O recurso GpioInt deve seguir imediatamente o recurso GpioIO e deve referir-se ao mesmo número de pino.
  • Os recursos 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. Colocar o pino no modo de pull especificado do estado de ativação não deve alterar o estado do pino. Por exemplo, se a folha de dados especificar que o pino vem com um pull up, especifique PinConfig como PullUp.

O firmware, o UEFI e o código de inicialização do driver não devem 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 se conecta corretamente com um pino. Um pino não deve alterar o estado inesperadamente durante a inicialização.

Modos de Unidade com Suporte

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

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

A propriedade SupportedDriveModes indica quais modos de unidade têm suporte do controlador GPIO. No exemplo acima, todos os modos de unidade a seguir têm suporte. A propriedade é um bitmask dos seguintes valores:

Valor da Bandeira Modo de Unidade Descrição
0x1 InputHighImpedance O pino dá suporte à entrada de alta impedância, que corresponde ao valor "PullNone" na ACPI.
0x2 InputPullUp O pino dá suporte a um resistor de pull-up interno, que corresponde ao valor "PullUp" na ACPI.
0x4 InputPullDown O pino dá suporte a um resistor de pull-down interno, que corresponde ao valor "PullDown" na ACPI.
0x8 OutputCmos O pino dá suporte à geração de altas fortes e baixos fortes (em vez de dreno aberto).

O InputHighImpedance e o 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 shifter de nível antes de atingir um cabeçalho exposto, declare os modos de unidade compatíveis com o SOC, mesmo que o modo de unidade não seja observável no cabeçalho externo. Por exemplo, se um pino passar por um deslocador de nível bidirecional que faz com que um pino pareça um dreno aberto com pull up resistivo, você nunca observará um estado de alta de 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 pino 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 no ASL, o número 1 é o segundo recurso GpioIo declarado no ASL, e assim por diante.
  • Numeração de pinos nativa – os usuários veem os números de pinos especificados nos descritores do GpioIo, por exemplo, 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

A propriedade UseDescriptorPinNumbers informa ao Windows para usar 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 sequencial de pinos.

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

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

A propriedade PinCount deve corresponder ao valor retornado pela propriedade TotalPins no retorno de chamada CLIENT_QueryControllerBasicInformation do driver GpioClx.

Escolha o esquema de numeração mais compatível com a documentação publicada existente para o seu quadro. Por exemplo, o Raspberry Pi usa numeração de pinos nativa porque muitos diagramas de saída de pinos existentes usam os números de pinos BCM2835. O MinnowBoardMax usa numeração de pino sequencial porque há poucos diagramas de saída de pinos existentes e a numeração sequencial de pinos simplifica a experiência do desenvolvedor porque apenas 10 pinos são expostos 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 na memória SOC
  • Deve usar o tratamento de interrupção do ActiveBoth emulado

UART

Se o driver UART usar SerCx ou SerCx2, use o 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 o rhproxy. O driver Serial.sys da caixa de entrada é um desses casos.

Para expor um UART de estilo SerCx ao modo de usuário, declare um recurso 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 no 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 controlador que os usuários usarão para acessar o barramento no modo de usuário.

Multiplexação de Pino do Runtime

A multiplexação de pino é a capacidade de usar o mesmo pino 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 multiplexado controla qual função está ativa no pino a qualquer momento. Tradicionalmente, o firmware é responsável por estabelecer atribuições de funções na inicialização, e essa atribuição permanece estática durante a sessão de inicialização. O uso de multiplexação de pino de runtime adiciona a capacidade de reconfigurar atribuições de função de pino no runtime. Permitir que os usuários escolham a função de um pino no runtime acelera o desenvolvimento, permitindo que os usuários reconfigurem rapidamente os pinos de uma placa e permite que o hardware dê suporte uma gama mais ampla de aplicativos do que uma configuração estática.

Os usuários consomem suporte de multiplexação para GPIO, I2C, SPI e UART sem escrever nenhum código adicional. Quando um usuário abre um GPIO ou barramento usando OpenPin() ou FromIdAsync(), os pinos físicos subjacentes são automaticamente multiplexado 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 objeto GpioPin, I2cDevice, SpiDevice ou SerialDevice, os pinos são liberados, permitindo que posteriormente sejam abertos para uma função diferente.

O Windows contém suporte interno para multiplexação de pino nas estruturas GpioClx, SpbCx e SerCx. Essas estruturas trabalham juntas para alternar automaticamente um pino para a função correta quando um pino ou barramento do GPIO é acessado. O acesso aos pinos é arbitrado para evitar conflitos entre vários clientes. Além desse suporte interno, as interfaces e protocolos para multiplexação de pino são de uso geral e podem ser estendidos para dar suporte a dispositivos e cenários adicionais.

Este documento primeiro descreve as interfaces e protocolos subjacentes envolvidos na multiplexação de pino e, em seguida, descreve como adicionar suporte para multiplexação de pino aos drivers do controlador GpioClx, SpbCx e SerCx.

Arquitetura da Multiplexação de Pino

Essa seção descreve as interfaces subjacentes e os protocolos envolvidos na multiplexação de pino. O conhecimento dos protocolos subjacentes não é necessariamente necessário para dar suporte a multiplexação de pino com drivers GpioClx/SpbCx/SerCx. Para obter detalhes sobre como dar suporte a multiplexação de pino com drivers GpioCls/SpbCx/SerCx, confira o Implementando o suporte a multiplexação de pino nos drivers de cliente GpioClx e Consumindo o suporte a multiplexação nos drivers do controlador SpbCx e SerCx.

O uso de multiplexação de pino é realizada pela cooperação de vários componentes.

  • Servidores de multiplexação de pino - são drivers que controlam o bloco de controle de multiplexação de pinos. Os servidores de multiplexação de pino recebem solicitações de multiplexação de pino de clientes por meio de solicitações para reservar recursos de multiplexação (por meio de IRP_MJ_CREATE) e solicitações para alternar a função de um pino (por meio de solicitações *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS-). O servidor de multiplexação de pino geralmente é o driver GPIO, uma vez que o bloco de multiplexação às vezes faz parte do bloco GPIO. Mesmo que o bloco de multiplexação seja um periférico separado, o driver GPIO é um local lógico para colocar a funcionalidade de multiplexação.
  • Clientes de multiplexação de pino - são drivers que consomem a multiplexação de pino. Os clientes de multiplexação de pino recebem recursos de multiplexação de pino do firmware de ACPI. Os recursos de multiplexação de pino são um tipo de recurso de conexão e são gerenciados pelo hub de recursos. Os clientes de multiplexação de pino reservam recursos de multiplexação de pino abrindo um identificador para o recurso. Para efetuar 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 pino fechando o identificador, momento em que a configuração de multiplexação é revertida para seu estado padrão.
  • Firmware de ACPI – especifica a configuração de multiplexação com recursos MsftFunctionConfig(). Os recursos MsftFunctionConfig expressam quais pinos, em qual configuração de multiplexação, são necessários para um cliente. Os recursos MsftFunctionConfig contêm o número de função, a configuração de pull e a lista de números de pinos. Os recursos MsftFunctionConfig são fornecidos aos clientes de multiplexação de pino como recursos de hardware, que são recebidos pelos drivers em seu retorno de chamada PrepareHardware, de forma semelhante aos recursos de conexão do GPIO e SPB. Os clientes recebem uma ID do hub de recursos que pode ser usada para abrir um identificador para o recurso.

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

A sequência de operações envolvidas na multiplexação de pino é mostrada abaixo.

Pin muxing client server interaction

  1. O cliente recebe recursos MsftFunctionConfig do firmware de ACPI em seu retorno de chamada EvtDevicePrepareHardware().
  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, em seguida, consulta o hub de recursos para obter o descritor de recursos.
  4. O servidor realiza arbitragem de compartilhamento para cada pino no descritor e conclui a solicitação 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 multiplexação de hardware tornando a função especificada ativa em cada pino.
  7. O cliente prossegue com operações que dependem da configuração de multiplexação de pino solicitada.
  8. Quando o cliente não precisa mais que os pinos sejam multiplexados, ele fecha o identificador.
  9. Em resposta ao identificador que está sendo fechado, o servidor reverte os pinos de volta para seu estado inicial.

Descrição do protocolo para clientes de multiplexação de pino

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

Analisando recursos

Um driver WDF recebe os recursos MsftFunctionConfig() em sua 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 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 multiplexar 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 possa ser fechado posteriormente. Quando o driver estiver pronto para liberar a configuração de multiplexação, ele deverá fechar o identificador de recursos chamando WdfObjectDelete() ou WdfIoTargetClose() se você pretende reutilizar o WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Quando o cliente fecha o identificador de recurso, os pinos voltam multiplexado para seu estado inicial e agora podem ser adquiridos por um cliente diferente.

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

Esta seção descreve como um servidor de multiplexação de pino expõe sua funcionalidade aos clientes. Isso não se aplica aos drivers de miniporta GpioClx, pois a estrutura implementa esse protocolo em nome dos drivers cliente. Para obter detalhes sobre como dar suporte a multiplexação de pino em drivers de cliente GpioClx, confira Implementando suporte de multiplexação em Drivers de Cliente GpioClx.

Manipulando solicitações de IRP_MJ_CREATE

Os clientes abrem um identificador para um recurso quando desejam reservar um recurso de multiplexação de pino. Um servidor de multiplexação de pino recebe solicitações IRP_MJ_CREATE por meio de uma operação de nova análise do hub de recursos. O componente do caminho final da solicitação IRP_MJ_CREATE contém a ID do hub de recursos, que é um número inteiro de 64 bits em formato hexadecimal. O servidor deve extrair a ID do hub de recursos do nome do arquivo usando RESOURCE_HUB_ID_FROM_FILE_NAME() de reshub.h e enviar IOCTL_RH_QUERY_CONNECTION_PROPERTIES para o hub de recursos a fim de obter o descritor MsftFunctionConfig().

O servidor deve validar o descritor e extrair o modo de compartilhamento e a lista de pinos do descritor. Ele deve então realizar a arbitragem de compartilhamento dos pinos e, se for bem-sucedido, marcar os pinos como reservados antes de concluir a solicitação.

A arbitragem de compartilhamento terá êxito em 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 será bem-sucedida.
  • 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 for compartilhada, a arbitragem de compartilhamento será bem-sucedida.
    • e a solicitação de entrada for exclusiva, a arbitragem de compartilhamento falhará.

Se a arbitragem de compartilhamento falhar, a solicitação deverá ser concluída com STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Se a arbitragem de compartilhamento for bem-sucedida, a solicitação deverá 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 do IrpSp->Parameters.Create.ShareAccess.

Manipulando solicitações IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

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

  • Definir o modo de pull especificado no membro PinConfiguration da estrutura PNP_FUNCTION_CONFIG_DESCRIPTOR no hardware.
  • Multiplexar o pino para a 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 o conhecimento de como o servidor interpreta este campo.

Lembre-se que quando o identificador for fechado, o servidor terá que reverter os pinos para a configuração em que estavam quando IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS foi recebido, portanto, o servidor pode precisar salvar o estado dos pinos antes de modificá-los.

Manipulando solicitações de IRP_MJ_CLOSE

Quando um cliente não precisa mais de um recurso de multiplexação, 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. O servidor deve então marcar os pinos como disponíveis em relação à arbitragem de compartilhamento e concluir a solicitação com STATUS_SUCCESS. Certifique-se de sincronizar adequadamente o tratamento de IRP_MJ_CLOSE com o tratamento de IRP_MJ_CREATE.

Diretrizes de criação para tabelas ACPI

Essa seção descreve como fornecer recursos de multiplexação para drivers de clientes. Observe que você precisará do compilador do Microsoft ASL build 14327 ou posterior para compilar tabelas contendo recursos MsftFunctionConfig(). Os recursos MsftFunctionConfig() são fornecidos para fixar clientes de multiplexação de pino como recursos de hardware. Os recursos MsftFunctionConfig() devem ser fornecidos para drivers que exigem alterações de multiplexação de pinos, que normalmente são drivers do controlador serial e do SPB, mas não devem ser fornecidos para drivers de periféricos seriais e do SPB, uma vez que o driver do controlador lida com 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 pino 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 descoordenados 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 da ativação 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 multiplexador.
  • ResourceSource – O caminho do namespace de ACPI do servidor de multiplexação de pino
  • ResourceSourceIndex – defina isso como 0
  • ResourceConsumer/ResourceProducer – defina isso como ResourceConsumer
  • VendorData – dados binários opcionais cujo significado é definido pelo servidor de multiplexação de pino. Isso geralmente deve ser deixado em branco
  • Lista de Pinos - uma lista separada por vírgulas de números de pinos aos quais a configuração se aplica. Quando o servidor de multiplexação de pino é um driver GpioClx, esses 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 do 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 do controlador, um recurso 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 resistor de pull-up habilitado.

Dando suporte à multiplexação em drivers de cliente GpioClx

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

  • CLIENT_ConnectFunctionConfigPins – chamado por GpioClx para comandar o driver de miniporta para aplicar a configuração de multiplexação especificada.
  • CLIENT_DisconnectFunctionConfigPins – chamado por GpioClx para comandar o driver de miniporta para reverter a configuração de multiplexação.

Confira as Funções de Retorno de Chamada de Eventos GpioClx para obter uma descrição dessas rotinas.

Além desses dois novos DDIs, os DDIs existentes devem ser auditados para compatibilidade de multiplexação de pino:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins é chamado pelo GpioClx para comandar o driver de miniporta para configurar um conjunto de pinos para entrada ou saída GPIO. O GPIO é mutuamente exclusivo com MsftFunctionConfig, o que significa que um pino nunca será conectado para o GPIO e MsftFunctionConfig ao mesmo tempo. Como a função padrão de um pino não precisa ser GPIO, um pino pode não necessariamente não ser multiplexado para o GPIO quando ConnectIoPins é chamado. O ConnectIoPins é necessário para executar todas as operações necessárias para preparar o pino para o E/S de GPIO, incluindo operações de multiplexação. CLIENT_ConnectInterrupt deve se comportar da mesma forma, uma vez que as interrupções podem ser consideradas como um caso especial de entrada GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt – Essa rotina deve retornar os 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, a miniporta também deve reverter o estado de multiplexação de cada pino para o estado em que se encontrava quando a rotina de _Connect foi chamada.

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

Suporte a multiplexação em drivers do controlador SpbCx e SerCx

A partir do Windows 10 build 14327, as estruturas SpbCx e SerCx contêm suporte interno para multiplexação de pino que permite que os drivers do controlador SpbCx e SerCx sejam clientes de multiplexação de pino sem nenhuma alteração de código nos próprios drivers do controlador. Por extensão, qualquer driver periférico SpbCx/SerCx que se conecte a um driver do controlador SpbCx/SerCx habilitado para multiplexação acionará a atividade de multiplexação de pino.

O diagrama a seguir mostra as dependências entre cada um desses componentes. Como você pode ver, a multiplexação de pino introduz uma dependência dos drivers do controlador SerCx e SpbCx para o driver GPIO, que geralmente é responsável pela multiplexação.

Pin muxing dependency

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

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

SpbCx reverte a configuração de multiplexação de pino em seu manipulador IRP_MJ_CLOSE, logo após invocar o retorno de chamada EvtSpbTargetDisconnect() do driver do controlador. 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 desmultiplexados quando o driver periférico fecha o identificador.

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

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

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

Verificação

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

  1. Verifique se cada driver do controlador SpbCx, GpioClx, e SerCx está sendo carregado e operando corretamente
  2. Verifique se ele rhproxy está presente no sistema. Algumas edições e builds do Windows não o têm.
  3. Compile e carregue seu nó do 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 do 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 *

Observe 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 o motivo pelo qual esse dispositivo não está carregando. Todos os dispositivos deveriam ter sido habilitados durante a inicialização da plataforma. A solução de problemas de drivers do controlador SpbCx, GpioClx ou SerCx está além do escopo deste documento.

Verifique se o 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, o rhproxy não existe no seu sistema. O Rhproxy está presente em todas as compilações do IoT Core e nas compilações do Windows Enterprise 15063 e posterior.

Compilar e carregar o ASL com a ACPITABL.dat

Agora que você criou um nó ASL do rhproxy, é hora de compilá-lo e carregá-lo. Você pode compilar o nó do rhproxy em um arquivo AML independente que pode ser anexado às tabelas ACPI do sistema. Como alternativa, se você tiver acesso às fontes ACPI do seu sistema, poderá inserir o nó do rhproxy diretamente nas tabelas ACPI da sua plataforma. No entanto, durante a inicialização, 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 WDK e encontre asl.exe em C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

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

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

  5. Ative a atribuição de teste em seu 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 comando a seguir 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 incluídas com sucesso no sistema.

Verifique se rhproxy está carregando e iniciando

Verifique o status do rhproxy:

devcon status *msft8000

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

  • Problema 51 – CM_PROB_WAITING_ON_DEPENDENCY – O sistema não está iniciando o rhproxy porque uma das dependências dele falhou ao carregar. Isso significa que os recursos passados para o rhproxy apontam para nós de ACPI inválidas ou os dispositivos de destino não estão iniciando. Primeiro, verifique se todos os dispositivos estão sendo executados com sucesso (confira "Verificar drivers do controlador" acima). Em seguida, verifique novamente seu ASL e certifique-se de que todos os caminhos de recursos (por exemplo, \_SB.I2C1) estejam corretos e apontem para nós válidos em seu DSDT.
  • Problema 10 - CM_PROB_FAILED_START - O Rhproxy falhou ao iniciar, provavelmente devido a um problema de análise de recursos. Examine seu ASL e verifique os índices de recursos no DSD e verifique se 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 diversas ferramentas de linha de comando para enumerar os dispositivos e ver se eles estão presentes.

Clone o repositório https://github.com/ms-iot/samples e crie os exemplos GpioTestTool, I2cTestTool, SpiTestTool e Mincomm. Copie as ferramentas para seu dispositivo em teste e use os comandos a seguir para enumerar os 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 e os nomes amigáveis corretos, verifique seu 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)

Verifique cada dispositivo de um aplicativo UWP

Use os exemplos a seguir para validar se os dispositivos funcionam na UWP.

Executar os Testes do HLK

Baixe o Hardware Lab Kit (HLK). Os seguintes testes são compatí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 o "dispositivo Proxy do Hub de Recursos":

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

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

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.

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

Recursos

Apêndice

Apêndice A - Listagem ASL do Raspberry Pi

Veja também Mapeamentos de 3 Pinos do Raspberry Pi 2

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 do MinnowBoardMax

Confira também Mapeamentos Máximos de pinos do 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 }},
                }
            })
        }
    }
}

Apêndice C - Exemplo de script do PowerShell para gerar recursos GPIO

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