Connessione di un driver KMDF ai pin di I/O GPIO
Una risorsa GPIO I/O è un set di pin GPIO configurati come input dati o output dati. Il driver per un dispositivo periferico che si connette fisicamente a questi pin acquisisce la risorsa I/O GPIO corrispondente dal sistema operativo. Il driver del dispositivo periferico apre una connessione ai pin GPIO in questa risorsa e invia richieste di I/O all'handle che rappresenta questa connessione.
Nell'esempio di codice seguente viene illustrato come il driver del driver in modalità kernel (KMDF) per un dispositivo periferico possa ottenere una descrizione della risorsa I/O GPIO assegnata dal gestore Plug and Play (PnP) al driver.
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;
...
}
Nell'esempio di codice precedente, la DeviceExtension
variabile è un puntatore al contesto del dispositivo per il dispositivo periferico. La XyzDrvGetDeviceExtension
funzione, che recupera questo contesto del dispositivo, viene implementata dal driver del dispositivo periferico. Questo driver ha registrato in precedenza la funzione di callback EvtDevicePrepareHardware chiamando il metodo WdfDeviceInitSetPnpPowerEventCallbacks .
Nell'esempio di codice seguente viene illustrato come il driver del dispositivo periferico può usare la descrizione della risorsa GPIO ottenuta nell'esempio di codice precedente per aprire un handle WDFIOTARGET alla risorsa I/O del driver.
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;
}
...
Nell'esempio di codice precedente, la Device
variabile è un handle WDFDEVICE per l'oggetto dispositivo framework per il dispositivo periferico. La funzione RESOURCE_HUB_CREATE_PATH_FROM_ID crea una stringa contenente il nome della risorsa I/O GPIO. Nell'esempio di codice viene usata questa stringa per aprire la risorsa I/O GPIO in base al nome.
Dopo aver ottenuto un handle per una risorsa di I/O GPIO, questo driver può inviare richieste di controllo I/O per leggere i dati dai pin GPIO o scrivere dati. Un driver che apre una risorsa I/O GPIO per le letture usa IOCTL_GPIO_READ_PINS richieste di controllo I/O per leggere i dati dai pin nella risorsa. Un driver che apre una risorsa I/O GPIO per le scritture usa IOCTL_GPIO_WRITE_PINS richieste di controllo I/O per scrivere dati nei pin nella risorsa. Nell'esempio di codice seguente viene illustrato come eseguire un'operazione di lettura o scrittura 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);
}
...
Nell'esempio di codice precedente, Data
è un puntatore a un buffer di dati, è la dimensione, Size
in byte, di questo buffer di dati e ReadOperation
indica se l'operazione richiesta è una lettura (TRUE) o una scrittura (FALSE).
Per ulteriori informazioni
Per altre informazioni sulle richieste di IOCTL_GPIO_READ_PINS , incluso il mapping dei pin di input dati ai bit nel buffer di output della richiesta, vedere IOCTL_GPIO_READ_PINS. Per altre informazioni sulle richieste di IOCTL_GPIO_WRITE_PINS , incluso il mapping dei bit nel buffer di input della richiesta ai pin di output dei dati, vedere IOCTL_GPIO_WRITE_PINS.
Per un driver di esempio che illustra come scrivere un driver di periferica GPIO che viene eseguito in modalità kernel, vedere il driver di esempio SimDevice nella raccolta di driver di esempio GPIO in GitHub.