Поделиться через


Включение доступа в режиме пользователя к GPIO, I2C и SPI

В Windows 10 и более поздних версиях API предоставляют прямой доступ из пользовательского режима к входным и выходным данным общего назначения (GPIO), каналу Inter-Integrated (I2C), последовательному периферийному интерфейсу (SPI) и универсальному асинхронному приемнику-приемнику (UART). Доски разработки, такие как Raspberry Pi 2, предоставляют подмножество этих подключений, что позволяет расширить базовый вычислительный модуль с пользовательской схемой для удовлетворения требований конкретного приложения. Эти низкоуровневые шины обычно используются совместно с другими критически важными встроенными функциями, с только подмножеством контактов GPIO и шин, предоставляемых на разъемах. Чтобы сохранить стабильность системы, необходимо указать, какие контакты и шины безопасны для изменений в приложениях пользовательского режима.

В этом документе описывается, как указать эту конфигурацию в advanced Configuration and Power Interface (ACPI) и предоставить средства для проверки правильности указания конфигурации.

Это важно

Аудитория этого документа — это единый расширяемый интерфейс встроенного ПО (UEFI) и разработчики ACPI. Предполагается наличие некоторого знакомства с ACPI, написанием на языке исходного кода ACPI (ASL) и SpbCx/GpioClx.

Доступ в пользовательском режиме к шинам низкого уровня в Windows осуществляется через существующие фреймворки GpioClx и SpbCx. Новый драйвер с именем RhProxy, доступный в Windows IoT Core и в Windows Enterprise, предоставляет ресурсы GpioClx и SpbCx для пользовательского режима. Чтобы включить API, узел устройства для rhproxy должен быть объявлен в таблицах ACPI с каждым из ресурсов GPIO и SPB, которые должны быть доступны в пользовательском режиме. В этом документе рассматривается создание и проверка ASL.

ASL на примерах

Давайте перейдем к процессу объявления узла устройства rhproxy на Raspberry Pi 2. Сначала в области \_SB создайте объявление устройства ACPI.

Device(RHPX)
{
    Name(_HID, "MSFT8000")
    Name(_CID, "MSFT8000")
    Name(_UID, 1)
}
  • _HID — идентификатор оборудования. Задайте для этого идентификатор оборудования для конкретного поставщика.
  • _CID — совместимый идентификатор. Должен быть "MSFT8000".
  • _UID — уникальный идентификатор. Задайте значение 1.

Затем мы объявляем каждый из ресурсов GPIO и SPB, которые должны предоставляться в пользовательском режиме. Порядок объявления ресурсов важен, так как индексы ресурсов используются для связывания свойств с ресурсами. Если доступно несколько шин I2C или SPI, первый объявленный автобус считается шиной по умолчанию для этого типа и будет экземпляром, возвращенным GetDefaultAsync() методами Windows.Devices.I2c.I2cController и Windows.Devices.SpiController.

SPI

Raspberry Pi имеет две доступные шины SPI. SPI0 имеет две аппаратные линии выбора микросхем, и SPI1 имеет одну аппаратную линию выбора микросхемы. Для каждой линии выбора микросхемы для каждой шины требуется одно объявление ресурса SPISerialBus(). Следующие два объявления ресурсов SPISerialBus предназначены для двух линий выбора микросхем в SPI0. Поле DeviceSelection содержит уникальное значение, которое драйвер интерпретирует как идентификатор линии аппаратного чипа. Точное значение, которое вы помещаете в поле DeviceSelection, зависит от того, как драйвер интерпретирует это поле дескриптора подключения ACPI.

Замечание

В этой статье содержатся ссылки на термин "раб" — термин, который корпорация Майкрософт не поддерживает и перестала использовать в новых продуктах и документации. Когда этот термин будет удален из программного обеспечения, мы удалим его из статьи.

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

Как программное обеспечение знает, что эти два ресурса должны быть связаны с одной шиной? Сопоставление удобного имени шины и индекса ресурсов указывается в DSD.

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

При этом создается шина с именем SPI0 с двумя линиями выбора микросхем — индексами ресурсов 0 и 1. Для объявления возможностей шины SPI требуется несколько дополнительных свойств.

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

Свойства MinClockInHz и MaxClockInHz указывают минимальные и максимальные скорости часов, поддерживаемые контроллером. API не позволит пользователям указывать значения за пределами этого диапазона. Тактовая частота передается драйверу SPB в поле _SPE дескриптора подключения (раздел ACPI 6.4.3.8.2.2).

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

Свойство SupportedDataBitLengths содержит длину бита данных, поддерживаемую контроллером. Несколько значений можно указать в разделенном запятыми списке. API не позволит пользователям указывать значения за пределами этого списка. Длина бита данных передается драйверу SPB в поле _LEN дескриптора подключения (раздел ACPI 6.4.3.8.2.2).

Эти объявления ресурсов можно рассматривать как "шаблоны". Некоторые поля фиксируются при загрузке системы, а другие динамически задаются во время выполнения. Исправлены следующие поля дескриптора SPISerialBus:

  • Выбор устройства
  • ПолярностьВыбораУстройства
  • WireMode
  • SlaveMode
  • Источник ресурсов

Следующие поля являются заполнителями для значений, указанных пользователем во время выполнения:

  • ДлинаБитовДанных
  • Скорость подключения
  • Полярность тактирующего сигнала
  • ClockPhase

Так как SPI1 содержит только одну линию выбора микросхемы, объявляется один ресурс 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

Соответствующее объявление дружественного имени, которое является обязательным, указывается в DSD и ссылается на индекс этого объявления ресурса.

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

При этом создается шина с именем SPI1 и связывает ее с индексом ресурсов 2.

