Risorse hardware per User-Mode driver di periferiche SPB

Gli esempi di codice di questo argomento illustrano come il driver UMDF ( User-Mode Driver Framework ) per un dispositivo periferico in un semplice bus periferico (SPB) ottiene le risorse hardware necessarie per operare il dispositivo. Incluse in queste risorse sono le informazioni usate dal driver per stabilire una connessione logica al dispositivo. Le risorse aggiuntive possono includere un interruzione e uno o più pin di input o output GPIO. Un pin GPIO è un pin su un dispositivo controller di I/O per utilizzo generico configurato come input o output. Per altre informazioni, vedere Driver del controller GPIO. A differenza di un dispositivo mappato alla memoria, un dispositivo periferico connesso a SPB non richiede un blocco di indirizzi di memoria di sistema per eseguire il mapping dei registri.

Questo driver implementa un'interfaccia IPnpCallbackHardware2 e registra questa interfaccia con UMDF durante la chiamata al metodo IDriverEntry::OnDeviceAdd del driver. Il framework chiama i metodi nell'interfaccia IPnpCallbackHardware2 per informare il driver delle modifiche nello stato di alimentazione del dispositivo.

Quando l'alimentazione viene ripristinata nel dispositivo periferico connesso a SPB, il framework driver chiama il metodo IPnpCallbackHardware2::OnPrepareHardware per notificare al driver che questo dispositivo deve essere preparato per l'uso. Durante questa chiamata, il driver riceve due elenchi di risorse hardware come parametri di input. Il parametro pWdfResourcesRaw punta all'elenco delle risorse non elaborate e il parametro pWdfResourcesTranslated punta all'elenco delle risorse tradotte. Entrambi i parametri sono puntatori agli oggetti IWDFCmResourceList . Le risorse tradotte includono l'ID connessione necessario per il driver di periferica SPB per stabilire una connessione logica al dispositivo periferico connesso con SPB. Per altre informazioni, vedere ID connessione per dispositivi periferiche SPB.

Per abilitare un driver di periferica UMDF per ricevere ID connessione nell'elenco delle risorse, il file INF che installa il driver deve includere la direttiva seguente nella sezione DDInstall specifica di WDF:

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Per altre informazioni su questa direttiva, vedere Specifica delle direttive WDF nei file INF.

Nell'esempio di codice seguente viene illustrato come il metodo OnPrepareHardware del driver ottiene l'ID connessione dal parametro 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;
    }
}

Nell'esempio di codice precedente viene copiato l'ID connessione per un dispositivo periferico connesso a SPB in una variabile denominata connectionId. Nell'esempio di codice seguente viene illustrato come incorporare l'ID connessione in un nome del percorso del dispositivo che può essere usato per identificare il dispositivo periferico.

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
     ...
}

Nell'esempio di codice precedente viene scritto il nome del percorso per il dispositivo periferico connesso con SPB nella szTargetPath matrice. Nell'esempio di codice seguente viene usato questo nome del percorso del dispositivo per aprire un handle di file al dispositivo periferico connesso 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
    ...
}

Nell'esempio di codice precedente la pRemoteTarget variabile è un puntatore a un oggetto IWDFRemoteTarget . Se la chiamata al metodo IWDFRemoteTarget::OpenFileByName ha esito positivo, il driver per il dispositivo periferico connesso a SPB può usare l'oggetto IWDFRemoteTarget per inviare richieste di I/O al dispositivo periferico. Prima che il driver invii una richiesta di lettura, scrittura o IOCTL al dispositivo periferico, il driver chiama il metodo IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite o IWDFRemoteTarget::FormatRequestForIoctl per formattare la richiesta di I/O. L'interfaccia IWDFRemoteTarget eredita questi tre metodi dall'interfaccia IWDFIoTarget . Successivamente, il driver chiama il metodo IWDFIoRequest::Send per inviare la richiesta di I/O al dispositivo periferico connesso a SPB.

Nell'esempio di codice seguente, il driver di periferica SPB chiama il metodo Send per inviare una richiesta di IRP_MJ_WRITE al dispositivo periferico connesso con 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);
}

L'esempio di codice precedente esegue le operazioni seguenti:

  1. La pWdfDevice variabile è un puntatore all'interfaccia IWDFDevice dell'oggetto dispositivo framework che rappresenta il dispositivo periferico connesso a SPB. Il metodo IWDFDevice::CreateRequest crea una richiesta di I/O e incapsula questa richiesta nell'istanza dell'interfaccia IWDFIoRequest a cui punta la pWdfIoRequest variabile.
  2. La pWdfDriver variabile è un puntatore all'interfaccia IWDFDriver dell'oggetto driver del framework che rappresenta il driver di periferica SPB. Le pInBuffer variabili e inBufferSize specificano l'indirizzo e le dimensioni del buffer di input che contiene i dati per la richiesta di scrittura. Il metodo IWDFDriver::CreatePreallocatedWdfMemory crea un oggetto memoria del framework per il buffer di input e designa l'oggetto IWDFIoRequest a cui pWdfIoRequest punta come oggetto padre dell'oggetto memoria (in modo che l'oggetto memoria venga rilasciato automaticamente quando viene rilasciato il relativo padre). Dopo che il driver chiama il metodo Release per rilasciare il riferimento locale all'oggetto memory, l'elemento padre contiene l'unico riferimento a questo oggetto.
  3. La pWdfRemoteTarget variabile è il puntatore di destinazione remoto ottenuto dalla chiamata OpenFileByName in un esempio di codice precedente. Il metodo IWDFRemoteTarget::FormatRequestForWrite formatta la richiesta di I/O per un'operazione di scrittura.
  4. La fSynchronous variabile è TRUE se la richiesta di scrittura deve essere inviata in modo sincrono ed è FALSE se deve essere inviata in modo asincrono. La pCallback variabile è un puntatore a un'interfaccia IRequestCallbackRequestCompletion creata in precedenza. Se la richiesta deve essere inviata in modo asincrono, la chiamata al metodo IWDFIoRequest::SetCompletionCallback registra questa interfaccia. Successivamente, il metodo IRequestCallbackRequestCompletion::OnCompletion viene chiamato per notificare al driver quando la richiesta viene completata in modo asincrono.
  5. Il metodo Send invia la richiesta di scrittura formattata al dispositivo periferico connesso a SPB. La Flags variabile indica se la richiesta di scrittura deve essere inviata in modo sincrono o asincrono.
  6. Se la richiesta viene inviata in modo sincrono, il metodo IWDFIoRequest::D eleteWdfObject elimina sia l'oggetto richiesta I/O a cui punta l'oggetto figlio puntato pWdfIoRequest da pInputMemory. L'interfaccia IWDFIoRequest eredita questo metodo dall'interfaccia IWDFObject . Se la richiesta viene inviata in modo asincrono, la chiamata al metodo DeleteWdfObject deve essere eseguita in un secondo momento, nel metodo OnCompletion del driver.

Un'implementazione alternativa dell'esempio di codice precedente potrebbe creare oggetti IWDFIoRequest e IWDFMemory durante l'inizializzazione del driver e usare ripetutamente questi stessi oggetti anziché creare ed eliminare nuovi oggetti ogni volta che viene inviata una richiesta di I/O. Per altre informazioni, vedere IWDFIoRequest2::Riuso e IWDFMemory::SetBuffer.

Inoltre, un'implementazione alternativa potrebbe controllare il codice di stato di I/O dalla richiesta di I/O se la chiamata di invio ha esito positivo. Per altre informazioni, vedere IWDFIoRequest::GetCompletionParams.