Condividi tramite


Risorse hardware per Kernel-Mode driver di periferica SPB

Gli esempi di codice in questo argomento illustrano come il driver KmDF (Kernel-Mode Driver Framework ) per un dispositivo periferico in un semplice bus periferico (SPB) ottiene le risorse hardware necessarie per il funzionamento del dispositivo. Incluse in queste risorse sono le informazioni usate dal driver per stabilire una connessione logica al dispositivo. Altre risorse possono includere un interrupt 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 GPIO (General-Purpose I/O). A differenza di un dispositivo mappato alla memoria, un dispositivo periferico connesso a SPB non richiede un blocco di indirizzi di memoria di sistema in cui eseguire il mapping dei registri.

Questo driver implementa un set di funzioni di callback degli eventi di Plug and Play e risparmio energia. Per registrare queste funzioni con KMDF, la funzione di callback degli eventi EvtDriverDeviceAdd del driver chiama il metodo WdfDeviceInitSetPnpPowerEventCallbacks . Il framework chiama le funzioni di callback degli eventi di risparmio energia per notificare al driver le modifiche dello stato di alimentazione del dispositivo periferico. Incluse in queste funzioni è la funzione EvtDevicePrepareHardware , che esegue tutte le operazioni necessarie per rendere il dispositivo accessibile al driver.

Quando l'alimentazione viene ripristinata nel dispositivo periferico, il framework driver chiama la funzione EvtDevicePrepareHardware per notificare al driver di periferica SPB 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 ResourcesRaw è un handle di oggetto WDFCMRESLIST per l'elenco di risorse non elaborate e il parametro ResourcesTranslated è un handle di oggetto WDFCMRESLIST per l'elenco di risorse tradotte. Le risorse tradotte includono l'ID connessione richiesto dal driver per stabilire una connessione logica al dispositivo periferico. Per altre informazioni, vedere ID di connessione per dispositivi periferici SPB-Connected.

L'esempio di codice seguente mostra come la funzione EvtDevicePrepareHardware ottiene l'ID connessione dal parametro 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;
    }
}

Nell'esempio di codice precedente l'ID connessione per un dispositivo periferico connesso a SPB viene copiato in una variabile denominata connectionId.

Nell'esempio di codice seguente viene illustrato come incorporare questo ID connessione in un nome di percorso del dispositivo che può essere usato per aprire una connessione logica al dispositivo periferico. Questo nome del percorso del dispositivo identifica l'hub risorse come componente di sistema da cui ottenere i parametri necessari per accedere al dispositivo periferico.

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

Nell'esempio di codice precedente, la macro DECLARE_UNICODE_STRING_SIZE crea la dichiarazione di una variabile UNICODE_STRING inizializzata denominata szDeviceName con un buffer sufficientemente grande da contenere un nome di percorso del dispositivo nel formato usato dall'hub risorse. Questa macro è definita nel file di intestazione Ntdef.h. La costante RESOURCE_HUB_PATH_SIZE specifica il numero di byte nel nome del percorso del dispositivo. La macro RESOURCE_HUB_CREATE_PATH_FROM_ID genera il nome del percorso del dispositivo dall'ID connessione. RESOURCE_HUB_PATH_SIZE e RESOURCE_HUB_CREATE_PATH_FROM_ID sono definiti nel file di intestazione Reshub.h.

Nell'esempio di codice seguente viene usato il nome del percorso del dispositivo per aprire un handle di file (denominato SpbIoTarget) nel dispositivo periferico connesso a 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
    ...
}

Nell'esempio di codice precedente, la funzione WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME inizializza la struttura WDF_IO_TARGET_OPEN_PARAMS in modo che il driver possa aprire una connessione logica al dispositivo periferico specificando il nome del dispositivo. La SpbIoTarget variabile è un handle WDFIOTARGET per un oggetto di destinazione I/O del framework. Questo handle è stato ottenuto da una chiamata precedente al metodo WdfIoTargetCreate , che non è illustrato nell'esempio. Se la chiamata al metodo WdfIoTargetOpen ha esito positivo, il driver può usare l'handle SpbIoTarget per inviare richieste di I/O al dispositivo periferico.

Nella funzione di callback dell'evento EvtDriverDeviceAdd , il driver di periferica SPB può chiamare il metodo WdfRequestCreate per allocare un oggetto richiesta framework da usare dal driver. Successivamente, quando l'oggetto non è più necessario, il driver chiama il metodo WdfObjectDelete per eliminare l'oggetto. Il driver può riutilizzare l'oggetto richiesta framework ottenuto dalla chiamata WdfRequestCreate più volte per inviare richieste di I/O al dispositivo periferico. Per una richiesta di lettura, scrittura o IOCTL, il driver chiama il metodo WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously o WdfIoTargetSendIoctlSynchronously per inviare la richiesta.

Nell'esempio di codice seguente il driver chiama WdfIoTargetSendWriteSynchronously per inviare in modo sincrono una richiesta di IRP_MJ_WRITE al dispositivo periferico connesso a SPB. All'inizio di questo esempio, la pBuffer variabile punta a un buffer non di paging contenente i dati da scrivere nel dispositivo periferico e la dataSize variabile specifica le dimensioni, in byte, di questi dati.

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

L'esempio di codice precedente esegue le operazioni seguenti:

  1. La chiamata di funzione WDF_MEMORY_DESCRIPTOR_INIT_BUFFER inizializza la memoryDescriptor variabile, ovvero una struttura WDF_MEMORY_DESCRIPTOR che descrive il buffer di input. In precedenza, il driver chiamò una routine come ExAllocatePoolWithTag per allocare il buffer dal pool non di paging e copiare i dati di scrittura in questo buffer.
  2. La chiamata di funzione WDF_REQUEST_SEND_OPTIONS_INIT inizializza la requestOptions variabile, ovvero una struttura WDF_REQUEST_SEND_OPTIONS che contiene le impostazioni facoltative per la richiesta di scrittura. In questo esempio la struttura configura la richiesta per il timeout se non viene completata dopo due secondi.
  3. La chiamata al metodo WdfIoTargetSendWriteSynchronously invia la richiesta di scrittura al dispositivo periferico connesso a SPB. Il metodo restituisce in modo sincrono, dopo il completamento o il timeout dell'operazione di scrittura. Se necessario, un altro thread del driver può chiamare WdfRequestCancelSentRequest per annullare la richiesta.

Nella chiamata WdfIoTargetSendWriteSynchronously il driver fornisce una variabile denominata SpbRequest, che è un handle per un oggetto richiesta framework creato in precedenza dal driver. Dopo la chiamata WdfIoTargetSendWriteSynchronously , il driver deve in genere chiamare il metodo WdfRequestReuse per preparare l'oggetto richiesta framework da usare di nuovo.