Требования к драйверу SPI

  • Должен использовать SpbCx или быть совместимым с SpbCx
  • Необходимо успешно сдать тесты MITT SPI
  • Должна поддерживать частоту 4 МГц
  • Должна поддерживать 8-разрядную длину данных
  • Должен поддерживать все режимы SPI: 0, 1, 2, 3

I2C

Затем мы объявляем ресурсы I2C. Raspberry Pi предоставляет одну шину I2C на выводах 3 и 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

Соответствующее объявление дружественного имени, обязательно указывается в DSD:

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

Это объявляет шину I2C с понятным именем “I2C1”, которая относится к индексу ресурса 3, что является индексом ресурса I2CSerialBus(), объявленного выше.

Установлены следующие поля дескриптора I2CSerialBus():

  • SlaveMode
  • Источник ресурсов

Следующие поля являются заполнителями для значений, указанных пользователем во время выполнения.

  • SlaveAddress
  • Скорость подключения
  • Режим адресации

Требования к драйверу I2C

  • Должен использовать SpbCx или быть совместимым с SpbCx
  • Должны быть успешно пройдены тесты MITT I2C
  • Должна поддерживать 7-разрядную адресацию
  • Должна поддерживать скорость 100 кГц
  • Должна поддерживать скорость 400kГц

GPIO

Затем мы объявляем все пин-коды GPIO, которые предоставляются в пользовательском режиме. Мы предлагаем следующие рекомендации по выбору того, какие закрепления следует предоставить:

  • Объявите все закрепления на предоставленных заголовках.
  • Объявите пины, подключенные к полезным встроенным функциям, таким как кнопки и светодиоды.
  • Не объявляйте пин-коды, зарезервированные для системных функций или не подключенные к чему-либо.

Следующий блок ASL объявляет два контакта — GPIO4 и GPIO5. Другие закрепления здесь не отображаются для краткости. Приложение C содержит пример скрипта PowerShell, который можно использовать для создания ресурсов 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 }

При объявлении закреплений GPIO необходимо соблюдать следующие требования:

  • Поддерживаются только контроллеры GPIO, сопоставленные с памятью. Контроллеры GPIO, интерфейсированные через I2C/SPI, не поддерживаются. Драйвер контроллера является контроллером, сопоставленным с памятью, если он задает флаг MemoryMappedController в структуре CLIENT_CONTROLLER_BASIC_INFORMATION в ответ на обратный вызов CLIENT_QueryControllerBasicInformation.
  • Для каждого пин-кода требуется как ресурс GpioIOIO, так и ресурс GpioInt. Ресурс GpioInt должен немедленно следовать ресурсу GpioIOIO и должен ссылаться на тот же номер пин-кода.
  • Ресурсы GPIO следует упорядочить в порядке возрастания номера пина.
  • Каждый ресурс GpioIO и GpioInt должен содержать ровно один номер вывода в списке выводов.
  • Поле ShareType обоих дескрипторов должно быть общим
  • Поле EdgeLevel дескриптора GpioInt должно быть Edge.
  • Поле ActiveLevel дескриптора GpioInt должно иметь значение ActiveBoth
  • Поле PinConfig
    • Должен быть одинаковым в дескрипторах GpioIO и GpioInt
    • Должен быть одним из PullUp, PullDown или PullNone. Это не может быть PullDefault.
    • Конфигурация подтяжки должна соответствовать включённому состоянию контакта. Установка пина в указанный режим подтяжки при включении питания не должна изменять состояние этого пина. Например, если таблица указывает, что пин-код поставляется с вытягиванием, укажите PinConfig как PullUp.

Код инициализации встроенного ПО, UEFI и драйверов не должен изменять состояние пина из его начального состояния во время загрузки. Только пользователь знает, что подключено к контакту, и поэтому какие переходы состояния являются безопасными. Состояние каждого контакта при включении должно быть задокументировано, чтобы пользователи могли разрабатывать оборудование, которое правильно взаимодействует с контактом. Пин не должен неожиданно изменять состояние во время загрузки.

Поддерживаемые режимы дисков

Если контроллер GPIO поддерживает встроенные подтягивающие и спускающие резисторы, помимо входа с высоким сопротивлением и CMOS-выхода, необходимо указать это с помощью необязательного параметра SupportedDriveModes.

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

Свойство SupportedDriveModes указывает, какие режимы дисков поддерживаются контроллером GPIO. В приведенном выше примере поддерживаются все следующие режимы дисков. Свойство представляет собой битовую маску следующих значений:

Значение флага Режим диска Описание
0x1 Входное высокоимпедансное состояние Пин-код поддерживает входные данные с высоким импедансом, соответствующий значению PullNone в ACPI.
0x2 Входной подтягивающий резистор Пин оснащён встроенным подтягивающим резистором, соответствующим значению PullUp в ACPI.
0x4 Входной режим понижения Вывод поддерживает встроенный подтягивающий резистор вниз, который соответствует значению "PullDown" в ACPI.
0x8 OutputCmos Закрепление поддерживает создание сильных максимумов и сильных минимумов (в отличие от открытого слива).

InputHighImpedance и OutputCmos поддерживаются почти всеми контроллерами GPIO. Если свойство SupportedDriveModes не указано, это значение по умолчанию.

Если сигнал GPIO проходит через преобразователь уровня перед достижением открытого заголовка, объявите режимы управления, поддерживаемые SOC, даже если режим управления не будет виден на внешнем разъеме. Например, если пин проходит через двунаправленный сдвиг уровня, который делает пин открытым коллектором с подтягивающим резистором, вы никогда не будете наблюдать высокоимпедансное состояние на внешнем разъеме, даже если пин настроен как вход с высоким импедансом. Вы по-прежнему должны объявить, что пин-код поддерживает входные данные с высокими импеансами.

