Compartir a través de


Habilitar el acceso de modo usuario a GPIO, I2C y SPI

En Windows 10 y versiones posteriores, las API se proporcionan con acceso directo desde el modo de usuario a la entrada y salida de uso general (GPIO), el circuito integrado entre (I2C), la interfaz periférica serie (SPI) y el transmisor de receptor asincrónico universal (UART). Los paneles de desarrollo, como Raspberry Pi 2, exponen un subconjunto de estas conexiones, lo que le permite ampliar un módulo de proceso base con circuitos personalizados para abordar una aplicación determinada. Estos buses de bajo nivel suelen compartirse con otras funciones de incorporación críticas, con solo un subconjunto de pines y buses GPIO expuestos en encabezados. Para conservar la estabilidad del sistema, es necesario especificar qué patillas y buses son seguros para la modificación por parte de las aplicaciones en modo de usuario.

En este documento se describe cómo especificar esta configuración en Interfaz avanzada de configuración y energía (ACPI) y se proporcionan herramientas para validar que la configuración se especificó correctamente.

Importante

La audiencia de este documento es Unified Extensible Firmware Interface (UEFI) y los desarrolladores ACPI. Se asume cierta familiaridad con la creación del lenguaje de origen ACPI (ASL) y SpbCx/GpioClx.

El acceso en modo de usuario a buses de bajo nivel en Windows se reduce a través de los marcos de GpioClx y SpbCx existentes. Un nuevo controlador llamado RhProxy, disponible en Windows IoT Core y Windows Enterprise, expone GpioClx y SpbCx recursos al modo de usuario. Para habilitar las API, se debe declarar un nodo de dispositivo para rhproxy en las tablas ACPI con cada uno de los recursos GPIO y SPB que se deben exponer al modo de usuario. Este documento le guía por la creación y comprobación del ASL.

ASL por ejemplo

Veamos la declaración de nodo del dispositivo rhproxy en Raspberry Pi 2. En primer lugar, cree la declaración del dispositivo ACPI en el ámbito \_SB.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID id. de – hardware. Establézcalo en un id. de hardware específico del proveedor.
  • –_CID id. compatible. Debe ser “MSFT8000”.
  • –_UID id. único. Establézcalo en 1.

A continuación, declaramos cada uno de los recursos GPIO y SPB que se deben exponer al modo de usuario. El orden en el que se declaran los recursos es importante porque los índices de recursos se usan para asociar propiedades a los recursos. Si hay varios buses I2C o SPI expuestos, el primer bus declarado se considera el ‘bus predeterminado’ para ese tipo y será la instancia devuelta por los métodos GetDefaultAsync() de Windows.Devices.I2c.I2cController y Windows.Devices.Spi.SpiController.

SPI

Raspberry Pi tiene dos buses SPI expuestos. SPI0 tiene dos líneas de selección de chip de hardware y SPI1 tiene una línea de selección de chip de hardware. Se requiere una declaración de recursos SPISerialBus() para cada línea de selección de chip para cada bus. Las dos declaraciones de recursos SPISerialBus siguientes son para las dos líneas de selección de chip en SPI0. El campo DeviceSelection contiene un valor único que el controlador interpreta como un identificador de línea de selección de chip de hardware. El valor exacto que ha colocado en el campo DeviceSelection depende de cómo el controlador interprete este campo del descriptor de conexión ACPI.

Nota:

Este artículo contiene referencias al término esclavo, un término que Microsoft no condona y ha dejado de usar en nuevos productos y documentación. Cuando se quite el término del software, se quitará también del artículo.

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

¿Cómo sabe el software que estos dos recursos deben asociarse con el mismo bus? La asignación entre el nombre descriptivo del bus y el índice de recursos se especifica en DSD:

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

Esto crea un bus denominado "SPI0" con dos líneas de selección de chip: índices de recursos 0 y 1. Se requieren varias propiedades más para declarar las funcionalidades del bus SPI.

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

Las propiedades MinClockInHz y MaxClockInHz especifican las velocidades de reloj mínimas y máximas admitidas por el controlador. La API impedirá que los usuarios especifiquen valores fuera de este intervalo. La velocidad del reloj se pasa al controlador SPB en el campo _SPE del descriptor de conexión (sección ACPI 6.4.3.8.2.2).

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

La propiedad SupportedDataBitLengths enumera las longitudes de bits de datos admitidas por el controlador. Se pueden especificar varios valores en una lista separada por comas. La API impedirá que los usuarios especifiquen valores fuera de esta lista. La longitud del bit de datos se pasa al controlador SPB en el campo _LEN del descriptor de conexión (sección ACPI 6.4.3.8.2.2).

Puede considerar estas declaraciones de recursos como “plantillas.” Algunos de los campos se fijan en el arranque del sistema, mientras que otros se especifican dinámicamente en tiempo de ejecución. Se han corregido los siguientes campos del descriptor SPISerialBus:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

Los campos siguientes son marcadores de posición para los valores especificados por el usuario en tiempo de ejecución:

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • ClockPhase

Dado que SPI1 solo contiene una sola línea de selección de chip, se declara un único recurso SPISerialBus():

