Подключение драйвера KMDF к контактам ввода-вывода GPIO
Ресурс ввода-вывода GPIO — это набор из одного или нескольких контактов GPIO, настроенных в качестве входных или выходных данных. Драйвер для периферийного устройства, которое физически подключается к этим контактам, получает соответствующий ресурс ввода-вывода GPIO из операционной системы. Драйвер периферийного устройства открывает подключение к контактам GPIO в этом ресурсе и отправляет запросы ввода-вывода дескриптору, представляющему это подключение.
В следующем примере кода показано, как драйвер платформы драйвера в режиме ядра (KMDF) для периферийного устройства может получить описание ресурса ввода-вывода GPIO, который диспетчер Plug and Play (PnP) назначил драйверу.
NTSTATUS
EvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesRaw,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
int ResourceCount, Index;
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
XYZ_DEVICE_CONTEXT *DeviceExtension;
...
DeviceExtension = XyzDrvGetDeviceExtension(Device);
ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
for (Index = 0; Index < ResourceCount; Index += 1) {
Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);
switch (Descriptor->Type) {
//
// GPIO I/O descriptors
//
case CmResourceTypeConnection:
//
// Check against expected connection type.
//
if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&
(Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) {
DeviceExtension->ConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
DeviceExtension->ConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
...
}
В предыдущем примере DeviceExtension
кода переменная является указателем на контекст устройства для периферийного устройства. Функция XyzDrvGetDeviceExtension
, которая извлекает этот контекст устройства, реализуется драйвером периферийного устройства. Этот драйвер ранее зарегистрировал функцию обратного вызова EvtDevicePrepareHardware , вызвав метод WdfDeviceInitSetPnpPowerEventCallbacks .
В следующем примере кода показано, как драйвер периферийного устройства может использовать описание ресурса GPIO, полученное в предыдущем примере кода, чтобы открыть дескриптор WDFIOTARGET для ресурса ввода-вывода GPIO драйвера.
NTSTATUS IoRoutine(WDFDEVICE Device, BOOLEAN ReadOperation)
{
WDFIOTARGET IoTarget;
XYZ_DEVICE_CONTEXT *DeviceExtension;
UNICODE_STRING ReadString;
WCHAR ReadStringBuffer[100];;
BOOL DesiredAccess;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES ObjectAttributes;
WDF_IO_TARGET_OPEN_PARAMS OpenParams
DeviceExtension = XyzDrvGetDeviceExtension(Device);
RtlInitEmptyUnicodeString(&ReadString,
ReadStringBuffer,
sizeof(ReadStringBuffer));
Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString,
DeviceExtension->ConnectionId.LowPart,
DeviceExtension->ConnectionId.HighPart);
NT_ASSERT(NT_SUCCESS(Status));
WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);
ObjectAttributes.ParentObject = Device;
Status = WdfIoTargetCreate(Device, &ObjectAttributes, &IoTarget);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
if (ReadOperation != FALSE) {
DesiredAccess = GENERIC_READ;
} else {
DesiredAccess = GENERIC_WRITE;
}
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, DesiredAccess);
Status = WdfIoTargetOpen(IoTarget, &OpenParams);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
...
В предыдущем примере кода переменная представляет собой дескриптор Device
WDFDEVICE для объекта устройства платформы для периферийного устройства. Функция RESOURCE_HUB_CREATE_PATH_FROM_ID создает строку, содержащую имя ресурса ввода-вывода GPIO. В примере кода эта строка используется для открытия ресурса ввода-вывода GPIO по имени.
После того как драйвер периферийного устройства получил дескриптор для ресурса ввода-вывода GPIO, этот драйвер может отправлять запросы управления вводом-выводом для чтения данных или записи данных в контакты GPIO. Драйвер, открывающий ресурс ввода-вывода GPIO для операций чтения, использует IOCTL_GPIO_READ_PINS запросов управления вводом-выводом для чтения данных из контактов в ресурсе. Драйвер, открывающий ресурс ввода-вывода GPIO для операций записи, использует IOCTL_GPIO_WRITE_PINS запросов управления вводом-выводом для записи данных в контакты в ресурсе. В следующем примере кода показано, как выполнить операцию чтения или записи GPIO.
WDF_OBJECT_ATTRIBUTES RequestAttributes;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_REQUEST_SEND_OPTIONS SendOptions;
WDFREQUEST IoctlRequest;
WDFIOTARGET IoTarget;
WDFMEMORY WdfMemory;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes);
Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Set up a WDF memory object for the IOCTL request.
//
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
Attributes.ParentObject = IoctlRequest;
Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Format the request.
//
if (ReadOperation != FALSE) {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_READ_PINS,
NULL,
0,
WdfMemory,
0);
} else {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_WRITE_PINS,
WdfMemory,
0,
WdfMemory,
0);
}
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Send the request synchronously (with a 60-second time-out).
//
WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions,
WDF_REL_TIMEOUT_IN_SEC(60));
Status = WdfRequestAllocateTimer(IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) {
Status = WdfRequestGetStatus(IoctlRequest);
}
...
В приведенном выше примере Data
кода — это указатель на буфер данных, Size
размер этого буфера данных в байтах и ReadOperation
указывает, является ли запрошенная операция операцией чтения (TRUE) или записи (FALSE).
Дополнительные сведения
Дополнительные сведения о запросах IOCTL_GPIO_READ_PINS , включая сопоставление контактов ввода данных с битами в буфере вывода запросов, см. в разделе IOCTL_GPIO_READ_PINS. Дополнительные сведения о запросах IOCTL_GPIO_WRITE_PINS , включая сопоставление битов во входном буфере запросов с контактами вывода данных, см. в разделе IOCTL_GPIO_WRITE_PINS.
Пример драйвера, в котором показано, как написать драйвер периферийного устройства GPIO, работающий в режиме ядра, см. в примере драйвера SimDevice в коллекции примеров драйверов GPIO на сайте GitHub.