Compartir a través de


Recursos de hardware para controladores periféricos de SPB User-Mode

Los ejemplos de código de este tema muestran cómo el controlador del marco de controlador del modo de usuario (UMDF) de un dispositivo periférico en un bus periférico simple (SPB) obtiene los recursos de hardware que requiere para operar el dispositivo. Incluido en estos recursos es la información que el controlador usa para establecer una conexión lógica con el dispositivo. Los recursos adicionales pueden incluir una interrupción y una o varias patillas de entrada o salida de GPIO. (Una patilla GPIO es una patilla en un dispositivo de controlador de E/S de uso general que está configurado como entrada o salida; para obtener más información, vea Controladores de controlador GPIO). A diferencia de un dispositivo asignado a la memoria, un dispositivo periférico conectado a SPB no requiere un bloque de direcciones de memoria del sistema para asignar sus registros.

Este controlador implementa una interfaz IPnpCallbackHardware2 y registra esta interfaz con la UMDF durante la llamada al método IDriverEntry::OnDeviceAdd del controlador. El marco llama a los métodos de la interfaz IPnpCallbackHardware2 para notificar al controlador los cambios en el estado de alimentación del dispositivo.

Cuando la alimentación se restaura en el dispositivo periférico conectado a SPB, el marco del controlador llama al método IPnpCallbackHardware2::OnPrepareHardware para notificar al controlador que este dispositivo debe estar preparado para su uso. Durante esta llamada, el controlador recibe dos listas de recursos de hardware como parámetros de entrada. El parámetro pWdfResourcesRaw apunta a la lista de recursos sin procesar y el parámetro pWdfResourcesTranslated apunta a la lista de recursos traducidos. Ambos parámetros son punteros a objetos IWDFCmResourceList . Los recursos traducidos incluyen el identificador de conexión que el controlador periférico SPB necesita para establecer una conexión lógica con el dispositivo periférico conectado a SPB. Para obtener más información, consulte Identificadores de conexión para dispositivos periféricos SPB.

Para permitir que un controlador periférico umDF reciba identificadores de conexión en su lista de recursos, el archivo INF que instala el controlador debe incluir la siguiente directiva en la sección DDInstall específica de WDF:

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Para obtener más información sobre esta directiva, vea Especificar directivas WDF en archivos INF.

En el ejemplo de código siguiente se muestra cómo el método OnPrepareHardware del controlador obtiene el identificador de conexión del 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;
    }
}

En el ejemplo de código anterior se copia el identificador de conexión de un dispositivo periférico conectado a SPB en una variable denominada connectionId. En el ejemplo de código siguiente se muestra cómo incorporar el identificador de conexión en un nombre de ruta de acceso del dispositivo que se puede usar para identificar el 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
     ...
}

En el ejemplo de código anterior se escribe el nombre de ruta de acceso del dispositivo periférico conectado a SPB en la szTargetPath matriz. En el ejemplo de código siguiente se usa este nombre de ruta de acceso del dispositivo para abrir un identificador de archivo en el dispositivo periférico conectado a 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
    ...
}

En el ejemplo de código anterior, la pRemoteTarget variable es un puntero a un objeto IWDFRemoteTarget . Si la llamada al método IWDFRemoteTarget::OpenFileByName se realiza correctamente, el controlador del dispositivo periférico conectado a SPB puede usar el objeto IWDFRemoteTarget para enviar solicitudes de E/S al dispositivo periférico. Antes de que el controlador envíe una solicitud de lectura, escritura o IOCTL al dispositivo periférico, el controlador llama al método IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite o IWDFRemoteTarget::FormatRequestForIoctl para dar formato a la solicitud de E/S. La interfaz IWDFRemoteTarget hereda estos tres métodos de la interfaz IWDFIoTarget . A continuación, el controlador llama al método IWDFIoRequest::Send para enviar la solicitud de E/S al dispositivo periférico conectado a SPB.

En el ejemplo de código siguiente, el controlador periférico SPB llama al método Send para enviar una solicitud de IRP_MJ_WRITE al dispositivo periférico conectado a 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);
}

El ejemplo de código anterior hace lo siguiente:

  1. La pWdfDevice variable es un puntero a la interfaz IWDFDevice del objeto de dispositivo de marco que representa el dispositivo periférico conectado a SPB. El método IWDFDevice::CreateRequest crea una solicitud de E/S y encapsula esta solicitud en la instancia de interfaz IWDFIoRequest a la que apunta la pWdfIoRequest variable.
  2. La pWdfDriver variable es un puntero a la interfaz IWDFDriver del objeto de controlador de marco que representa el controlador periférico SPB. Las pInBuffer variables y inBufferSize especifican la dirección y el tamaño del búfer de entrada que contiene los datos de la solicitud de escritura. El método IWDFDriver::CreatePreallocatedWdfMemory crea un objeto de memoria de marco para el búfer de entrada y designa el objeto IWDFIoRequest al pWdfIoRequest que apunta como el objeto primario del objeto de memoria (de modo que el objeto de memoria se libere automáticamente cuando se libere su elemento primario). Una vez que el controlador llama al método Release para liberar su referencia local al objeto de memoria, el elemento primario contiene la única referencia a este objeto.
  3. La pWdfRemoteTarget variable es el puntero de destino remoto que se obtuvo de la llamada OpenFileByName en un ejemplo de código anterior. El método IWDFRemoteTarget::FormatRequestForWrite da formato a la solicitud de E/S para una operación de escritura.
  4. La fSynchronous variable es TRUE si la solicitud de escritura se va a enviar de forma sincrónica y es FALSE si se va a enviar de forma asincrónica. La pCallback variable es un puntero a una interfaz IRequestCallbackRequestCompletion creada anteriormente. Si la solicitud se va a enviar de forma asincrónica, la llamada al método IWDFIoRequest::SetCompletionCallback registra esta interfaz. Más adelante, se llama al método IRequestCallbackRequestCompletion::OnCompletion para notificar al controlador cuando se completa la solicitud de forma asincrónica.
  5. El método Send envía la solicitud de escritura con formato al dispositivo periférico conectado a SPB. La Flags variable indica si la solicitud de escritura se va a enviar de forma sincrónica o asincrónica.
  6. Si la solicitud se envía de forma sincrónica, el método IWDFIoRequest::D eleteWdfObject elimina el objeto de solicitud de E/S al que apunta pWdfIoRequest y el objeto secundario al pInputMemoryque apunta . La interfaz IWDFIoRequest hereda este método de la interfaz IWDFObject . Si la solicitud se envía de forma asincrónica, la llamada al método DeleteWdfObject debe producirse más adelante, en el método OnCompletion del controlador.

Una implementación alternativa del ejemplo de código anterior podría crear objetos IWDFIoRequest e IWDFMemory durante la inicialización del controlador y usar repetidamente estos mismos objetos en lugar de crear y eliminar objetos nuevos cada vez que se envía una solicitud de E/S. Para obtener más información, vea IWDFIoRequest2::Reuse e IWDFMemory::SetBuffer.

Además, una implementación alternativa podría inspeccionar el código de estado de E/S de la solicitud de E/S si la llamada send se realiza correctamente. Para obtener más información, vea IWDFIoRequest::GetCompletionParams.