// Index 2
SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                           // MOSI - GPIO 20 - Pin 38
                           // MISO - GPIO 19 - Pin 35
                           // CE1  - GPIO 17 - Pin 11
    1,                     // Device selection (CE1)
    PolarityLow,           // Device selection polarity
    FourWireMode,          // wiremode
    0,                     // databit len: placeholder
    ControllerInitiated,   // slave mode
    0,                     // connection speed: placeholder
    ClockPolarityLow,      // clock polarity: placeholder
    ClockPhaseFirst,       // clock phase: placeholder
    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
    0,                     // ResourceSourceIndex
                           // Resource usage
    )                      // Vendor Data

La declaración de nombre descriptivo que acompaña, que es necesaria, se especifica en el DSD y hace referencia al índice de esta declaración de recursos.

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

Esto crea un bus denominado “SPI1” y lo asocia al índice de recursos 2.

Requisitos del controlador SPI

  • Debe usar SpbCx o ser compatible con SpbCx
  • Debe haber superado las pruebas SPI de MITT
  • Debe admitir la velocidad del reloj de 4Mhz
  • Debe admitir la longitud de datos de 8 bits
  • Debe admitir todos los modos SPI: 0, 1, 2, 3

I2C

A continuación, declaramos los recursos de I2C. Raspberry Pi expone un solo bus I2C en las patillas 3 y 5.

// Index 3
I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
    0xFFFF,                // SlaveAddress: placeholder
    ,                      // SlaveMode: default to ControllerInitiated
    0,                     // ConnectionSpeed: placeholder
    ,                      // Addressing Mode: placeholder
    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
    ,
    ,
    )                      // VendorData

La declaración de nombre descriptivo que acompaña, que es necesaria, se especifica en el DSD:

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

Esto declara un bus I2C con el nombre descriptivo "I2C1" que hace referencia al índice de recursos 3, que es el índice del recurso I2CSerialBus() que declaramos anteriormente.

Se han corregido los siguientes campos del descriptor I2CSerialBus():

  • SlaveMode
  • ResourceSource

Los siguientes campos son marcadores de posición para los valores especificados por el usuario en tiempo de ejecución.

  • SlaveAddress
  • ConnectionSpeed
  • AddressingMode

Requisitos del controlador I2C

  • Debe usar SpbCx o ser compatible con SpbCx
  • Debe haber superado las pruebas de MITT I2C
  • Debe admitir el direccionamiento de 7 bits
  • Debe admitir la velocidad del reloj de 100 kHz
  • Debe admitir la velocidad del reloj de 400kHz.

GPIO

A continuación, declaramos todos los patillas GPIO que se exponen al modo de usuario. Ofrecemos las siguientes instrucciones para decidir qué patillas exponer:

  • Declare todos los patillas en los encabezados expuestos.
  • Declare pins que están conectados a funciones de incorporación útiles como botones y LED.
  • No declare patillas reservadas para funciones del sistema o que no estén conectadas a nada.

El siguiente bloque de ASL declara dos patillas: GPIO4 y GPIO5. Las otras patillas no se muestran aquí para mayor brevedad. El apéndice C contiene un script de PowerShell de ejemplo que se puede usar para generar los 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 }

Se deben observar los siguientes requisitos al declarar patillas GPIO:

  • Solo se admiten controladores GPIO asignados a memoria. No se admiten controladores GPIO interfazados a través de I2C/SPI. El controlador del controlador es un controlador asignado a memoria si establece la marca MemoryMappedController en la estructura CLIENT_CONTROLLER_BASIC_INFORMATION en respuesta a la devolución de llamada de CLIENT_QueryControllerBasicInformation.
  • Cada patilla requiere un recurso GpioIO y GpioInt. El recurso GpioInt debe seguir inmediatamente el recurso GpioIO y debe hacer referencia al mismo número de patilla.
  • Los recursos GPIO deben ordenarse aumentando el número de patillas.
  • Cada recurso GpioIO y GpioInt deben contener exactamente un número de patilla en la lista de patillas.
  • El campo ShareType de ambos descriptores debe ser Compartido
  • El campo EdgeLevel del descriptor GpioInt debe ser Edge
  • El campo ActiveLevel del descriptor GpioInt debe ser ActiveBoth
  • Campo PinConfig
    • Debe ser el mismo en los descriptores GpioIO y GpioInt
    • Debe ser uno de PullUp, PullDown o PullNone. No puede ser PullDefault.
    • La configuración de extracción debe coincidir con el estado de encendido de la patilla. La colocación del pin en el modo de extracción especificado del estado de encendido no debe cambiar el estado de la patilla. Por ejemplo, si la hoja de datos especifica que el pin aparece con una extracción, especifique PinConfig como PullUp.

El código de inicialización de firmware, UEFI y controlador no debe cambiar el estado de una patilla desde su estado de encendido durante el arranque. Solo el usuario sabe lo que está asociado a un pin y, por tanto, qué transiciones de estado son seguras. El estado de encendido de cada patilla debe documentarse para que los usuarios puedan diseñar hardware que interactúa correctamente con un pin. Una patilla no debe cambiar el estado inesperadamente durante el arranque.

Modos de unidad admitidos

Si el controlador GPIO admite resistencias integradas de extracción y extracción, además de la entrada de alta impedancia y la salida de CMOS, debe especificarlo con la propiedad SupportedDriveModes opcional.

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

La propiedad SupportedDriveModes indica qué modos de unidad son compatibles con el controlador GPIO. En el ejemplo anterior, se admiten todos los siguientes modos de unidad. La propiedad es una máscara de bits de los siguientes valores:

Valor de marca Modo de unidad Descripción
0x1 InputHighImpedance La patilla admite una entrada de impedancia alta, que corresponde al “valor PullNone” en ACPI.
0x2 InputPullUp El pin admite una resistencia de extracción integrada, que corresponde al valor "PullUp" en ACPI.
0x4 InputPullDown El pin admite una resistencia de extracción integrada, que corresponde al valor "PullUp" en ACPI.
0x8 OutputCmos La patilla admite la generación de altos fuertes y fuertes mínimos (en lugar de drenaje abierto).

InputHighImpedance y OutputCmos son compatibles con casi todos los controladores GPIO. Si no se especifica la propiedad SupportedDriveModes, este es el valor predeterminado.

Si una señal GPIO pasa por un desplazamiento de nivel antes de llegar a un encabezado expuesto, declare los modos de unidad admitidos por el SOC, incluso si el modo de unidad no sería observable en el encabezado externo. Por ejemplo, si un pin pasa por un desplazamiento de nivel bidireccional que hace que un pin aparezca como purga abierta con extracción resistiva, nunca observará un estado de impedancia alto en el encabezado expuesto incluso si la patilla está configurada como una entrada de impedancia alta. Todavía debe declarar que el pin admite una entrada de impedancia alta.

Numeración de patillas

Windows admite dos esquemas de numeración de patillas:

  • Numeración secuencial de patillas: Los usuarios ven números como 0, 1, 2... hasta el número de patillas expuestas. 0 es el primer recurso GpioIo declarado en ASL, 1 es el segundo recurso GpioIo declarado en ASL, etc.
  • Numeración de patillas nativas: Los usuarios ven los números de pin especificados en descriptores GpioIo, por ejemplo, 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

La propiedad UseDescriptorPinNumbers indica a Windows que use la numeración de patillas nativas en lugar de numeración de patillas secuenciales. Si no se especifica la propiedad UseDescriptorPinNumbers o su valor es cero, Windows tendrá como valor predeterminado numeración de patillas secuenciales.

Si se usa la numeración de patillas nativas, también debe especificar la propiedad PinCount.

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

La propiedad PinCount debe coincidir con el valor devuelto a través de la propiedad TotalPins en la devolución de llamada CLIENT_QueryControllerBasicInformation del controlador GpioClx.

Elija el esquema de numeración más compatible con la documentación publicada existente para el panel. Por ejemplo, Raspberry Pi usa la numeración de patillas nativas porque muchos diagramas de anclaje existentes usan los números de patilla BCM2835. MinnowBoardMax usa la numeración de patillas secuenciales porque hay pocos diagramas de anclaje existentes y la numeración de patillas secuenciales simplifica la experiencia del desarrollador porque solo se exponen 10 patillas de más de 200 patillas. La decisión de usar la numeración de patillas secuenciales o nativas debe tener como objetivo reducir la confusión del desarrollador.

Requisitos del controlador GPIO

  • Debe usar GpioClx
  • Debe estar asignada la memoria en SOC
  • Debe usar el control de interrupciones de ActiveBoth emulado.

UART

Si el controlador UART usa SerCx o SerCx2, puede usar rhproxy para exponer el controlador al modo de usuario. Los controladores UART que crean una interfaz de dispositivo de tipo GUID_DEVINTERFACE_COMPORT no necesitan usar rhproxy. El controlador de bandeja de entrada Serial.sys es uno de estos casos.

Para exponer un SerCxUART de estilo al modo de usuario, declare un UARTSerialBus recurso como se indica a continuación.

// Index 2
UARTSerialBus(           // Pin 17, 19 of JP1, for SIO_UART2
    115200,                // InitialBaudRate: in bits ber second
    ,                      // BitsPerByte: default to 8 bits
    ,                      // StopBits: Defaults to one bit
    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
    ,                      // IsBigEndian: default to LittleEndian
    ,                      // Parity: Defaults to no parity
    ,                      // FlowControl: Defaults to no flow control
    32,                    // ReceiveBufferSize
    32,                    // TransmitBufferSize
    "\\_SB.URT2",          // ResourceSource: UART bus controller name
    ,
    ,
    ,
    )

Solo se fija el campo ResourceSource mientras que todos los demás campos son marcadores de posición para los valores especificados en tiempo de ejecución por el usuario.

La declaración de nombre descriptivo que acompaña es:

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

Esto asigna el nombre “descriptivo UART2” al controlador, que es el identificador que los usuarios usarán para acceder al bus desde el modo de usuario.

Multiplexación de patillas en tiempo de ejecución

La multiplexación de patillas es la capacidad de usar la misma patilla física para diferentes funciones. Varios periféricos en chip diferentes, como un controlador I2C, un controlador SPI y un controlador GPIO, pueden enrutarse al mismo pin físico en un SOC. El bloque mux controla qué función está activa en el pin en cualquier momento dado. Tradicionalmente, el firmware es responsable de establecer asignaciones de funciones en el arranque y esta asignación permanece estática a través de la sesión de arranque. La multiplexación de patillas en tiempo de ejecución agrega la capacidad de volver a configurar las asignaciones de funciones de anclaje en tiempo de ejecución. Permitir que los usuarios elijan una función de anclaje a velocidades de tiempo de ejecución, ya que permiten a los usuarios volver a configurar rápidamente las patillas de una placa y permite que el hardware admita una gama más amplia de aplicaciones que una configuración estática.