Закрепление нумерирования

Windows поддерживает две схемы нумерирования контактов:

  • Последовательное нумерование выводов — пользователи видят такие числа, как 0, 1, 2... вплоть до количества открытых выводов. 0 является первым ресурсом GpioIo, объявленным в ASL, 1 является вторым ресурсом GpioIo, объявленным в ASL, и т. д.
  • Нативная нумерация выводов — пользователи видят номера выводов, указанные в дескрипторах GpioIo, например 4, 5, 12, 13, ...
Package (2) { “GPIO-UseDescriptorPinNumbers”, 1 },

Свойство UseDescriptorPinNumbers указывает Windows использовать собственное нумерование выводов вместо последовательного нумерования выводов. Если свойство UseDescriptorPinNumbers не указано или его значение равно нулю, Windows по умолчанию будет использовать последовательное нумерирование пин-кодов.

Если используется исходная нумерация контактов, необходимо также указать свойство PinCount.

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

Свойство PinCount должно соответствовать значению, возвращаемому через свойство TotalPins в обратном вызове драйвера CLIENT_QueryControllerBasicInformation, GpioClx.

Выберите схему нумерирования, которая наиболее совместима с существующей опубликованной документацией для вашей платы. К примеру, Raspberry Pi использует собственную нумерацию контактов, так как многие существующие схемы распиновки используют номера контактов BCM2835. MinnowBoardMax использует последовательное нумерование пинов, так как существует немного схем распиновки, а последовательное нумерование упрощает работу разработчика, так как из более чем 200 контактов доступно только 10. Решение об использовании последовательной или собственной нумерации контактов должно стремиться к снижению путаницы у разработчиков.

Требования к драйверу GPIO

  • Необходимо использовать GpioClx
  • Должен быть сопоставлен с памятью on-SOC
  • Должен использовать эмулированную обработку прерываний ActiveBoth

УАРТ (универсальный асинхронный приемопередатчик)

Если драйвер UART использует SerCx или SerCx2, можно использовать rhproxy для предоставления драйвера пользовательскому режиму. Драйверам UART, создающим интерфейс устройства типа GUID_DEVINTERFACE_COMPORT, не нужно использовать rhproxy. Драйвер папки "Входящие" Serial.sys является одним из этих случаев.

Чтобы сделать доступным SerCx-стиль UART в режиме пользователя, объявите ресурс UARTSerialBus следующим образом.

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

Только поле ResourceSource исправлено, а все остальные поля являются заполнителями для значений, указанных во время выполнения пользователем.

Сопровождающее объявление понятного имени:

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

Это присваивает контроллеру дружественное имя UART2, которое будет использоваться пользователями для доступа к шине из пользовательского режима.

Мультиплексирование пин-кода среды выполнения

Мультиплексирование контактов — это возможность использовать один и тот же физический вывод для разных функций. Несколько различных периферийных устройств на микросхеме, таких как контроллер I2C, контроллер SPI и контроллер GPIO, могут быть перенаправлены на один и тот же физический пин-код на SOC. Блок мультиплексора управляет, какая функция активна на контакте в любой момент времени. Традиционно встроенное ПО отвечает за установку назначений функций при загрузке, и это назначение остается статическим через сеанс загрузки. Мультиплексирование выводов в среде выполнения добавляет возможность перенастройки функций выводов во время выполнения. Для того чтобы пользователи могли выбирать функцию пина во время выполнения, тем самым ускоряя разработку, им предоставляется возможность быстро перенастраивать пины платы, что позволяет оборудованию поддерживать более широкий спектр приложений, чем это возможно при статической конфигурации.

Пользователи используют поддержку GPIO, I2C, SPI и UART без необходимости писать дополнительный код. Когда пользователь открывает GPIO или шину с помощью OpenPin() или FromIdAsync(), базовые физические пин-коды автоматически сопоставляются с запрошенной функцией. Если пин-коды уже используются другой функцией, вызов OpenPin() или FromIdAsync() завершится сбоем. Когда пользователь закрывает устройство, удаляя объект GpioPin, I2cDevice, SpiDeviceили SerialDevice, контакты освобождаются, чтобы их можно было позже открыть для другой функции.

Windows содержит встроенную поддержку мультиплексирования контактов в рамках GpioClx , SpbCx и SerCx . Эти платформы работают вместе, чтобы автоматически переключать пин-код на правильную функцию при доступе к пин-коду или шине GPIO. Доступ к пин-кодам является арбитражем, чтобы предотвратить конфликты между несколькими клиентами. Помимо этой встроенной поддержки, интерфейсы и протоколы для мультиплексирования выводов предназначены для общего использования и могут быть расширены для поддержки дополнительных устройств и сценариев.

В этом документе сначала описываются базовые интерфейсы и протоколы, связанные с мультиплексированием пин-адресов, а затем описывается, как добавить поддержку мультиплексирования пин-адресов в драйверы контроллеров GpioClx, SpbCx и SerCx.

Архитектура мультиплексирования пинов

В этом разделе описываются базовые интерфейсы и протоколы, связанные с мультиплексированием пин-кода. Знание базовых протоколов не обязательно для поддержки мультиплексирования выводов с драйверами GpioClx/SpbCx/SerCx. Дополнительные сведения о поддержке мультиплексирования пинов с драйверами GpioCls/SpbCx/SerCx смотрите в статье Реализация поддержки мультиплексирования пинов в драйверах клиентов GpioClx и Использование мультиплексирования в драйверах контроллеров SpbCx и SerCx.

