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


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

В Windows 10 и более поздних версиях API предоставляются с прямым доступом из пользовательского режима к входным и выходным данным общего назначения (GPIO), Межинконференционным каналом (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. Сначала создайте объявление устройства ACPI в области \_SB.

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

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

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:

  • DeviceSelection
  • DeviceSelectionPolarity
  • WireMode
  • SlaveMode
  • ResourceSource

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

  • DataBitLength
  • ConnectionSpeed
  • ClockPolarity
  • 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
  • ResourceSource

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

  • SlaveAddress
  • ConnectionSpeed
  • АдресацияMode

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

  • Должен использовать SpbCx или быть совместимым с SpbCx
  • Должны пройти тесты MITT I2C
  • Должна поддерживать 7-разрядную адресацию
  • Должна поддерживать скорость 1000 Гц
  • Должна поддерживать скорость 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. В приведенном выше примере поддерживаются все следующие режимы дисков. Свойство представляет собой битовую маску следующих значений:

Значение флага Режим диска Description
0x1 InputHighImpedance Пин-код поддерживает входные данные с высоким импедансом, соответствующий значению PullNone в ACPI.
0x2 InputPullUp Пин-код поддерживает встроенный резистор подтягивания, соответствующий значению PullUp в ACPI.
0x4 InputPullDown Пин-код поддерживает встроенный резистор вниз, соответствующий значению 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 использует последовательное нумерирование пин-кода, так как существует несколько существующих схем закреплений, а последовательное нумерирование пин-кода упрощает работу разработчика, так как только 10 контактов предоставляются из более чем 200 контактов. Решение об использовании последовательного или собственного нумерирования закреплений должно стремиться к снижению путаницы разработчика.

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

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

UART

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

Чтобы предоставить UART в пользовательском режиме стиль SerCx, объявите 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 в одной из его областей контекста, чтобы его можно было закрыть позже. Когда драйвер готов к выпуску конфигурации mxing, он должен закрыть дескриптор ресурсов, вызвав 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 и SerCx платформы содержат встроенную поддержку мультиплексирования контактов, SpbCx которая позволяет SpbCx SerCx и драйверам контроллера быть закреплением клиентов с мультиплексированием без каких-либо изменений кода драйверов контроллера. По расширению любой периферийный драйвер SpbCx/SerCx, который подключается к драйверу контроллера SpbCx/SerCx, активирует действие мультиплексирования пин-адресов.

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

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

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

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

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

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

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

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

Проверка

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

  1. Убедитесь, что каждый SpbCxдрайвер контроллера GpioClxSerCx загружается и работает правильно.
  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 и создайте примеры, SpiTestToolа также Mincomm выполните сборку и I2cTestToolсборкуGpioTestTool. Скопируйте средства на тестируемое устройство и используйте следующие команды для перечисления устройств.

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, показывающий вкладку

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

Ресурсы

Приложение

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

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

DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "RHPROXY", 1)
{
    Scope (\_SB)
    {
        Device(RHPX)
        {
            Name(_HID, "MSFT8000")
            Name(_CID, "MSFT8000")
            Name(_UID, 1)

            Name(_CRS, ResourceTemplate()
            {
                // Index 0
                SPISerialBus(            // Pin 5, 7, 9 , 11 of JP1 for SIO_SPI
                    1,                     // Device selection
                    PolarityLow,           // Device selection polarity
                    FourWireMode,          // wiremode
                    8,                     // databit len
                    ControllerInitiated,   // slave mode
                    8000000,               // Connection speed
                    ClockPolarityLow,      // Clock polarity
                    ClockPhaseSecond,      // clock phase
                    "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
                    0,                     // ResourceSourceIndex
                    ResourceConsumer,      // Resource usage
                    JSPI,                  // DescriptorName: creates name for offset of resource descriptor
                    )                      // Vendor Data

                // Index 1
                I2CSerialBus(            // Pin 13, 15 of JP1, for SIO_I2C5 (signal)
                    0xFF,                  // SlaveAddress: bus address
                    ,                      // SlaveMode: default to ControllerInitiated
                    400000,                // ConnectionSpeed: in Hz
                    ,                      // Addressing Mode: default to 7 bit
                    "\\_SB.I2C6",          // ResourceSource: I2C bus controller name (For MinnowBoard Max, hardware I2C5(0-based) is reported as ACPI I2C6(1-based))
                    ,
                    ,
                    JI2C,                  // Descriptor Name: creates name for offset of resource descriptor
                    )                      // VendorData

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

                // Index 3
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {0}  // Pin 21 of JP1 (GPIO_S5[00])
                // Index 4
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {0}

                // Index 5
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {1}  // Pin 23 of JP1 (GPIO_S5[01])
                // Index 6
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {1}

                // Index 7
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO2",) {2}  // Pin 25 of JP1 (GPIO_S5[02])
                // Index 8
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO2",) {2}

                // Index 9
                UARTSerialBus(           // Pin 6, 8, 10, 12 of JP1, for SIO_UART1
                    115200,                // InitialBaudRate: in bits ber second
                    ,                      // BitsPerByte: default to 8 bits
                    ,                      // StopBits: Defaults to one bit
                    0xfc,                  // LinesInUse: 8 1-bit flags to declare line enabled
                    ,                      // IsBigEndian: default to LittleEndian
                    ,                      // Parity: Defaults to no parity
                    FlowControlHardware,   // FlowControl: Defaults to no flow control
                    32,                    // ReceiveBufferSize
                    32,                    // TransmitBufferSize
                    "\\_SB.URT1",          // ResourceSource: UART bus controller name
                    ,
                    ,
                    UAR1,              // DescriptorName: creates name for offset of resource descriptor
                    )

                // Index 10
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {62}  // Pin 14 of JP1 (GPIO_SC[62])
                // Index 11
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {62}

                // Index 12
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {63}  // Pin 16 of JP1 (GPIO_SC[63])
                // Index 13
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {63}

                // Index 14
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {65}  // Pin 18 of JP1 (GPIO_SC[65])
                // Index 15
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {65}

                // Index 16
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {64}  // Pin 20 of JP1 (GPIO_SC[64])
                // Index 17
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {64}

                // Index 18
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {94}  // Pin 22 of JP1 (GPIO_SC[94])
                // Index 19
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {94}

                // Index 20
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {95}  // Pin 24 of JP1 (GPIO_SC[95])
                // Index 21
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {95}

                // Index 22
                GpioIo (Shared, PullNone, 0, 0, IoRestrictionNone, "\\_SB.GPO0",) {54}  // Pin 26 of JP1 (GPIO_SC[54])
                // Index 23
                GpioInt(Edge, ActiveBoth, SharedAndWake, PullNone, 0,"\\_SB.GPO0",) {54}
            })

            Name(_DSD, Package()
            {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package()
                {
                    // SPI Mapping
                    Package(2) { "bus-SPI-SPI0", Package() { 0 }},

                    Package(2) { "SPI0-MinClockInHz", 100000 },
                    Package(2) { "SPI0-MaxClockInHz", 15000000 },
                    // SupportedDataBitLengths takes a list of support data bit length
                    // Example : Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8, 7, 16 }},
                    Package(2) { "SPI0-SupportedDataBitLengths", Package() { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }},
                     // I2C Mapping
                    Package(2) { "bus-I2C-I2C5", Package() { 1 }},
                    // UART Mapping
                    Package(2) { "bus-UART-UART2", Package() { 2 }},
                    Package(2) { "bus-UART-UART1", Package() { 9 }},
                }
            })
        }
    }
}

Приложение 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;
}