Recursos de hardware para drivers periféricos do User-Mode SPB

Os exemplos de código neste tópico mostram como o driver UMDF ( User-Mode Driver Framework ) para um dispositivo periférico em um SPB ( barramento periférico simples ) 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 gpio. (Um pino gpio é um pino em um dispositivo de controlador de E/S de uso geral configurado como uma entrada ou uma saída; para obter mais informações, consulte Drivers do controlador GPIO.) Ao contrário de um dispositivo mapeado na memória, um dispositivo periférico conectado a SPB não exige um bloco de endereços de memória do sistema para o qual mapear seus registros.

Esse driver implementa uma interface IPnpCallbackHardware2 e registra essa interface com o UMDF durante a chamada para o método IDriverEntry::OnDeviceAdd do driver. A estrutura chama os métodos na interface IPnpCallbackHardware2 para notificar o driver de alterações no estado de energia do dispositivo.

Quando a energia é restaurada para o dispositivo periférico conectado ao SPB, a estrutura do driver chama o método IPnpCallbackHardware2::OnPrepareHardware para notificar o driver de que esse dispositivo deve ser preparado para uso. Durante essa chamada, o driver recebe duas listas de recursos de hardware como parâmetros de entrada. O parâmetro pWdfResourcesRaw aponta para a lista de recursos brutos e o parâmetro pWdfResourcesTranslated aponta para a lista de recursos traduzidos. Ambos os parâmetros são ponteiros para objetos IWDFCmResourceList . Os recursos traduzidos incluem a ID de conexão que o driver periférico do SPB precisa para estabelecer uma conexão lógica com o dispositivo periférico conectado ao SPB. Para obter mais informações, consulte IDs de conexão para dispositivos periféricos SPB.

Para permitir que um driver periférico UMDF receba IDs de conexão em sua lista de recursos, o arquivo INF que instala o driver deve incluir a seguinte diretiva em sua seção DDInstall específica do WDF :

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Para obter mais informações sobre essa diretiva, consulte Especificando diretivas WDF em arquivos INF.

O exemplo de código a seguir mostra como o método OnPrepareHardware do driver obtém a ID de conexão do parâmetro pWdfResourcesTranslated .

BOOLEAN fConnectIdFound = FALSE;
BOOLEAN fDuplicateFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;

resourceCount = pWdfResourcesTranslated->GetCount();

// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = pWdfResourcesTranslated->GetDescriptor(ix);

    if (pDescriptor == NULL)
    {
        hr = 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 (fConnIdFound == FALSE)
                    {
                        // Save the SPB connection ID.
                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectIdFound = TRUE;
                    }
                    else
                    {
                        fDuplicateFound = TRUE;
                    }
                }
            }

            if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
            {
                // Check for GPIO pin resource.
                ...
            }
        }
        break;

    case CmResourceTypeInterrupt:
        {
            // Check for interrupt resources.
            ...
        }
        break;

    default:
        // Ignore all other resource descriptors.
        break;
    }
}