Мультиплексирование пинов осуществляется взаимодействием нескольких компонентов.

  • Драйверы серверов мультиплексирования пинов — это драйверы, управляющие блоком управления мультиплексированием. Серверы мультиплексирования пинов получают запросы на мультиплексирование пинов от клиентов через запросы на резервирование ресурсов мультиплексирования (через IRP_MJ_CREATE) и запросы на переключение функций пинов (через *IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS - запросы). Сервер мультиплексирования пинов обычно является драйвером GPIO, поскольку блок мультиплексирования иногда является частью блока GPIO. Даже если блок мьюксирования является отдельным периферийным устройством, драйвер GPIO является логическим местом для размещения функций мьюзинга.
  • Клиенты мультиплексирования пин-кода — это драйверы, использующие мультиплексирование пин-кода. Клиенты мультиплексирования пин-кода получают ресурсы мультиплексирования пин-кода из встроенного ПО ACPI. Ресурсы мультиплексирования выводов — это тип ресурса подключения и управляются узлом управления ресурсами. Клиенты, использующие мультиплексирование контактов, резервируют ресурсы, открывая дескриптор к ресурсу. Чтобы внести изменения в конфигурацию оборудования, клиенты обязаны зафиксировать конфигурацию, отправив запрос IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Клиенты освобождают ресурсы мультиплексирования пинов, закрывая дескриптор, при этом конфигурация пинов приводится к состоянию по умолчанию.
  • Встроенное ПО ACPI — задает конфигурацию мультиплексирования с ресурсами MsftFunctionConfig(). Ресурсы MsftFunctionConfig выражают, какие контакты и в какой конфигурации мультиплексирования требуются клиенту. Ресурсы MsftFunctionConfig содержат номер функции, конфигурацию извлечения и список чисел пин-кода. Ресурсы MsftFunctionConfig предоставляются клиентам с мультиплексированием выводов в качестве аппаратных ресурсов, которые драйверы получают в своем обратном вызове PrepareHardware, аналогично ресурсам подключения GPIO и SPB. Клиенты получают идентификатор ресурсного узла, который можно использовать для получения доступа к ресурсу.

Необходимо передать параметр командной строки /MsftInternal в asl.exe, чтобы скомпилировать файлы ASL, содержащие дескрипторы MsftFunctionConfig(), так как эти дескрипторы в настоящее время рассматриваются рабочим комитетом ACPI. Например: asl.exe /MsftInternal dsdt.asl

Ниже показана последовательность операций, связанных с мультиплексированием контактов.

взаимодействие с клиентским сервером с использованием мультиплексирования пин-кода

  1. Клиент получает ресурсы MsftFunctionConfig из прошивки ACPI в функции EvtDevicePrepareHardware() в обратном вызове.
  2. Клиент использует вспомогательные функции центра ресурсов RESOURCE_HUB_CREATE_PATH_FROM_ID() для создания пути из идентификатора ресурса, а затем открывает дескриптор пути (с помощью ZwCreateFile(), IoGetDeviceObjectPointer()или WdfIoTargetOpen()).
  3. Сервер извлекает идентификатор концентратора ресурсов из пути к файлу с помощью вспомогательных функций концентратора ресурсов RESOURCE_HUB_ID_FROM_FILE_NAME(), а затем запрашивает концентратор ресурсов, чтобы получить дескриптор ресурса.
  4. Сервер выполняет арбитраж совместного использования для каждого пина в дескрипторе и завершает запрос IRP_MJ_CREATE.
  5. Клиент выдает запрос IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS на полученный дескриптор.
  6. В ответ на IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINSсервер выполняет операцию аппаратного мультиплексирования, активируя указанную функцию на каждом выводе.
  7. Клиент выполняет операции, зависящие от запрошенной конфигурации мультиплексирования пин-кода.
  8. Если клиенту больше не требуется мультиплексирование выводов, он закрывает дескриптор.
  9. В ответ на закрываемый дескриптор сервер возвращает пин-код обратно в исходное состояние.

Описание протокола для клиентов с мультиплексированием пин-кода

В этом разделе описывается, как клиент использует функции мультиплексирования контактов. Это не относится к драйверам контроллера SerCx и SpbCx, так как платформы реализуют этот протокол от имени драйверов контроллера.

Анализ ресурсов

Драйвер WDF получает MsftFunctionConfig() ресурсы в функции EvtDevicePrepareHardware(). Ресурсы MsftFunctionConfig можно определить следующими полями:

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

Подпрограмма EvtDevicePrepareHardware() может извлекать ресурсы MsftFunctionConfig следующим образом:

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

Резервирование и закрепление ресурсов

Когда клиент хочет мультиплексировать контакты, он резервирует и фиксирует ресурс MsftFunctionConfig. В следующем примере показано, как клиент может зарезервировать и зафиксировать ресурсы 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;
}

Драйвер должен хранить WDFIOTARGET в одной из его областей контекста, чтобы его можно было закрыть позже. Когда драйвер готов к выпуску конфигурации мультиплексирования, он должен закрыть дескриптор ресурсов, вызвав WdfObjectDelete()или WdfIoTargetClose(), если вы планируете повторно использовать WDFIOTARGET.

    WdfObjectDelete(resourceHandle);

Когда клиент закрывает дескриптор ресурса, пины мультиплексируются обратно в исходное состояние и теперь могут быть захвачены другим клиентом.

Описание протокола для серверов мультиплексирования пин-кодов

В этом разделе описывается, как сервер мультиплексирования пин-кода предоставляет свои функциональные возможности клиентам. Это не относится к GpioClx драйверам минипорта, так как платформа реализует этот протокол от имени клиентских драйверов. Дополнительные сведения о поддержке мультиплексирования контактов в клиентских драйверах GpioClx см. в разделе Реализация поддержки мультиплексирования в клиентских драйверах GpioClx.