Los usuarios consumen compatibilidad de multiplexación con GPIO, I2C, SPI y UART sin escribir código adicional. Cuando un usuario abre un GPIO o bus mediante OpenPin() o FromIdAsync(), las patillas físicas subyacentes se silencian automáticamente a la función solicitada. Si las patillas ya están en uso por otra función, se producirá un error en la llamada a OpenPin() o FromIdAsync(). Cuando el usuario cierra el dispositivo mediante la eliminación del objetoGpioPin, I2cDevice, SpiDevice o SerialDevice, se liberan las patillas, lo que permite que se abran posteriormente para una función diferente.

Windows contiene compatibilidad integrada para la multiplexación de patillas en los marcos de GpioClx, SpbCxy SerCx. Estos marcos funcionan juntos para cambiar automáticamente una patilla a la función correcta cuando se accede a un pin o bus GPIO. El acceso a las patillas se arbitra para evitar conflictos entre varios clientes. Además de esta compatibilidad integrada, las interfaces y los protocolos para la multiplexación de patillas son de uso general y se pueden ampliar para admitir dispositivos y escenarios adicionales.

En este documento se describen primero las interfaces y protocolos subyacentes implicados en la multiplexación de patillas y, a continuación, se describe cómo agregar compatibilidad con la multiplexación de patillas a los controladores de controlador GpioClx, SpbCx y SerCx.

Arquitectura de multiplexación de patillas

En esta sección se describen las interfaces y protocolos subyacentes implicados en la multiplexación de patillas. El conocimiento de los protocolos subyacentes no es necesariamente necesario para admitir la multiplexación de patillas con controladores GpioClx/SpbCx/SerCx. Para obtener más información sobre cómo admitir la multiplexación de patillas con controladores GpioCls/SpbCx/SerCx, vea Compatibilidad con la implementación de la multiplexación de patillas en controladores de cliente GpioClx y Consumo de compatibilidad con la multiplexación en los controladores de controlador SpbCx y SerCx.

La multiplexación de patillas se realiza mediante la cooperación de varios componentes.

  • Servidores de multiplexación de patillas: estos son controladores que controlan el bloque de control de multiplexación de patillas. Los servidores de multiplexación de patillas reciben solicitudes de multiplexación de patillas de los clientes a través de solicitudes para reservar recursos de multiplexación (a través de IRP_MJ_CREATE) y solicitudes para cambiar una función de patilla (a través de *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS- solicitudes). El servidor de multiplexación de patillas suele ser el controlador GPIO, ya que el bloque de multiplexación a veces forma parte del bloque GPIO. Incluso si el bloque de multiplexación es un periférico independiente, el controlador GPIO es un lugar lógico para colocar la funcionalidad de multiplexación.
  • Clientes de multiplexación de patillas: estos son controladores que consumen la multiplexación de patillas. Los clientes de multiplexación de patillas reciben recursos de multiplexación de patillas del firmware ACPI. Los recursos de multiplexación de patillas son un tipo de recurso de conexión y los administra el centro de recursos. Los clientes de multiplexación de patillas reservan recursos de multiplexación de patillas abriendo un identificador para el recurso. Para realizar un cambio de hardware, los clientes deben confirmar la configuración mediante el envío de una solicitud de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Los clientes liberan recursos de multiplexación de patillas cerrando el identificador, en cuyo momento la configuración de multiplexación se revierte a su estado predeterminado.
  • Firmware ACPI: especifica la configuración de multiplexación con MsftFunctionConfig() recursos. Los recursos MsftFunctionConfig expresan qué patillas, en qué configuración de multiplexación, son necesarias para un cliente. Los recursos MsftFunctionConfig contienen el número de función, la configuración de extracción y la lista de números de patillas. Los recursos MsftFunctionConfig se proporcionan para anclar clientes de multiplexación como recursos de hardware, que los controladores reciben en su devolución de llamada PrepareHardware de forma similar a los recursos de conexión GPIO y SPB. Los clientes reciben un identificador del centro de recursos que se puede usar para abrir un identificador para el recurso.

Debe pasar el modificador de línea de comandos de /MsftInternal a asl.exe para compilar archivos ASL que contengan descriptores MsftFunctionConfig(), ya que estos descriptores están actualmente revisados por el comité de trabajo ACPI. Por ejemplo: asl.exe /MsftInternal dsdt.asl

A continuación se muestra la secuencia de operaciones implicadas en la multiplexación de patillas.

Interacción del servidor cliente de multiplexación de patillas

  1. El cliente recibe recursos MsftFunctionConfig del firmware ACPI en su devolución de llamada EvtDevicePrepareHardware()..
  2. El cliente usa la función RESOURCE_HUB_CREATE_PATH_FROM_ID() auxiliar del centro de recursos para crear una ruta de acceso desde el identificador de recurso y, a continuación, abre un identificador a la ruta de acceso (mediante ZwCreateFile(), IoGetDeviceObjectPointer()o WdfIoTargetOpen()).
  3. El servidor extrae el identificador del centro de recursos de la ruta de acceso del archivo mediante las funciones RESOURCE_HUB_ID_FROM_FILE_NAME()auxiliares del centro de recursos y, a continuación, consulta el centro de recursos para obtener el descriptor de recursos.
  4. El servidor realiza el arbitraje de uso compartido para cada patilla del descriptor y completa la solicitud de IRP_MJ_CREATE.
  5. El cliente emite una solicitud de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS en el identificador recibido.
  6. En respuesta a IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, el servidor realiza la operación de multiplexación de hardware haciendo que la función especificada se active en cada patilla.
  7. El cliente continúa con las operaciones que dependen de la configuración de multiplexación de patillas solicitada.
  8. Cuando el cliente ya no requiere que se muxen las patillas, cierra el identificador.
  9. En respuesta al identificador que se está cerrando, el servidor revierte las patillas a su estado inicial.

