Conectando um driver KMDF a pinos de E/S gpio
Um recurso de E/S de GPIO é um conjunto de um ou mais pinos GPIO configurados como entradas de dados ou saídas de dados. O driver de um dispositivo periférico que se conecta fisicamente a esses pinos adquire o recurso de E/S de GPIO correspondente do sistema operacional. O driver de dispositivo periférico abre uma conexão com os pinos gpio nesse recurso e envia solicitações de E/S para o identificador que representa essa conexão.
O exemplo de código a seguir mostra como o driver KMDF (estrutura de driver do modo kernel) para um dispositivo periférico pode obter uma descrição do recurso de E/S gpio que o gerenciador de Plug and Play (PnP) atribuiu ao driver.
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;
...
}
No exemplo de código anterior, a DeviceExtension
variável é um ponteiro para o contexto do dispositivo para o dispositivo periférico. A XyzDrvGetDeviceExtension
função , que recupera esse contexto de dispositivo, é implementada pelo driver de dispositivo periférico. Esse driver registrou anteriormente sua função de retorno de chamada EvtDevicePrepareHardware chamando o método WdfDeviceInitSetPnpPowerEventCallbacks .
O exemplo de código a seguir mostra como o driver de dispositivo periférico pode usar a descrição do recurso GPIO obtida no exemplo de código anterior para abrir um identificador WDFIOTARGET para o recurso de E/S gpio do driver.
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;
}
...
No exemplo de código anterior, a Device
variável é um identificador WDFDEVICE para o objeto de dispositivo de estrutura do dispositivo periférico. A função RESOURCE_HUB_CREATE_PATH_FROM_ID cria uma cadeia de caracteres que contém o nome do recurso de E/S gpio. O exemplo de código usa essa cadeia de caracteres para abrir o recurso de E/S gpio por nome.
Depois que o driver de dispositivo periférico tiver obtido um identificador para um recurso de E/S de GPIO, esse driver poderá enviar solicitações de controle de E/S para ler dados ou gravar dados nos pinos gpio. Um driver que abre um recurso de E/S de GPIO para leituras usa IOCTL_GPIO_READ_PINS solicitações de controle de E/S para ler dados dos pinos no recurso. Um driver que abre um recurso de E/S de GPIO para gravações usa IOCTL_GPIO_WRITE_PINS solicitações de controle de E/S para gravar dados nos pinos no recurso. O exemplo de código a seguir mostra como executar uma operação de leitura ou gravação do 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);
}
...
No exemplo de código anterior, Data
é um ponteiro para um buffer de dados, Size
é o tamanho, em bytes, desse buffer de dados e ReadOperation
indica se a operação solicitada é uma leitura (TRUE) ou uma gravação (FALSE).
Para obter mais informações
Para obter mais informações sobre solicitações de IOCTL_GPIO_READ_PINS , incluindo o mapeamento de pinos de entrada de dados para os bits no buffer de saída da solicitação, consulte IOCTL_GPIO_READ_PINS. Para obter mais informações sobre IOCTL_GPIO_WRITE_PINS solicitações, incluindo o mapeamento dos bits no buffer de entrada de solicitação para pinos de saída de dados, consulte IOCTL_GPIO_WRITE_PINS.
Para obter um driver de exemplo que mostra como escrever um driver periférico GPIO executado no modo kernel, consulte o driver de exemplo SimDevice na coleção de drivers de exemplo GPIO no GitHub.