Обработка запросов IRP_MJ_CREATE

Клиенты открывают дескриптор для ресурса, чтобы зарезервировать ресурс для мультиплексирования пинов. Сервер мультиплексирования пин-кода получает запросы IRP_MJ_CREATE путем операции повторного анализа из концентратора ресурсов. Конечный компонент пути запроса IRP_MJ_CREATE содержит идентификатор концентратора ресурсов, который является 64-разрядным целым числом в шестнадцатеричном формате. Сервер должен извлечь идентификатор концентратора ресурсов из имени файла с помощью RESOURCE_HUB_ID_FROM_FILE_NAME() из reshub.h и отправить IOCTL_RH_QUERY_CONNECTION_PROPERTIES в концентратор ресурсов, чтобы получить дескриптор MsftFunctionConfig().

Сервер должен проверить дескриптор и извлечь режим общего доступа и список закреплений из дескриптора. Затем он должен выполнять обмен арбитражем для пин-кодов, и в случае успеха помечайте пин-коды как зарезервированные перед завершением запроса.

Арбитраж совместного использования завершается успешно, если для каждого пина в списке пинов арбитраж выполнен успешно. Каждый пин-код должен быть арбитражирован следующим образом:

  • Если пин-код еще не зарезервирован, обмен арбитражом успешно выполнен.
  • Если пин-код уже зарезервирован как эксклюзивный, обмен арбитража завершается ошибкой.
  • Если пин-код уже зарезервирован как общий,
    • и входящий запрос распределяется, процесс арбитража проходит успешно.
    • и входящий запрос является эксклюзивным, процесс арбитража при разделении завершается сбоем.

Если арбитраж общего доступа завершится сбоем, запрос следует завершить с STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE. Если арбитраж совместного использования успешно выполнен, запрос должен быть завершён с STATUS_SUCCESS.

Обратите внимание, что режим общего доступа для входящего запроса должен быть взят из дескриптора MsftFunctionConfig, а не IrpSp->Parameters.Create.ShareAccess.

Обработка запросов IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS

После того как клиент успешно зарезервировал ресурс MsftFunctionConfig, открыв дескриптор, он может отправить IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, чтобы запросить сервер выполнить фактическую операцию мультиплексирования оборудования. Когда сервер получает IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, для каждого пина в списке пинов он должен

  • Задайте режим подтяжки, указанный в параметре PinConfiguration структуры PNP_FUNCTION_CONFIG_DESCRIPTOR, в аппаратном обеспечении.
  • Настройте пин на функцию, указанную членом FunctionNumber структуры PNP_FUNCTION_CONFIG_DESCRIPTOR.

Затем сервер должен завершить запрос с STATUS_SUCCESS.

Значение FunctionNumber определяется сервером, и понятно, что дескриптор MsftFunctionConfig был создан с знанием того, как сервер интерпретирует это поле.

Помните, что при закрытии дескриптора сервер должен будет вернуть пин-коды в конфигурацию, в которую они находились при получении IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, поэтому серверу может потребоваться сохранить состояние пин-кода перед их изменением.

Обработка запросов IRP_MJ_CLOSE

Если клиент больше не требует ресурса мультиплексирования, он закрывает его дескриптор. Когда сервер получает запрос IRP_MJ_CLOSE, он должен вернуть пин-коды в состояние, в которое они находились при получении IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS. Если клиент никогда не отправлял IOCTL_GPIO_COMMIT_FUNCTION_CONFIG_PINS, никаких действий не требуется. Затем сервер должен пометить пин-коды как доступные для обмена арбитражем, и завершить запрос с помощью STATUS_SUCCESS. Не забудьте правильно синхронизировать обработку IRP_MJ_CLOSE с обработкой IRP_MJ_CREATE.

Рекомендации по разработке таблиц ACPI

В этом разделе описывается, как предоставлять ресурсы для мьюксирования клиентским драйверам. Обратите внимание, что для компиляции таблиц, содержащих MsftFunctionConfig() ресурсов, потребуется сборка компилятора Microsoft ASL 14327 или более поздней версии. MsftFunctionConfig() ресурсы предоставляются клиентам мультиплексирования в виде аппаратных ресурсов. MsftFunctionConfig() ресурсы должны предоставляться драйверам, которым требуется изменение мультиплексирования пинов, которые обычно являются драйверами SPB и последовательных контроллеров, но не должны предоставляться драйверам SPB и драйверам последовательных периферийных устройств, поскольку драйвер контроллера обрабатывает конфигурацию мультиплексирования. Макрос MsftFunctionConfig() ACPI определяется следующим образом:

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

  • Общий или эксклюзивный — если это монопольное, этот пин-код может быть приобретен одним клиентом одновременно. При совместном доступе несколько общих клиентов могут получить ресурс. Всегда устанавливайте это в эксклюзивный режим, поскольку разрешение нескольким несогласованным клиентам получать доступ к изменяемому ресурсу может привести к гонкам данных и, следовательно, непредсказуемым результатам.
  • PinPullConfig — один из
    • PullDefault — используйте конфигурацию подтяжки по умолчанию при включении питания, определяемую SOC.
    • PullUp — включение резистора подтягивания
    • PullDown — включение резистора вниз
    • PullNone — отключение всех подтягивающих резисторов
  • FunctionNumber — номер функции для программирования в мьюкс.
  • ResourceSource — путь к пространству имен ACPI сервера мультиплексирования пин-кода
  • ResourceSourceIndex — задайте для этого значение 0
  • ResourceConsumer/ResourceProducer — задайте для этого значение ResourceConsumer
  • VendorData — двоичные данные, которые являются необязательными и значение которых определяется сервером мультиплексирования выводов. Обычно это должно оставаться пустым
  • Список контактов — разделенный запятыми список чисел пин-кода, к которым применяется конфигурация. Если сервер мультиплексирования пин-кода является драйвером GpioClx, это номера пин-кода GPIO и имеют то же значение, что и цифры пин-кода в дескрипторе GpioIo.