Descripción del protocolo para los clientes de multiplexación de patillas

En esta sección se describe cómo un cliente consume la funcionalidad de multiplexación de patillas. Esto no se aplica a los controladores de controlador de SerCx y SpbCx, ya que los marcos implementan este protocolo en nombre de los controladores de controlador.

Análisis de recursos

Un controlador WDF recibe recursos MsftFunctionConfig() en su rutina EvtDevicePrepareHardware(). Los recursos MsftFunctionConfig se pueden identificar mediante los campos siguientes:

CM_PARTIAL_RESOURCE_DESCRIPTOR::Type = CmResourceTypeConnection
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Class = CM_RESOURCE_CONNECTION_CLASS_FUNCTION_CONFIG
CM_PARTIAL_RESOURCE_DESCRIPTOR::u.Connection.Type = CM_RESOURCE_CONNECTION_TYPE_FUNCTION_CONFIG

Una EvtDevicePrepareHardware() rutina puede extraer recursos MsftFunctionConfig de la siguiente manera:

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

Reservar y confirmar recursos

Cuando un cliente quiere mux pins, se reserva y confirma el recurso MsftFunctionConfig. En el ejemplo siguiente se muestra cómo un cliente podría reservar y 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;
}

El controlador debe almacenar el WDFIOTARGET en una de sus áreas de contexto para que se pueda cerrar más adelante. Cuando el controlador esté listo para liberar la configuración de multiplexación, debe cerrar el identificador de recursos mediante una llamada a WdfObjectDelete() o WdfIoTargetClose() si piensa reutilizar WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Cuando el cliente cierra su identificador de recursos, las patillas se vuelven a cambiar a su estado inicial y ahora se pueden adquirir mediante un cliente diferente.

Descripción del protocolo para los servidores de multiplexación de patillas

En esta sección se describe cómo un servidor de multiplexación de patillas expone su funcionalidad a los clientes. Esto no se aplica a GpioClx los controladores de miniportar, ya que el marco implementa este protocolo en nombre de los controladores cliente. Para obtener más información sobre cómo admitir la multiplexación de patillas en controladores de cliente de GpioClx, vea Compatibilidad con la multiplexación en controladores de cliente GpioClx.

Control de solicitudes de IRP_MJ_CREATE

Los clientes abren un identificador a un recurso cuando quieren reservar un recurso de multiplexación de patillas. Un servidor de multiplexación de patillas recibe solicitudes IRP_MJ_CREATE mediante una operación de reanálisis desde el centro de recursos. El componente de ruta de acceso final de la solicitud IRP_MJ_CREATE contiene el identificador del centro de recursos, que es un entero de 64 bits en formato hexadecimal. El servidor debe extraer el identificador del centro de recursos del nombre de archivo mediante RESOURCE_HUB_ID_FROM_FILE_NAME() de reshub.h y enviar IOCTL_RH_QUERY_CONNECTION_PROPERTIES al centro de recursos para obtener el descriptor MsftFunctionConfig().

El servidor debe validar el descriptor y extraer el modo de uso compartido y la lista de patillas del descriptor. A continuación, debe realizar el arbitraje de uso compartido para las patillas y, si se realiza correctamente, marcar las patillas como reservadas antes de completar la solicitud.

El arbitraje de uso compartido se realiza correctamente si el arbitraje de uso compartido se realiza correctamente para cada pin de la lista de patillas. Cada patilla debe ser arbitrada de la siguiente manera:

  • Si el pin aún no está reservado, el arbitraje de uso compartido se realiza correctamente.
  • Si el pin ya está reservado como exclusivo, se produce un error en el arbitraje de uso compartido.
  • Si el pin ya está reservado como compartido,
    • y la solicitud entrante se comparte, el arbitraje de uso compartido se realiza correctamente.
    • y la solicitud entrante es exclusiva y se produce un error en el arbitraje de uso compartido.

Si se produce un error en el arbitraje de uso compartido, la solicitud debe completarse con STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Si el arbitraje de uso compartido se realiza correctamente, la solicitud debe completarse con STATUS_SUCCESS.

Tenga en cuenta que el modo de uso compartido de la solicitud entrante debe tomarse del descriptor MsftFunctionConfig, no IrpSp->Parameters.Create.ShareAccess.

Control de solicitudes de IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

Después de que el cliente haya reservado correctamente un recurso MsftFunctionConfig abriendo un identificador, puede enviar IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS para solicitar al servidor que realice la operación de multiplexación de hardware real. Cuando el servidor recibe IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, para cada pin de la lista de patillas debe

  • Establezca el modo de extracción especificado en el miembro PinConfiguration de la estructura PNP_FUNCTION_CONFIG_DESCRIPTOR en hardware.
  • Muxifica el pin a la función especificada por el miembro FunctionNumber de la estructura PNP_FUNCTION_CONFIG_DESCRIPTOR.

A continuación, el servidor debe completar la solicitud con STATUS_SUCCESS.

El significado de FunctionNumber se define mediante el servidor y se entiende que el descriptor MsftFunctionConfig se creó con conocimiento de cómo interpreta el servidor este campo.

