Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os exemplos de código neste tópico mostram como o Driver FrameworkKernel-Mode (KMDF) para um dispositivo periférico em barramento periférico simples (SPB) obtém os recursos de hardware necessários para operar o dispositivo. Incluídas nesses recursos estão as informações que o driver usa para estabelecer uma conexão lógica com o dispositivo. Recursos adicionais podem incluir uma interrupção e um ou mais pinos de entrada ou saída do GPIO. (Um pino GPIO é um pino em um controlador de E/S de uso geral, configurado como entrada ou saída; para obter mais informações, consulte General-Purpose Drivers de E/S (GPIO).) Ao contrário de um dispositivo mapeado pela memória, um dispositivo periférico conectado ao SPB não requer um bloco de endereços de memória do sistema para o qual mapear seus registros.
Esse driver implementa um conjunto de funções de retorno de chamada de evento, plug and play, e de gerenciamento de energia. Para registrar essas funções no KMDF, a função de callback de evento EvtDriverDeviceAdd do driver invoca o método WdfDeviceInitSetPnpPowerEventCallbacks. A estrutura chama as funções de retorno de chamada de evento de gerenciamento de energia para notificar o driver sobre alterações no estado de energia do dispositivo periférico. Incluída nessas funções está a função EvtDevicePrepareHardware , que executa todas as operações necessárias para tornar o dispositivo acessível ao driver.
Quando a energia é restaurada para o dispositivo periférico, a estrutura de driver chama a função EvtDevicePrepareHardware para notificar o driver periférico do SPB de que esse dispositivo deve estar preparado para uso. Durante essa chamada, o driver recebe duas listas de recursos de hardware como parâmetros de entrada. O parâmetro ResourcesRaw é um identificador de objeto WDFCMRESLIST para a lista de recursos brutos e o parâmetro ResourcesTranslated é um identificador de objeto WDFCMRESLIST para a lista de recursos traduzidos. Os recursos traduzidos incluem a ID de conexão necessária para que o driver estabeleça uma conexão lógica com o dispositivo periférico. Para obter mais informações, consulte IDs de conexão para dispositivos periféricos SPB-Connected.
O exemplo de código a seguir mostra como a função EvtDevicePrepareHardware obtém a ID de conexão do parâmetro ResourcesTranslated .
BOOLEAN fConnectionIdFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;
NTSTATUS status = STATUS_SUCCESS;
resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
pDescriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, ix);
if (pDescriptor == NULL)
{
status = E_POINTER;
break;
}
// Determine the resource type.
switch (pDescriptor->Type)
{
case CmResourceTypeConnection:
{
// Check against the expected connection types.
UCHAR Class = pDescriptor->u.Connection.Class;
UCHAR Type = pDescriptor->u.Connection.Type;
if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL)
{
if (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)
{
if (fConnectionIdFound == FALSE)
{
// Save the SPB connection ID.
connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
fConnectionIdFound = TRUE;
}
}
}
if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
{
// Check for GPIO pin resource.
...
}
}
break;
case CmResourceTypeInterrupt:
{
// Check for interrupt resource.
...
}
break;
default:
// Don't care about other resource descriptors.
break;
}
}
O exemplo de código anterior copia a ID de conexão de um dispositivo periférico conectado ao SPB em uma variável chamada connectionId.
O exemplo de código a seguir mostra como incorporar essa ID de conexão em um nome de caminho de dispositivo que pode ser usado para abrir uma conexão lógica com o dispositivo periférico. Esse nome de caminho do dispositivo identifica o hub de recursos como o componente do sistema do qual obter os parâmetros necessários para acessar o dispositivo periférico.
// Use the connection ID to create the full device path name.
DECLARE_UNICODE_STRING_SIZE(szDeviceName, RESOURCE_HUB_PATH_SIZE);
status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&szDeviceName,
connectionId.LowPart,
connectionId.HighPart);
if (!NT_SUCCESS(status))
{
// Error handling
...
}
No exemplo de código anterior, a macro DECLARE_UNICODE_STRING_SIZE cria a declaração de uma variável de UNICODE_STRING inicializada chamada szDeviceName que tem um buffer grande o suficiente para conter um nome de caminho do dispositivo no formato usado pelo hub de recursos. Essa macro é definida no arquivo de cabeçalho Ntdef.h. A constante RESOURCE_HUB_PATH_SIZE especifica o número de bytes no nome do caminho do dispositivo. A macro RESOURCE_HUB_CREATE_PATH_FROM_ID gera o nome do caminho do dispositivo a partir da ID da conexão.
RESOURCE_HUB_PATH_SIZE e RESOURCE_HUB_CREATE_PATH_FROM_ID são definidos no arquivo de cabeçalho Reshub.h.
O exemplo de código a seguir usa o nome do caminho do dispositivo para abrir um identificador de arquivo (nomeado SpbIoTarget) para o dispositivo periférico conectado ao SPB.
// Open the SPB peripheral device as a remote I/O target.
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&openParams,
&szDeviceName,
(GENERIC_READ | GENERIC_WRITE));
openParams.ShareAccess = 0;
openParams.CreateDisposition = FILE_OPEN;
openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;
status = WdfIoTargetOpen(SpbIoTarget, &openParams);
if (!NT_SUCCESS(status))
{
// Error handling
...
}
No exemplo de código anterior, a função WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME inicializa a estrutura WDF_IO_TARGET_OPEN_PARAMS para que o driver possa abrir uma conexão lógica com o dispositivo periférico especificando o nome do dispositivo. A variável SpbIoTarget é um identificador WDFIOTARGET para um objeto de destino de E/S do framework. Esse identificador foi obtido de uma chamada anterior para o método WdfIoTargetCreate , que não é mostrado no exemplo. Se a chamada para o método WdfIoTargetOpen for bem-sucedida, o driver poderá usar o identificador SpbIoTarget para enviar solicitações de E/S para o dispositivo periférico.
Na função de retorno de chamada de evento EvtDriverDeviceAdd, o driver periférico SPB pode chamar o método WdfRequestCreate para alocar um objeto de solicitação de framework para uso pelo driver. Posteriormente, quando o objeto não for mais necessário, o driver chamará o método WdfObjectDelete para excluir o objeto. O driver pode reutilizar o objeto de solicitação de estrutura obtido da chamada WdfRequestCreate várias vezes para enviar solicitações de E/S para o dispositivo periférico. Para uma solicitação de leitura, gravação ou IOCTL, o driver chama o método WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously ou WdfIoTargetSendIoctlSynchronously para enviar a solicitação.
No exemplo de código a seguir, o driver chama WdfIoTargetSendWriteSynchronously para enviar de forma síncrona uma solicitação IRP_MJ_WRITE para o dispositivo periférico conectado ao SPB. No início deste exemplo, a pBuffer variável aponta para um buffer nãopagado que contém os dados que devem ser gravados no dispositivo periférico e a dataSize variável especifica o tamanho, em bytes, desses dados.
ULONG_PTR bytesWritten;
NTSTATUS status;
// Describe the input buffer.
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, pBuffer, dataSize);
// Configure the write request to time out after 2 seconds.
WDF_REQUEST_SEND_OPTIONS requestOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
requestOptions.Timeout = WDF_REL_TIMEOUT_IN_SEC(2);
// Send the write request synchronously.
status = WdfIoTargetSendWriteSynchronously(SpbIoTarget,
SpbRequest,
&memoryDescriptor,
NULL,
&requestOptions,
&bytesWritten);
if (!NT_SUCCESS(status))
{
// Error handling
...
}
O exemplo de código anterior faz o seguinte:
- A chamada da função WDF_MEMORY_DESCRIPTOR_INIT_BUFFER inicializa a
memoryDescriptorvariável, que é uma estrutura WDF_MEMORY_DESCRIPTOR que descreve o buffer de entrada. Anteriormente, o driver chamava uma rotina como ExAllocatePoolWithTag para alocar o buffer do pool nãopagado e copiava os dados de gravação para esse buffer. - A chamada da função WDF_REQUEST_SEND_OPTIONS_INIT inicializa a
requestOptionsvariável, que é uma estrutura WDF_REQUEST_SEND_OPTIONS que contém as configurações opcionais para a solicitação de gravação. Neste exemplo, a configuração define que a solicitação expire se não for concluída após dois segundos. - A chamada para o método WdfIoTargetSendWriteSynchronously envia a solicitação de gravação para o dispositivo periférico conectado ao SPB. O método retorna de forma síncrona, após a operação de gravação ser concluída ou atingir o tempo limite. Se necessário, outro thread de driver pode chamar WdfRequestCancelSentRequest para cancelar a solicitação.
Na chamada WdfIoTargetSendWriteSynchronously , o driver fornece uma variável chamada SpbRequest, que é um identificador para um objeto de solicitação de estrutura que o driver criou anteriormente. Após a chamada WdfIoTargetSendWriteSynchronously , o driver normalmente deve chamar o método WdfRequestReuse para preparar o objeto de solicitação de estrutura a ser usado novamente.