Connexion d’un pilote KMDF à des broches d’E/S GPIO
Une ressource d’E/S GPIO est un ensemble d’une ou plusieurs broches GPIO configurées en tant qu’entrées de données ou sorties de données. Le pilote d’un périphérique qui se connecte physiquement à ces broches acquiert la ressource d’E/S GPIO correspondante à partir du système d’exploitation. Le pilote de périphérique ouvre une connexion aux broches GPIO de cette ressource et envoie des demandes d’E/S au handle qui représente cette connexion.
L’exemple de code suivant montre comment le pilote KMDF (Kernel-Mode Driver Framework) pour un périphérique peut obtenir une description de la ressource d’E/S GPIO que le gestionnaire de Plug-and-Play (PnP) a attribuée au pilote.
NTSTATUS
EvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesRaw,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
int ResourceCount, Index;
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
XYZ_DEVICE_CONTEXT *DeviceExtension;
...
DeviceExtension = XyzDrvGetDeviceExtension(Device);
ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
for (Index = 0; Index < ResourceCount; Index += 1) {
Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);
switch (Descriptor->Type) {
//
// GPIO I/O descriptors
//
case CmResourceTypeConnection:
//
// Check against expected connection type.
//
if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&
(Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) {
DeviceExtension->ConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
DeviceExtension->ConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;
...
}
Dans l’exemple de code précédent, la DeviceExtension
variable est un pointeur vers le contexte de l’appareil périphérique. La XyzDrvGetDeviceExtension
fonction, qui récupère ce contexte d’appareil, est implémentée par le pilote de périphérique. Ce pilote a précédemment inscrit sa fonction de rappel EvtDevicePrepareHardware en appelant la méthode WdfDeviceInitSetPnpPowerEventCallbacks .
L’exemple de code suivant montre comment le pilote de périphérique peut utiliser la description de la ressource GPIO obtenue dans l’exemple de code précédent pour ouvrir un handle WDFIOTARGET à la ressource d’E/S GPIO du pilote.
NTSTATUS IoRoutine(WDFDEVICE Device, BOOLEAN ReadOperation)
{
WDFIOTARGET IoTarget;
XYZ_DEVICE_CONTEXT *DeviceExtension;
UNICODE_STRING ReadString;
WCHAR ReadStringBuffer[100];;
BOOL DesiredAccess;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES ObjectAttributes;
WDF_IO_TARGET_OPEN_PARAMS OpenParams
DeviceExtension = XyzDrvGetDeviceExtension(Device);
RtlInitEmptyUnicodeString(&ReadString,
ReadStringBuffer,
sizeof(ReadStringBuffer));
Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString,
DeviceExtension->ConnectionId.LowPart,
DeviceExtension->ConnectionId.HighPart);
NT_ASSERT(NT_SUCCESS(Status));
WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);
ObjectAttributes.ParentObject = Device;
Status = WdfIoTargetCreate(Device, &ObjectAttributes, &IoTarget);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
if (ReadOperation != FALSE) {
DesiredAccess = GENERIC_READ;
} else {
DesiredAccess = GENERIC_WRITE;
}
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, DesiredAccess);
Status = WdfIoTargetOpen(IoTarget, &OpenParams);
if (!NT_SUCCESS(Status)) {
goto IoErrorEnd;
}
...
Dans l’exemple de code précédent, la Device
variable est un handle WDFDEVICE pour l’objet d’appareil framework pour le périphérique. La fonction RESOURCE_HUB_CREATE_PATH_FROM_ID crée une chaîne qui contient le nom de la ressource d’E/S GPIO. L’exemple de code utilise cette chaîne pour ouvrir la ressource d’E/S GPIO par nom.
Une fois que le pilote de périphérique a obtenu un handle pour une ressource d’E/S GPIO, ce pilote peut envoyer des demandes de contrôle d’E/S pour lire ou écrire des données dans les broches GPIO. Un pilote qui ouvre une ressource d’E/S GPIO pour les lectures utilise IOCTL_GPIO_READ_PINS demandes de contrôle d’E/S pour lire les données des broches de la ressource. Un pilote qui ouvre une ressource d’E/S GPIO pour les écritures utilise IOCTL_GPIO_WRITE_PINS demandes de contrôle d’E/S pour écrire des données dans les broches de la ressource. L’exemple de code suivant montre comment effectuer une opération de lecture ou d’écriture GPIO.
WDF_OBJECT_ATTRIBUTES RequestAttributes;
WDF_OBJECT_ATTRIBUTES Attributes;
WDF_REQUEST_SEND_OPTIONS SendOptions;
WDFREQUEST IoctlRequest;
WDFIOTARGET IoTarget;
WDFMEMORY WdfMemory;
NTSTATUS Status;
WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes);
Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Set up a WDF memory object for the IOCTL request.
//
WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
Attributes.ParentObject = IoctlRequest;
Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Format the request.
//
if (ReadOperation != FALSE) {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_READ_PINS,
NULL,
0,
WdfMemory,
0);
} else {
Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
IoctlRequest,
IOCTL_GPIO_WRITE_PINS,
WdfMemory,
0,
WdfMemory,
0);
}
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
//
// Send the request synchronously (with a 60-second time-out).
//
WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions,
WDF_REL_TIMEOUT_IN_SEC(60));
Status = WdfRequestAllocateTimer(IoctlRequest);
if (!NT_SUCCESS(Status)) {
goto RwErrorExit;
}
if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) {
Status = WdfRequestGetStatus(IoctlRequest);
}
...
Dans l’exemple de code précédent, Data
est un pointeur vers une mémoire tampon de données, Size
est la taille, en octets, de cette mémoire tampon de données et ReadOperation
indique si l’opération demandée est une lecture (TRUE) ou une écriture (FALSE).
Informations supplémentaires
Pour plus d’informations sur les requêtes IOCTL_GPIO_READ_PINS , y compris le mappage des broches d’entrée de données aux bits de la mémoire tampon de sortie de la requête, consultez IOCTL_GPIO_READ_PINS. Pour plus d’informations sur les requêtes IOCTL_GPIO_WRITE_PINS , y compris le mappage des bits de la mémoire tampon d’entrée de requête aux broches de sortie de données, consultez IOCTL_GPIO_WRITE_PINS.
Pour obtenir un exemple de pilote qui montre comment écrire un pilote de périphérique GPIO qui s’exécute en mode noyau, consultez l’exemple de pilote SimDevice dans la collection d’exemples de pilotes GPIO sur GitHub.