Recuerde que cuando se cierra el identificador, el servidor tendrá que revertir las patillas a la configuración en la que se encontraban cuando se recibió IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, por lo que es posible que el servidor tenga que guardar el estado de los patillas antes de modificarlos.

Control de solicitudes de IRP_MJ_CLOSE

Cuando un cliente ya no requiere un recurso de multiplexación, cierra su identificador. Cuando un servidor recibe una solicitud de IRP_MJ_CLOSE, debe revertir las patillas al estado en el que se encontraban cuando se recibió IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Si el cliente nunca envió un IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, no es necesario realizar ninguna acción. A continuación, el servidor debe marcar las patillas como disponibles con respecto al arbitraje de uso compartido y completar la solicitud con STATUS_SUCCESS. Asegúrese de sincronizar correctamente IRP_MJ_CLOSE control con IRP_MJ_CREATE control.

Directrices de creación para tablas ACPI

En esta sección se describe cómo proporcionar recursos de multiplexación a los controladores cliente. Tenga en cuenta que necesitará la compilación 14327 o posterior del compilador ASL de Microsoft para compilar tablas que contengan recursos de MsftFunctionConfig(). MsftFunctionConfig() recursos se proporcionan para anclar clientes de multiplexación como recursos de hardware. MsftFunctionConfig() los recursos deben proporcionarse a los controladores que requieren cambios de multiplexación de patillas, que normalmente son controladores de controlador de serie y SPB, pero no deben proporcionarse a los controladores periféricos serie y SPB, ya que el controlador controla la configuración de multiplexación. La macro MsftFunctionConfig() ACPI se define de la siguiente manera:

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

  • Compartido/Exclusivo: Si es exclusivo, este pin se puede adquirir mediante un solo cliente a la vez. Si se comparte, varios clientes compartidos pueden adquirir el recurso. Establézcalo siempre en exclusivo, ya que permitir que varios clientes no coordinados accedan a un recurso mutable pueden provocar carreras de datos y, por tanto, resultados imprevisibles.
  • PinPullConfig: uno de
    • PullDefault: use la configuración de extracción predeterminada definida por SOC
    • PullUp: habilitar resistencia de extracción
    • PullDown: habilitar resistencia de extracción
    • PullNone: deshabilitar todas las resistencias de extracción
  • FunctionNumber: número de función que se va a programar en la mux.
  • ResourceSource: La ruta de acceso del espacio de nombres ACPI del servidor de multiplexación de patillas
  • ResourceSourceIndex: establézcalo en 0
  • ResourceConsumer/ResourceProducer: establézcalo en ResourceConsumer
  • VendorData: datos binarios opcionales cuyo significado se define mediante el servidor de multiplexación de patillas. Esto normalmente debe dejarse en blanco
  • Lista de patillas: una lista separada por comas de números de patillas a los que se aplica la configuración. Cuando el servidor de multiplexación de patillas es un controlador GpioClx, estos son números de patillas GPIO y tienen el mismo significado que los números de patilla en un descriptor GpioIo.

En el ejemplo siguiente se muestra cómo puede proporcionar un recurso MsftFunctionConfig() a un controlador 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)
    }
}

Además de la memoria y los recursos de interrupción que normalmente requiere un controlador de controlador, también se especifica un MsftFunctionConfig() recurso. Este recurso permite al controlador del controlador I2C colocar las patillas 2 y 3, administradas por el nodo del dispositivo en \_SB. GPIO0: en la función 4 con resistencia de extracción habilitada.

Compatibilidad con la compatibilidad con la multiplexación en controladores de cliente GpioClx

GpioClx tiene compatibilidad integrada con la multiplexación de patillas. Controladores de minipuerto de GpioClx (también conocidos como "controladores de cliente GpioClx"), hardware de controlador GPIO de unidad. A partir de la compilación 14327 de Windows 10, los controladores de minipuerto de GpioClx pueden agregar compatibilidad con la multiplexación de patillas mediante la implementación de dos nuevos DDIs:

  • CLIENT_Conectar FunctionConfigPins: llamado por GpioClx para comando al controlador de minipuerto para aplicar la configuración de multiplexación especificada.
  • CLIENT_Conectar FunctionConfigPins: llamado por GpioClx para comando al controlador de minipuerto para aplicar la configuración de multiplexación especificada.

Vea funciones de devolución de llamada de eventos GpioClx para obtener una descripción de estas rutinas.

Además de estos dos nuevos DDIs, se deben auditar las DDIs existentes para la compatibilidad con la multiplexación de patillas:

  • GpioClx llama a CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt – CLIENT_ConnectIoPins para que el controlador de minipuerto configure un conjunto de patillas para la entrada o salida de GPIO. GPIO es mutuamente excluyente con MsftFunctionConfig, lo que significa que un pin nunca se conectará para GPIO y MsftFunctionConfig al mismo tiempo. Dado que no es necesario que una función predeterminada del pin sea GPIO, es posible que un pin no sea multiplexado necesariamente a GPIO cuando se llama a ConnectIoPins. Conectar IoPins es necesario para realizar todas las operaciones necesarias para que el pin esté listo para la E/S de GPIO, incluidas las operaciones de multiplexación. CLIENT_ConnectInterrupt deben comportarse de forma similar, ya que las interrupciones se pueden considerar como un caso especial de entrada GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt: Esta rutina debe devolver patillas al estado en el que se encontraban cuando se llamó a CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt, a menos que se especifique la marca PreserveConfiguration. Además de revertir la dirección de las patillas a su estado predeterminado, el minipuerto también debe revertir cada estado de multiplexación de patillas al estado en el que se encontraba cuando se llamó a la rutina _Connect.