В следующем примере показано, как можно предоставить ресурс MsftFunctionConfig() драйверу контроллера 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)
    }
}

Помимо ресурсов памяти и прерывания, которые обычно требуются драйвером контроллера, также указывается ресурс MsftFunctionConfig(). Этот ресурс позволяет драйверу контроллера I2C перевести выводы 2 и 3, управляемые узлом устройства \_SB.GPIO0, в режим функции 4 с включенным резистором подтягивания.

Поддержка мультиплексирования в клиентских драйверах GpioClx

GpioClx имеет встроенную поддержку мультиплексирования пинов. Драйверы минипорта GpioClx (также называемые драйверами клиентов GpioClx) управляют оборудованием контроллера GPIO. В Windows 10 версии сборки 14327 драйверы минипорта GpioClx могут добавить поддержку мультиплексирования выводов путем реализации двух новых интерфейсов программирования драйвера (DDIs):

  • CLIENT_ConnectFunctionConfigPins — вызывается GpioClx, чтобы указать минипорт-драйверу применить указанную конфигурацию мультиплексирования.
  • CLIENT_DisconnectFunctionConfigPins — вызывается GpioClx, чтобы командовать минипорт-драйверу, чтобы вернуть конфигурацию мьюксирования.

Сведения об этих подпрограммах см. в функциях обратного вызова событий GpioClx .

Помимо этих двух новых DDIs существующие DDIs должны быть проверены для обеспечения совместимости мультиплексирования пин-кодов:

  • CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt — CLIENT_ConnectIoPins вызывается GpioClx, чтобы дать команду мини-драйверу для настройки набора контактов GPIO для ввода или вывода. GPIO является взаимоисключающим с MsftFunctionConfig, то есть пин-код никогда не будет подключен для GPIO и MsftFunctionConfig одновременно. Так как по умолчанию контакт не обязан выполнять функцию GPIO, он может не быть мультиплексирован в режим GPIO при вызове ConnectIoPins. ConnectIoPins требуется для выполнения всех операций, необходимых для подготовки пин-кода для операций ввода-вывода GPIO, включая операции мультиплексирования. CLIENT_ConnectInterrupt должны вести себя аналогичным образом, так как прерывания можно рассматривать как особый случай ввода GPIO.
  • CLIENT_DisconnectIoPins/CLIENT_DisconnectInterrupt. Эти подпрограммы должны возвращать пин-коды в состояние, в которое они находились при вызове CLIENT_ConnectIoPins/CLIENT_ConnectInterrupt, если не указан флаг PreserveConfiguration. Помимо возврата направления пинов в состояние по умолчанию, минипорт должен также вернуть состояние мультиплексирования каждого пина в то состояние, в котором он находился при вызове подпрограммы _Connect.

Например, предположим, что по умолчанию конфигурация мультиплексирования контакта — UART, а контакт также можно использовать в качестве GPIO. При вызове CLIENT_ConnectIoPins для подключения пина к GPIO, его нужно переключить на GPIO, а при CLIENT_DisconnectIoPins следует переключить пин назад на UART. Как правило, подпрограммы отключения должны отменять операции, выполняемые подпрограммами Connect.

Поддержка мультиплексирования в драйверах контроллера SpbCx и SerCx

По состоянию на сборку Windows 10 14327 платформы SpbCx и SerCx содержат встроенную поддержку мультиплексирования выводов, которая позволяет драйверам контроллера SpbCx и SerCx стать клиентами мультиплексирования без изменения кода самих драйверов контроллера. В результате любой периферийный драйвер SpbCx/SerCx, который подключается к контроллерному драйверу SpbCx/SerCx с поддержкой мультиплексирования, вызовет активность мультиплексирования пинов.

На следующей схеме показаны зависимости между каждым из этих компонентов. Как видно, мультиплексирование пинов создаёт зависимость драйверов контроллеров SerCx и SpbCx по отношению к драйверу GPIO, который обычно отвечает за это мультиплексирование.

зависимость мультиплексирования выводов для

Во время инициализации устройства платформы SpbCx и SerCx анализируют все ресурсы MsftFunctionConfig(), предоставляемые в качестве аппаратных ресурсов для устройства. SpbCx/SerCx затем приобретает и освобождает ресурсы мультиплексирования пин-кода по требованию.

SpbCx применяет конфигурацию мультиплексирования выводов в обработчике IRP_MJ_CREATE непосредственно перед вызовом обратного вызова EvtSpbTargetConnect() драйвера клиента. Если не удалось применить конфигурацию мультиплексирования, обратный вызов EvtSpbTargetConnect() драйвера контроллера не будет вызван. Таким образом, драйвер контроллера SPB может предположить, что к моменту вызова EvtSpbTargetConnect() контакты переключены для функции SPB.

SpbCx отменяет конфигурацию мультиплексирования контактов в обработчике IRP_MJ_CLOSE сразу после вызова драйвера контроллера EvtSpbTargetDisconnect() обратного вызова. В результате выводы мультиплексируются с функцией SPB всякий раз, когда периферийный драйвер открывает дескриптор драйвера контроллера SPB, и демультиплексируются, когда периферийный драйвер закрывает свой дескриптор.