O exemplo de código anterior copia a ID de conexão de um dispositivo periférico conectado a SPB em uma variável chamada connectionId. O exemplo de código a seguir mostra como incorporar a ID de conexão em um nome de caminho de dispositivo que pode ser usado para identificar o dispositivo periférico.

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
// TODO: Replace this hardcoded string with the appropriate
//       helper method from Reshub.h when available.
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(szTargetPath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

O exemplo de código anterior grava o nome do caminho para o dispositivo periférico conectado ao SPB na szTargetPath matriz. O exemplo de código a seguir usa esse nome de caminho de dispositivo para abrir um identificador de arquivo para o dispositivo periférico conectado ao SPB.

UMDF_IO_TARGET_OPEN_PARAMS openParams;

openParams.dwShareMode = 0;
openParams.dwCreationDisposition = OPEN_EXISTING;
openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
hres = pRemoteTarget->OpenFileByName(&szTargetPath[0],
                                     (GENERIC_READ | GENERIC_WRITE),
                                     &openParams);
if (FAILED(hres))
{
    // Error handling
    ...
}

No exemplo de código anterior, a pRemoteTarget variável é um ponteiro para um objeto IWDFRemoteTarget . Se a chamada para o método IWDFRemoteTarget::OpenFileByName for bem-sucedida, o driver para o dispositivo periférico conectado ao SPB poderá usar o objeto IWDFRemoteTarget para enviar solicitações de E/S para o dispositivo periférico. Antes que o driver envie uma solicitação de leitura, gravação ou IOCTL para o dispositivo periférico, o driver chama o método IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite ou IWDFRemoteTarget::FormatRequestForIoctl para formatar a solicitação de E/S. A interface IWDFRemoteTarget herda esses três métodos da interface IWDFIoTarget . Em seguida, o driver chama o método IWDFIoRequest::Send para enviar a solicitação de E/S para o dispositivo periférico conectado ao SPB.

No exemplo de código a seguir, o driver periférico do SPB chama o método Send para enviar uma solicitação IRP_MJ_WRITE para o dispositivo periférico conectado ao SPB.

HRESULT hres;
IWDFMemory *pInputMemory = NULL;
IWDFRemoteTarget *pWdfIoRequest = NULL;

// Create a new I/O request.
if (SUCCEEDED(hres))
{
    hres = pWdfDevice->CreateRequest(NULL, 
                                     pWdfDevice, 
                                     &pWdfIoRequest);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Allocate memory for the input buffer.
if (SUCCEEDED(hres))
{
    hres = pWdfDriver->CreatePreallocatedWdfMemory(pInBuffer, 
                                                   inBufferSize, 
                                                   NULL,
                                                   pWdfIoRequest,
                                                   &pInputMemory);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }

    // After this call, the parent holds the only reference.
    pWdfMemory->Release();
}

// Format the I/O request for a write operation.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForWrite(pWdfIoRequest,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Send the request to the SPB controller.
if (SUCCEEDED(hres))
{
    ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;

    // Set the I/O completion callback.
    if (!fSynchronous)
    {
        pWdfIoRequest->SetCompletionCallback(pCallback, NULL);
    }

    hres = pWdfIoRequest->Send(pRemoteTarget, Flags, 0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

if (fSynchronous || FAILED(hres))
{
    pWdfIoRequest->DeleteWdfObject();
    SAFE_RELEASE(pWdfIoRequest);
}

O exemplo de código anterior faz o seguinte:

  1. A pWdfDevice variável é um ponteiro para a interface IWDFDevice do objeto de dispositivo de estrutura que representa o dispositivo periférico conectado ao SPB. O método IWDFDevice::CreateRequest cria uma solicitação de E/S e encapsula essa solicitação na instância da interface IWDFIoRequest que é apontada pela pWdfIoRequest variável .
  2. A pWdfDriver variável é um ponteiro para a interface IWDFDriver do objeto de driver de estrutura que representa o driver periférico do SPB. As pInBuffer variáveis e inBufferSize especificam o endereço e o tamanho do buffer de entrada que contém os dados da solicitação de gravação. O método IWDFDriver::CreatePreallocatedWdfMemory cria um objeto de memória de estrutura para o buffer de entrada e designa o objeto IWDFIoRequest apontado por pWdfIoRequest como o objeto pai do objeto de memória (para que o objeto de memória seja liberado automaticamente quando seu pai for liberado). Depois que o driver chama o método Release para liberar sua referência local ao objeto de memória, o pai mantém a única referência a esse objeto.
  3. A pWdfRemoteTarget variável é o ponteiro de destino remoto obtido da chamada OpenFileByName em um exemplo de código anterior. O método IWDFRemoteTarget::FormatRequestForWrite formata a solicitação de E/S para uma operação de gravação.
  4. A fSynchronous variável será TRUE se a solicitação de gravação for enviada de forma síncrona e for FALSE se for enviada de forma assíncrona. A pCallback variável é um ponteiro para uma interface IRequestCallbackRequestCompletion criada anteriormente. Se a solicitação for enviada de forma assíncrona, a chamada para o método IWDFIoRequest::SetCompletionCallback registrará essa interface. Posteriormente, o método IRequestCallbackRequestCompletion::OnCompletion é chamado para notificar o driver quando a solicitação for concluída de forma assíncrona.
  5. O método Send envia a solicitação de gravação formatada para o dispositivo periférico conectado ao SPB. A Flags variável indica se a solicitação de gravação deve ser enviada de forma síncrona ou assíncrona.
  6. Se a solicitação for enviada de forma síncrona, o método IWDFIoRequest::D eleteWdfObject excluirá o objeto de solicitação de E/S apontado por pWdfIoRequest e o objeto filho apontado por pInputMemory. A interface IWDFIoRequest herda esse método da interface IWDFObject . Se a solicitação for enviada de forma assíncrona, a chamada para o método DeleteWdfObject deverá ocorrer posteriormente, no método OnCompletion do driver.

Uma implementação alternativa do exemplo de código anterior pode criar objetos IWDFIoRequest e IWDFMemory durante a inicialização do driver e usar repetidamente esses mesmos objetos em vez de criar e excluir novos objetos sempre que uma solicitação de E/S for enviada. Para obter mais informações, consulte IWDFIoRequest2::Reuse e IWDFMemory::SetBuffer.

Além disso, uma implementação alternativa pode inspecionar o código de status de E/S da solicitação de E/S se a chamada Enviar for bem-sucedida. Para obter mais informações, consulte IWDFIoRequest::GetCompletionParams.