Por ejemplo, supongamos que una configuración de multiplexación predeterminada de patillas es UART y que la patilla también se puede usar como GPIO. Cuando se llama a CLIENT_ConnectIoPins para conectar la patilla para GPIO, debe silenciar la patilla a GPIO y, en CLIENT_DisconnectIoPins, debe silenciar la patilla de nuevo a UART. En general, las rutinas Desconexión deben deshacer las operaciones realizadas por las rutinas Conectar.

Compatibilidad con la multiplexación en controladores de controlador SpbCx y SerCx

A partir de la compilación 14327 de Windows 10, los SpbCx marcos y SerCx contienen compatibilidad integrada para la multiplexación de patillas que permite SpbCx que los controladores y SerCx controladores de controlador sean clientes de multiplexación de patillas sin cambios de código en los propios controladores del controlador. Por extensión, cualquier controlador periférico SpbCx/SerCx que se conecte a un controlador de controlador spbCx/SerCx habilitado para multiplexación desencadenará la actividad de multiplexación de patillas.

En el diagrama siguiente se muestran las dependencias entre cada uno de estos componentes. Como puede ver, la multiplexación de patillas introduce una dependencia de los controladores de controlador SerCx y SpbCx al controlador GPIO, que normalmente es responsable de la muxación.

Dependencia de multiplexación de patillas

En el momento de la inicialización del dispositivo, los SpbCx marcos y SerCx analizan todos los MsftFunctionConfig() recursos proporcionados como recursos de hardware para el dispositivo. A continuación, SpbCx/SerCx adquiere y libera los recursos de multiplexación de patillas a petición.

SpbCx aplica la configuración de multiplexación de patillas en su controlador de IRP_MJ_CREATE, justo antes de llamar a la devolución de llamada del controlador cliente EvtSpbTargetConnect(). Si no se pudo aplicar la configuración de multiplexación, no se llamará a la devolución de llamada del controlador del EvtSpbTargetConnect() controlador. Por lo tanto, un controlador del controlador SPB puede suponer que las patillas se muxan a la función SPB en el momento en que se llama a EvtSpbTargetConnect().

SpbCx revierte la configuración de multiplexación de patillas en su controlador de IRP_MJ_CLOSE, justo después de invocar la devolución de llamada del controlador EvtSpbTargetDisconnect(). El resultado es que las patillas se muxan a la función SPB cada vez que un controlador periférico abre un identificador al controlador del controlador SPB y se silencian cuando el controlador periférico cierra su controlador.

SerCx se comporta de forma similar. SerCx adquiere todos los recursos MsftFunctionConfig() en su controlador de IRP_MJ_CREATE justo antes de invocar al controlador EvtSerCx2FileOpen() devolución de llamada y libera todos los recursos en su controlador de IRP_MJ_CLOSE, justo después de invocar al controlador del control del controlador llamada devolución de EvtSerCx2FileClose.

La implicación de la multiplexación dinámica de patillas para SerCx los controladores de controlador y SpbCx es que deben ser capaces de tolerar que las patillas se muxen de la función SPB/UART en determinados momentos. Los controladores de controlador deben asumir que las patillas no se muxarán hasta que se llame a EvtSpbTargetConnect() o EvtSerCx2FileOpen(). Los anclajes no son necesarios para la función SPB/UART durante las siguientes devoluciones de llamada. A continuación no se muestra una lista completa, pero representa las rutinas de PNP más comunes implementadas por controladores de controlador.

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

Comprobación

Cuando esté listo para probar rhproxy, resulta útil usar el siguiente procedimiento paso a paso.

  1. Compruebe que cada SpbCxcontrolador de controlador, GpioClxy SerCx está cargando y funcionando correctamente
  2. Compruebe que rhproxy está presente en el sistema. Algunas ediciones y compilaciones de Windows no lo tienen.
  3. Compilación y carga del nodo rhproxy mediante ACPITABL.dat
  4. Comprobación de que el nodo del rhproxy dispositivo existe
  5. Comprobación de que rhproxy está cargando e iniciando
  6. Compruebe que los dispositivos esperados están expuestos al modo de usuario
  7. Compruebe que puede interactuar con cada dispositivo desde la línea de comandos
  8. Comprobar que puede interactuar con cada dispositivo desde una aplicación para UWP
  9. Ejecución de pruebas de HLK

Comprobación de los controladores de controlador

Dado que rhproxy expone otros dispositivos en el sistema al modo de usuario, solo funciona si esos dispositivos ya funcionan. El primer paso es comprobar que esos dispositivos (los controladores I2C, SPI, GPIO que desea exponer) ya funcionan.

En el símbolo del sistema, ejecute

devcon status *

Examine la salida y compruebe que se inician todos los dispositivos de interés. Si un dispositivo tiene un código de problema, debe solucionar el motivo por el que ese dispositivo no se está cargando. Todos los dispositivos deben haberse habilitado durante la incorporación inicial de la plataforma. La solución de problemas SpbCxde controladores, GpioClxo SerCx está fuera del ámbito de este documento.

Compruebe que rhproxy está presente en el sistema

Compruebe que el servicio rhproxy está presente en el sistema.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Si la clave reg no está presente, rhproxy no existe en el sistema. Rhproxy está presente en todas las compilaciones de IoT Core y Windows Enterprise compilación 15063 y posteriores.

