Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les exemples de code de cette rubrique montrent comment le pilote Kernel-Mode Driver Framework (KMDF) pour un périphérique sur un bus périphérique simple (SPB) obtient les ressources matérielles dont il a besoin pour utiliser l’appareil. Inclus dans ces ressources est les informations que le pilote utilise pour établir une connexion logique à l’appareil. Des ressources supplémentaires 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 General-Purpose Pilotes d’E/S (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 un ensemble de fonctions de rappel d’événements Plug-and-Play et de gestion de l’alimentation. Pour inscrire ces fonctions avec KMDF, la fonction de rappel d’événement EvtDriverDeviceAdd du pilote appelle la méthode WdfDeviceInitSetPnpPowerEventCallbacks . L’infrastructure appelle les fonctions de rappel d’événements de gestion de l’alimentation pour informer le pilote des modifications apportées à l’état d’alimentation de l’appareil périphérique. Inclus dans ces fonctions est la fonction EvtDevicePrepareHardware , qui effectue toutes les opérations nécessaires pour rendre l’appareil accessible au pilote.
Lorsque l’alimentation est restaurée sur le périphérique, l’infrastructure de pilotes appelle la fonction EvtDevicePrepareHardware pour informer le pilote périphérique SPB que cet appareil doit être préparé pour une utilisation. Pendant cet appel, le pilote reçoit deux listes de ressources matérielles en tant que paramètres d’entrée. Le paramètre ResourcesRaw est un handle d’objet WDFCMRESLIST pour la liste des ressources brutes, et le paramètre ResourcesTranslated est un handle d’objet WDFCMRESLIST pour la liste des ressources traduites. Les ressources traduites incluent l’ID de connexion requis par le pilote pour établir une connexion logique au périphérique périphérique. Pour plus d’informations, consultez ID de connexion pour SPB-Connected périphériques.
L’exemple de code suivant montre comment la fonction EvtDevicePrepareHardware obtient l’ID de connexion à partir du paramètre 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;
}
}
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 cet ID de connexion dans un nom de chemin d’accès d’appareil qui peut être utilisé pour ouvrir une connexion logique à l’appareil périphérique. Ce nom de chemin d’accès d’appareil identifie le hub de ressources comme composant système à partir duquel obtenir les paramètres nécessaires pour accéder à l’appareil périphérique.
// 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
...
}
Dans l’exemple de code précédent, la macro DECLARE_UNICODE_STRING_SIZE crée la déclaration d’une variable d’UNICODE_STRING initialisée nommée szDeviceName qui a une mémoire tampon suffisamment grande pour contenir un nom de chemin d’accès d’appareil au format utilisé par le hub de ressources. Cette macro est définie dans le fichier d’en-tête Ntdef.h. La constante RESOURCE_HUB_PATH_SIZE spécifie le nombre d’octets dans le nom du chemin d’accès de l’appareil. La macro RESOURCE_HUB_CREATE_PATH_FROM_ID génère le nom du chemin d’accès de l’appareil à partir de l’ID de connexion.
RESOURCE_HUB_PATH_SIZE et RESOURCE_HUB_CREATE_PATH_FROM_ID sont définis dans le fichier d’en-tête Reshub.h.
L’exemple de code suivant utilise le chemin d’accès de l’appareil pour ouvrir un handle de fichier, nommé SpbIoTarget, pour l’appareil périphérique connecté au 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
...
}
Dans l’exemple de code précédent, la fonction WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME initialise la structure WDF_IO_TARGET_OPEN_PARAMS afin que le pilote puisse ouvrir une connexion logique à l’appareil périphérique en spécifiant le nom de l’appareil. La SpbIoTarget variable est un handle de type WDFIOTARGET vers un objet cible d'E/S dans un framework. Ce handle a été obtenu à partir d’un appel précédent à la méthode WdfIoTargetCreate , qui n’est pas illustré dans l’exemple. Si l’appel à la méthode WdfIoTargetOpen réussit, le pilote peut utiliser le SpbIoTarget handle pour envoyer des demandes d’E/S à l’appareil périphérique.
Dans la fonction de rappel d’événement EvtDriverDeviceAdd , le pilote périphérique SPB peut appeler la méthode WdfRequestCreate pour allouer un objet de demande d’infrastructure à utiliser par le pilote. Plus tard, lorsque l’objet n’est plus nécessaire, le pilote appelle la méthode WdfObjectDelete pour supprimer l’objet. Le pilote peut réutiliser l’objet de requête de framework obtenu à partir de l’appel WdfRequestCreate plusieurs fois pour envoyer des demandes d’E/S à l’appareil périphérique. Pour une demande de lecture, d'écriture ou IOCTL, le pilote appelle la méthode WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously ou WdfIoTargetSendIoctlSynchronously pour envoyer la requête.
Dans l’exemple de code suivant, le pilote appelle WdfIoTargetSendWriteSynchronously pour envoyer de manière synchrone une demande de IRP_MJ_WRITE à l’appareil périphérique connecté à SPB. Au début de cet exemple, la pBuffer variable pointe vers une mémoire tampon non paginé qui contient les données à écrire sur l’appareil périphérique, et la dataSize variable spécifie la taille, en octets, de ces données.
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’exemple de code précédent effectue les opérations suivantes :
-
L’appel de fonction WDF_MEMORY_DESCRIPTOR_INIT_BUFFER initialise la
memoryDescriptorvariable, qui est une structure WDF_MEMORY_DESCRIPTOR qui décrit la mémoire tampon d’entrée. Auparavant, le pilote appelait une routine telle qu’ExAllocatePoolWithTag pour allouer la mémoire tampon à partir d’un pool non paginé et copié les données d’écriture dans cette mémoire tampon. - L’appel de fonction WDF_REQUEST_SEND_OPTIONS_INIT initialise la
requestOptionsvariable, qui est une structure WDF_REQUEST_SEND_OPTIONS qui contient les paramètres facultatifs de la demande d’écriture. Dans cet exemple, la structure configure la requête pour qu’elle expire si elle ne se termine pas après deux secondes. - L’appel à la méthode WdfIoTargetSendWriteSynchronously envoie la demande d’écriture à l’appareil périphérique connecté à SPB. La méthode retourne de façon synchrone, une fois l’opération d’écriture terminée ou expire. Si nécessaire, un autre thread de pilote peut appeler WdfRequestCancelSentRequest pour annuler la requête.
Dans l’appel WdfIoTargetSendWriteSynchronously , le pilote fournit une variable nommée SpbRequest, qui est un handle à un objet de demande d’infrastructure que le pilote a créé précédemment. Après l’appel WdfIoTargetSendWriteSynchronously , le pilote doit généralement appeler la méthode WdfRequestReuse pour préparer l’objet de demande d’infrastructure à réutiliser.