Ressources matérielles pour les pilotes périphériques SPB User-Mode

Les exemples de code de cette rubrique montrent comment le pilote UMDF ( User-Mode Driver Framework ) pour un périphérique sur un bus de périphérique simple (SPB) obtient les ressources matérielles dont il a besoin pour faire fonctionner l’appareil. Ces ressources incluent les informations que le pilote utilise pour établir une connexion logique à l’appareil. D’autres ressources peuvent inclure une interruption et une ou plusieurs broches d’entrée ou de sortie GPIO. (Une broche GPIO est une broche sur un périphérique de contrôleur d’E/S à usage général configuré en tant qu’entrée ou sortie ; pour plus d’informations, consultez Pilotes de contrôleur GPIO.) Contrairement à un appareil mappé en mémoire, un périphérique connecté à SPB n’a pas besoin d’un bloc d’adresses mémoire système pour mapper ses registres.

Ce pilote implémente une interface IPnpCallbackHardware2 et inscrit cette interface auprès de l’UMDF lors de l’appel à la méthode IDriverEntry::OnDeviceAdd du pilote. L’infrastructure appelle les méthodes de l’interface IPnpCallbackHardware2 pour informer le pilote des modifications apportées à l’état d’alimentation de l’appareil.

Lorsque l’alimentation est rétablie sur le périphérique connecté à SPB, l’infrastructure du pilote appelle la méthode IPnpCallbackHardware2::OnPrepareHardware pour informer le pilote que ce périphérique doit être prêt à être utilisé. Pendant cet appel, le pilote reçoit deux listes de ressources matérielles en tant que paramètres d’entrée. Le paramètre pWdfResourcesRaw pointe vers la liste des ressources brutes, et le paramètre pWdfResourcesTranslated pointe vers la liste des ressources traduites. Les deux paramètres sont des pointeurs vers des objets IWDFCmResourceList . Les ressources traduites incluent l’ID de connexion dont le pilote de périphérique SPB a besoin pour établir une connexion logique au périphérique connecté à SPB. Pour plus d’informations, consultez ID de connexion pour les périphériques SPB.

Pour permettre à un pilote de périphérique UMDF de recevoir des ID de connexion dans sa liste de ressources, le fichier INF qui installe le pilote doit inclure la directive suivante dans sa section DDInstall spécifique à WDF :

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Pour plus d’informations sur cette directive, consultez Spécification de directives WDF dans les fichiers INF.

L’exemple de code suivant montre comment la méthode OnPrepareHardware du pilote obtient l’ID de connexion à partir du paramètre 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;
    }
}

L’exemple de code précédent copie l’ID de connexion d’un périphérique connecté à SPB dans une variable nommée connectionId. L’exemple de code suivant montre comment incorporer l’ID de connexion dans un nom de chemin d’accès d’appareil qui peut être utilisé pour identifier l’appareil périphérique.

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

L’exemple de code précédent écrit le nom du chemin d’accès du périphérique connecté à SPB dans le szTargetPath tableau. L’exemple de code suivant utilise ce nom de chemin d’accès d’appareil pour ouvrir un handle de fichier sur l’appareil périphérique connecté à 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
    ...
}

Dans l’exemple de code précédent, la pRemoteTarget variable est un pointeur vers un objet IWDFRemoteTarget . Si l’appel à la méthode IWDFRemoteTarget::OpenFileByName réussit, le pilote du périphérique connecté à SPB peut utiliser l’objet IWDFRemoteTarget pour envoyer des demandes d’E/S à l’appareil périphérique. Avant que le pilote n’envoie une demande de lecture, d’écriture ou DETL IOCTL au périphérique, il appelle la méthode IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite ou IWDFRemoteTarget::FormatRequestForIoctl pour mettre en forme la demande d’E/S. L’interface IWDFRemoteTarget hérite de ces trois méthodes de l’interface IWDFIoTarget. Ensuite, le pilote appelle la méthode IWDFIoRequest::Send pour envoyer la demande d’E/S au périphérique connecté à SPB.

Dans l’exemple de code suivant, le pilote de périphérique SPB appelle la méthode Send pour envoyer une demande de IRP_MJ_WRITE au périphérique connecté à 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’exemple de code précédent effectue les opérations suivantes :

  1. La pWdfDevice variable est un pointeur vers l’interface IWDFDevice de l’objet d’appareil framework qui représente le périphérique connecté à SPB. La méthode IWDFDevice::CreateRequest crée une demande d’E/S et encapsule cette requête dans l’interface IWDFIoRequest instance pointée par la pWdfIoRequest variable.
  2. La pWdfDriver variable est un pointeur vers l’interface IWDFDriver de l’objet de pilote d’infrastructure qui représente le pilote périphérique SPB. Les pInBuffer variables et inBufferSize spécifient l’adresse et la taille de la mémoire tampon d’entrée qui contient les données de la demande d’écriture. La méthode IWDFDriver::CreatePreallocatedWdfMemory crée un objet de mémoire framework pour la mémoire tampon d’entrée et désigne l’objet IWDFIoRequest pointé vers par pWdfIoRequest comme objet parent de l’objet mémoire (de sorte que l’objet mémoire soit automatiquement libéré lorsque son parent est libéré). Une fois que le pilote appelle la méthode Release pour libérer sa référence locale à l’objet mémoire, le parent détient la seule référence à cet objet.
  3. La pWdfRemoteTarget variable est le pointeur cible distant obtenu à partir de l’appel OpenFileByName dans un exemple de code antérieur. La méthode IWDFRemoteTarget::FormatRequestForWrite met en forme la demande d’E/S pour une opération d’écriture.
  4. La fSynchronous variable est TRUE si la demande d’écriture doit être envoyée de manière synchrone, et false si elle doit être envoyée de manière asynchrone. La pCallback variable est un pointeur vers une interface IRequestCallbackRequestCompletion créée précédemment. Si la demande doit être envoyée de manière asynchrone, l’appel à la méthode IWDFIoRequest::SetCompletionCallback inscrit cette interface. Plus tard, la méthode IRequestCallbackRequestCompletion::OnCompletion est appelée pour notifier le pilote lorsque la demande se termine de manière asynchrone.
  5. La méthode Send envoie la demande d’écriture mise en forme au périphérique connecté à SPB. La Flags variable indique si la demande d’écriture doit être envoyée de manière synchrone ou asynchrone.
  6. Si la requête est envoyée de manière synchrone, la méthode IWDFIoRequest::D eleteWdfObject supprime à la fois l’objet de demande d’E/S pointé vers pWdfIoRequest et l’objet enfant pointé vers pInputMemory. L’interface IWDFIoRequest hérite de cette méthode de l’interface IWDFObject. Si la requête est envoyée de manière asynchrone, l’appel à la méthode DeleteWdfObject doit se produire ultérieurement, dans la méthode OnCompletion du pilote.

Une autre implémentation de l’exemple de code précédent peut créer des objets IWDFIoRequest et IWDFMemory lors de l’initialisation du pilote, et utiliser à plusieurs reprises ces mêmes objets au lieu de créer et de supprimer de nouveaux objets chaque fois qu’une demande d’E/S est envoyée. Pour plus d’informations, consultez IWDFIoRequest2::Reuse et IWDFMemory::SetBuffer.

En outre, une autre implémentation peut inspecter le code d’E/S status à partir de la demande d’E/S si l’appel d’envoi réussit. Pour plus d’informations, consultez IWDFIoRequest::GetCompletionParams.