Compilar y cargar ASL con ACPITABL.dat

Ahora que ha creado un nodo rhproxy ASL, es el momento de compilarlo y cargarlo. Puede compilar el nodo rhproxy en un archivo AML independiente que se pueda anexar a las tablas ACPI del sistema. Como alternativa, si tiene acceso a los orígenes ACPI del sistema, puede insertar el nodo rhproxy directamente en las tablas ACPI de la plataforma. Sin embargo, durante la incorporación inicial puede ser más fácil usar ACPITABL.dat.

  1. Cree un archivo denominado yourboard.asl y coloque el nodo del dispositivo RHPX dentro de DefinitionBlock:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Descargue el WDK y busque asl.exe en C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

  3. Ejecute el siguiente comando para generar ACPITABL.dat:

    asl.exe yourboard.asl
    
  4. Copie el archivo ACPITABL.dat resultante en c:\windows\system32 en el sistema en prueba.

  5. Active la asignación de pruebas en el sistema en prueba:

    bcdedit /set testsigning on
    
  6. Reinicie el sistema en prueba. El sistema anexará las tablas ACPI definidas en ACPITABL.dat a las tablas de firmware del sistema.

Compruebe que el nodo del dispositivo rhproxy existe

Ejecute el siguiente comando para enumerar el nodo del dispositivo rhproxy.

devcon status *msft8000

La salida de devcon debe indicar que el dispositivo está presente. Si el nodo del dispositivo no está presente, las tablas ACPI no se agregaron correctamente al sistema.

Compruebe que rhproxy está cargando e iniciando

Compruebe el estado de rhproxy:

devcon status *msft8000

Si la salida indica que rhproxy se inicia, rhproxy se ha cargado y iniciado correctamente. Si ve un código de problema, debe investigar. Algunos códigos de problema comunes son:

  • Problema 51: CM_PROB_WAITING_ON_DEPENDENCY el sistema no está iniciando rhproxy porque una de sus dependencias no se pudo cargar. Esto significa que los recursos pasados al punto rhproxy a nodos ACPI no válidos o los dispositivos de destino no se inician. En primer lugar, compruebe que todos los dispositivos se ejecutan correctamente (vea "Comprobar controladores de controlador" anteriormente). A continuación, compruebe su ASL y asegúrese de que todas las rutas de acceso de recursos (por ejemplo, \_SB.I2C1) son correctas y apuntan a nodos válidos en el DSDT.
  • Problema 10: CM_PROB_FAILED_START Rhproxy no se pudo iniciar, lo más probable es que se deba a un problema de análisis de recursos. Revise los índices de recursos de ASL y compruebe dos veces los índices de recursos en DSD y compruebe que los recursos GPIO se especifican en el orden creciente del número de patillas.

Compruebe que los dispositivos esperados están expuestos al modo de usuario

Ahora que rhproxy se está ejecutando, debe haber creado interfaces de dispositivos a las que puede acceder el modo de usuario. Usaremos varias herramientas de línea de comandos para enumerar los dispositivos y ver que están presentes.

Clone el https://github.com/ms-iot/samples repositorio y compile los GpioTestToolejemplos, I2cTestTool, SpiTestTooly Mincomm. Copie las herramientas en el dispositivo en prueba y use los siguientes comandos para enumerar los dispositivos.

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

Debería ver los dispositivos y los nombres descriptivos enumerados. Si no ve los dispositivos correctos y los nombres descriptivos, compruebe su ASL.

Comprobación de cada dispositivo en la línea de comandos

El siguiente paso consiste en usar las herramientas de línea de comandos para abrir e interactuar con los dispositivos.

Ejemplo de I2CTestTool:

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

Ejemplo de SpiTestTool:

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

Ejemplo de GpioTestTool:

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

Ejemplo de MinComm (serie). Conecte Rx a Tx antes de ejecutar:

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

Comprobar cada dispositivo desde una aplicación para UWP

Use los ejemplos siguientes para validar que los dispositivos funcionan desde UWP.

Ejecución de las pruebas de HLK

Descargue el Kit de laboratorio de hardware (HLK). Las siguientes pruebas están disponibles:

Al seleccionar el nodo del dispositivo rhproxy en el administrador de HLK, las pruebas aplicables se seleccionarán automáticamente.

En el administrador de HLK, seleccione "Dispositivo proxy del centro de recursos":

Captura de pantalla del Kit de laboratorio de hardware de Windows que muestra la pestaña Selección con la opción dispositivo proxy del centro de recursos seleccionada.

A continuación, haga clic en la pestaña Pruebas y seleccione Pruebas I2C WinRT, Gpio WinRT y Spi WinRT.

Captura de pantalla del Kit de laboratorio de hardware de Windows que muestra la pestaña Pruebas con la opción G P I O Win R T Functional and Stress Tests (Pruebas funcionales y de esfuerzo de G P I O Win R T) seleccionada.

Haga clic en Ejecutar seleccionado. Para obtener más documentación sobre cada prueba, haga clic con el botón derecho en la prueba y haga clic en “Descripción de la prueba.”

Recursos

Apéndice

Apéndice A: Lista de ASL de Raspberry Pi

Vea también Raspberry Pi 2 y las asignaciones de pines 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: Lista de ASL de MinnowBoardMax

Vea también Asignaciones de patillas máximas de 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: script de PowerShell de ejemplo para generar recursos GPIO

El siguiente script se puede usar para generar las declaraciones 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;
}