SerCx ведет себя аналогично. SerCx получает все ресурсы MsftFunctionConfig() в обработчике IRP_MJ_CREATE перед вызовом обратного вызова EvtSerCx2FileOpen() драйвера контроллера и освобождает все ресурсы в обработчике IRP_MJ_CLOSE, сразу после вызова обратного вызова EvtSerCx2FileClose драйвера контроллера.

Последствия динамического мультиплексирования контактов для драйверов контроллеров SerCx и SpbCx заключается в том, что они должны быть способны допускать использование контактов вне функции SPB/UART в определенные моменты. Драйверам контроллера необходимо предполагать, что пины не будут мультиплексированы до вызова EvtSpbTargetConnect() или EvtSerCx2FileOpen(). Пин-коды не необходимы для функции SPB/UART во время следующих обратных вызовов. Ниже приведен не полный список, но представляет наиболее распространенные подпрограммы PNP, реализованные драйверами контроллеров.

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

Проверка

Когда вы будете готовы протестировать rhproxy, полезно использовать следующую пошаговую процедуру.

  1. Убедитесь, что каждый драйвер контроллера SpbCx, GpioClxи SerCx загружается и работает корректно.
  2. Убедитесь, что в системе присутствует rhproxy. Некоторые выпуски и сборки Windows не имеют его.
  3. Компиляция и загрузка узла rhproxy с помощью ACPITABL.dat
  4. Убедитесь, что узел устройства rhproxy существует
  5. Убедитесь, что rhproxy загружается и запускается
  6. Убедитесь, что ожидаемые устройства переданы в пользовательский режим
  7. Убедитесь, что вы можете взаимодействовать с каждым устройством из командной строки.
  8. Убедитесь, что вы можете взаимодействовать с каждым устройством из приложения UWP
  9. Выполнение тестов HLK

Проверка драйверов контроллера

Так как rhproxy предоставляет другие устройства в системе в пользовательском режиме, он работает только в том случае, если эти устройства уже работают. Первым шагом является проверка того, что эти устройства — контроллеры I2C, SPI, GPIO, которые вы хотите предоставить, уже работают.

В командной строке выполните команду

devcon status *

Просмотрите выходные данные и убедитесь, что запущены все интересующие устройства. Если у устройства есть код проблемы, необходимо устранить неполадки, почему это устройство не загружается. Все устройства должны были быть включены во время первоначальной настройки платформы. Устранение неполадок SpbCx, GpioClxили драйверов контроллеров SerCx выходит за рамки этого документа.

Убедитесь, что rhproxy присутствует в системе

Убедитесь, что служба rhproxy присутствует в системе.

reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\rhproxy

Если ключ reg отсутствует, rhproxy не существует в вашей системе. Rhproxy присутствует во всех сборках IoT Core и Windows Enterprise сборки 15063 и более поздних версий.

Компиляция и загрузка ASL с помощью ACPITABL.dat

Теперь, когда вы создали узел ASL rhproxy, пришло время скомпилировать и загрузить его. Узел rhproxy можно скомпилировать в автономный AML-файл, который можно добавить в системные таблицы ACPI. Кроме того, если у вас есть доступ к источникам ACPI системы, можно вставить узел rhproxy непосредственно в таблицы ACPI платформы. Однако во время начальной настройки может быть проще использовать ACPITABL.dat.

  1. Создайте файл с именем yourboard.asl и поместите узел устройства RHPX в DefinitionBlock:

    DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
    {
        Scope (\_SB)
        {
            Device(RHPX)
            {
            ...
            }
        }
    }
    
  2. Скачайте WDK и найдите asl.exe на C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify

  3. Выполните следующую команду, чтобы создать ACPITABL.dat:

    asl.exe yourboard.asl
    
  4. Скопируйте полученный файл ACPITABL.dat в файл c:\windows\system32 в системе под тестом.

  5. Включите функцию testsigning в тестируемой системе:

    bcdedit /set testsigning on
    
  6. Перезагрузите систему под тестированием. Система добавит таблицы ACPI, определенные в ACPITABL.dat в системные таблицы встроенного ПО.

Убедитесь, что узел устройства rhproxy существует

Выполните следующую команду, чтобы перечислить узел устройства rhproxy.

devcon status *msft8000

Выходные данные devcon должны указывать на наличие устройства. Если узел устройства отсутствует, таблицы ACPI не были успешно добавлены в систему.

Убедитесь, что rhproxy загружает и запускается

Проверьте состояние rhproxy:

devcon status *msft8000

Если выходные данные указывают на запуск rhproxy, rhproxy загружен и успешно запущен. Если вы видите код проблемы, необходимо разобраться. Ниже приведены некоторые распространенные коды проблем:

  • Проблема 51 - CM_PROB_WAITING_ON_DEPENDENCY Система не запускает rhproxy, так как одна из ее зависимостей не была загружена. Это означает, что ресурсы, переданные в rhproxy, указывают на недопустимые узлы ACPI или целевые устройства не запускаются. Сначала дважды проверьте успешность работы всех устройств (см. раздел "Проверка драйверов контроллера" выше). Затем дважды проверьте ASL и убедитесь, что все пути к ресурсам (например, \_SB.I2C1) правильны и указывают на допустимые узлы в DSDT.
  • Проблема 10 - CM_PROB_FAILED_START - Rhproxy не удалось запустить, скорее всего, из-за проблемы синтаксического анализа ресурсов. Проверьте ваш ASL и дважды проверьте индексы ресурсов в DSD, убедитесь, что ресурсы GPIO указаны в порядке возрастания номеров выводов.

Убедитесь, что ожидаемые устройства переданы в пользовательский режим

Теперь, когда выполняется rhproxy, он должен создать интерфейсы устройств, к которым можно получить доступ в пользовательском режиме. Мы используем несколько инструментов командной строки для перечисления устройств и проверки их присутствия.

