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

Les exemples de code de cette rubrique montrent comment le pilote User-Mode Driver Framework (UMDF) pour un périphérique sur un bus périphérique simple obtient les ressources matérielles dont il a besoin pour faire fonctionner l’appareil. Inclus dans ces ressources est les informations que le pilote utilise pour établir une connexion logique à l’appareil. Des ressources complémentaires peuvent inclure une interruption et une ou plusieurs broches GPIO d'entrée ou de sortie. (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, voir Pilotes de contrôleur GPIO.) Contrairement à un appareil mappé en mémoire, un périphérique connecté à SPB ne nécessite pas de bloc d’adresses mémoire système pour mapper ses registres.

Ce pilote implémente une interface IPnpCallbackHardware2 et inscrit cette interface avec la fonction UMDF pendant 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 restaurée sur l’appareil périphérique connecté à SPB, l’infrastructure de pilotes appelle la méthode IPnpCallbackHardware2 ::OnPrepareHardware pour avertir 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 que le pilote de périphérique SPB nécessite pour établir une connexion logique avec le 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 des 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 chemin d'accès de l'appareil périphérique connecté à SPB dans le tableau szTargetPath. L’exemple de code suivant utilise ce nom de chemin d’accès d’appareil pour ouvrir un descripteur de fichier sur l’appareil périphérique connecté via le 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 de l’appareil 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 envoie une requête de lecture, d’écriture ou IOCTL au périphérique, le pilote appelle la méthode IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite ou IWDFRemoteTarget::FormatRequestForIoctl pour mettre en forme la requête 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 requête d’E/S à l’appareil périphérique connecté à SPB.

Dans l’exemple de code suivant, le pilote périphérique SPB appelle la méthode Send pour envoyer une requête IRP_MJ_WRITE à l’appareil 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 l’appareil périphérique connecté à SPB. La méthode IWDFDevice ::CreateRequest crée une requête d’E/S et encapsule cette requête dans l’instance d’interface IWDFIoRequest 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 variables pInBuffer 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 mémoire d’infrastructure pour la mémoire tampon d’entrée et désigne l’objet IWDFIoRequest vers lequel pointe pWdfIoRequest l’objet parent de l’objet mémoire (afin 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 contient 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 précédent. La méthode IWDFRemoteTarget ::FormatRequestForWrite met en forme la requête d’E/S pour une opération d’écriture.
  4. La variable a la fSynchronous valeur TRUE si la demande d’écriture doit être envoyée de manière synchrone, et a la valeur 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 façon 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 requête se termine de façon asynchrone.
  5. La méthode Send envoie la demande d’écriture mise en forme à l’appareil périphérique connecté à SPB. La Flags variable indique si la demande d’écriture doit être envoyée de façon synchrone ou asynchrone.
  6. Si la requête est envoyée de façon synchrone, la méthode IWDFIoRequest ::D eleteWdfObject supprime à la fois l’objet de requête d’E/S pointé vers pWdfIoRequest et l’objet enfant pointé par pInputMemory. L’interface IWDFIoRequest hérite de cette méthode de l’interface IWDFObject . Si la requête est envoyée de façon 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 pendant 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 requête 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’état d’E/S à partir de la demande d’E/S si l’appel d’envoi réussit. Pour plus d’informations, consultez IWDFIoRequest ::GetCompletionParams.