Клонируйте репозиторий https://github.com/ms-iot/samples и создайте GpioTestTool, I2cTestTool, SpiTestToolи Mincomm примеры. Скопируйте средства на тестируемое устройство и используйте следующие команды для перечисления устройств.

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

Вы должны увидеть ваши устройства и дружественные имена. Если вы не видите нужные устройства и понятные имена, дважды проверьте ASL.

Проверка каждого устройства в командной строке

Следующим шагом является использование средств командной строки для открытия и взаимодействия с устройствами.

Пример I2CTestTool:

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

Пример SpiTestTool:

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

Пример GpioTestTool:

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

Пример MinComm (serial). Подключите Rx к Tx перед выполнением:

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

Проверка каждого устройства из приложения UWP

Используйте следующие примеры, чтобы проверить, работают ли устройства из UWP.

Запуск тестов HLK

Скачайте комплект аппаратных лабораторных средств (HLK). Доступны следующие тесты:

При выборе узла устройства rhproxy в диспетчере HLK будут автоматически выбраны применимые тесты.

В диспетчере HLK выберите "Устройство прокси-сервера Концентратора ресурсов":

Скриншот комплекта лаборатории оборудования Windows, на котором показана вкладка «Выбор» с выбранным параметром прокси-устройства Центра ресурсов.

Затем откройте вкладку "Тесты" и выберите тесты I2C WinRT, Gpio WinRT и Spi WinRT.

снимок экрана Windows Hardware Lab Kit, на вкладке

Нажмите "Выполнить выбранное". Дополнительная документация по каждому тесту доступна правой кнопкой мыши, щелкнув тест и щелкнув "Описание теста".

Ресурсы

Приложение

Приложение A — Список ASL для Raspberry Pi

См. также расположение контактов Raspberry Pi 2 и 3

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{

    Scope (\_SB)
    {
        //
        // RHProxy Device Node to enable WinRT API
        //
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE0  - GPIO 8  - Pin 24
                    0,                     // Device selection (CE0)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 1
                SPISerialBus(              // SCKL - GPIO 11 - Pin 23
                                           // MOSI - GPIO 10 - Pin 19
                                           // MISO - GPIO 9  - Pin 21
                                           // CE1  - GPIO 7  - Pin 26
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data

                // Index 2
                SPISerialBus(              // SCKL - GPIO 21 - Pin 40
                                           // MOSI - GPIO 20 - Pin 38
                                           // MISO - GPIO 19 - Pin 35
                                           // CE1  - GPIO 17 - Pin 11
                    1,                     // Device selection (CE1)
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    0,                     // databit len: placeholder
                    ControllerInitiated,   // slave mode
                    0,                     // connection speed: placeholder
                    ClockPolarityLow,      // clock polarity: placeholder
                    ClockPhaseFirst,       // clock phase: placeholder
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                                           // Resource usage
                    )                      // Vendor Data
                // Index 3
                I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
                    0xFFFF,                // SlaveAddress: placeholder
                    ,                      // SlaveMode: default to ControllerInitiated
                    0,                     // ConnectionSpeed: placeholder
                    ,                      // Addressing Mode: placeholder
                    "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
                    ,
                    ,
                    )                      // VendorData

                // Index 4 - GPIO 4 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 4 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 4 }
                // Index 6 - GPIO 5 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 5 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 5 }
                // Index 8 - GPIO 6 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 6 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 6 }
                // Index 10 - GPIO 12 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 12 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 12 }
                // Index 12 - GPIO 13 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 13 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 13 }
                // Index 14 - GPIO 16 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 16 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 16 }
                // Index 16 - GPIO 18 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 18 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 18 }
                // Index 18 - GPIO 22 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 22 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 22 }
                // Index 20 - GPIO 23 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 23 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 23 }
                // Index 22 - GPIO 24 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 24 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 24 }
                // Index 24 - GPIO 25 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 25 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 25 }
                // Index 26 - GPIO 26 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 26 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 26 }
                // Index 28 - GPIO 27 -
                GpioIO(Shared, PullDown, , , , "\\_SB.GPI0", , , , ) { 27 }
                GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",) { 27 }
                // Index 30 - GPIO 35 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 35 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 35 }
                // Index 32 - GPIO 47 -
                GpioIO(Shared, PullUp, , , , "\\_SB.GPI0", , , , ) { 47 }
                GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",) { 47 }
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // Reference http://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
                    // SPI 0
                    Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},                       // Index 0 & 1
                    Package(2) { "SPI0-MinClockInHz", 7629 },                               // 7629 Hz
                    Package(2) { "SPI0-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // SPI 1
                    Package(2) { "bus-SPI-SPI1", Package() { 2 }},                          // Index 2
                    Package(2) { "SPI1-MinClockInHz", 30518 },                              // 30518 Hz
                    Package(2) { "SPI1-MaxClockInHz", 125000000 },                          // 125 MHz
                    Package(2) { "SPI1-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
                    // I2C1
                    Package(2) { "bus-I2C-I2C1", Package() { 3 }},
                    // GPIO Pin Count and supported drive modes
                    Package (2) { "GPIO-PinCount", 54 },
                    Package (2) { "GPIO-UseDescriptorPinNumbers", 1 },
                    Package (2) { "GPIO-SupportedDriveModes", 0xf },                        // InputHighImpedance, InputPullUp, InputPullDown, OutputCmos
                }
            })
        }
    }
}

Приложение B. Список ASL для MinnowBoardMax

См. также сопоставления макс. пин-кода 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 }},
                }
            })
        }
    }
}

Приложение C. Пример скрипта PowerShell для создания ресурсов GPIO

Следующий скрипт можно использовать для создания деклараций ресурсов GPIO для 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;
}