Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В примерах кода в этом разделе показано, как драйвер Kernel-Mode Driver Framework (KMDF) для периферийного устройства на простой периферийной шине (SPB) получает аппаратные ресурсы, необходимые для работы устройства. В этих ресурсах содержатся сведения, используемые драйвером для установления логического подключения к устройству. Дополнительные ресурсы могут включать прерывание и один или несколько входных или выходных контактов GPIO. (Вывод GPIO — это контакт на устройстве контроллера ввода-вывода общего назначения, настроенного в качестве входа или выхода; подробнее см. в разделе General-Purpose Драйверы ввода-вывода (GPIO).) В отличие от устройства, сопоставленного с памятью, периферийное устройство, подключенное по шине SPB, не требует блока адресов системной памяти для сопоставления своих регистров.
Этот драйвер реализует набор функций обратного вызова событий Plug and Play и управления питанием. Чтобы зарегистрировать эти функции в рамках KMDF, функция обратного вызова события EvtDriverDeviceAdd вызывает метод WdfDeviceInitSetPnpPowerEventCallbacks. Платформа вызывает функции обратного вызова событий управления питанием, чтобы уведомить драйвер изменений в состоянии питания периферийных устройств. В эти функции входит функция EvtDevicePrepareHardware , которая выполняет все операции, необходимые для обеспечения доступа устройства к драйверу.
Когда питание восстанавливается на периферийное устройство, платформа драйверов вызывает функцию EvtDevicePrepareHardware , чтобы уведомить периферийный драйвер SPB о том, что это устройство должно быть подготовлено для использования. Во время этого вызова драйвер получает два списка аппаратных ресурсов в качестве входных параметров. Параметр ResourcesRaw — это дескриптор объекта WDFCMRESLIST в список необработанных ресурсов, а параметр ResourcesTranslated — это дескриптор объекта WDFCMRESLIST в список преобразованных ресурсов. Преобразованные ресурсы включают идентификатор подключения , необходимый драйверу для установления логического подключения к периферийным устройствам. Дополнительные сведения см. в разделе Идентификаторы подключений для SPB-Connected периферийных устройств.
В следующем примере кода показано, как функция EvtDevicePrepareHardware получает идентификатор подключения из параметра 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;
}
}
Приведенный выше пример кода копирует идентификатор подключения для подключенного к SPB периферийного устройства в переменную с именем connectionId.
В следующем примере кода показано, как включить этот идентификатор подключения в имя пути устройства, которое можно использовать для открытия логического подключения к периферийным устройствам. Это имя пути устройства определяет концентратор ресурсов как системный компонент, из которого необходимо получить параметры, необходимые для доступа к периферийным устройствам.
// 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
...
}
В приведенном выше примере кода макрос DECLARE_UNICODE_STRING_SIZE создает объявление инициализированной переменной UNICODE_STRING с szDeviceName именем буфера, достаточно большим, чтобы содержать имя пути устройства в формате, используемом концентратором ресурсов. Этот макрос определен в файле заголовка Ntdef.h.
Константа RESOURCE_HUB_PATH_SIZE указывает количество байтов в имени пути устройства. Макрос RESOURCE_HUB_CREATE_PATH_FROM_ID создает имя пути устройства из идентификатора подключения.
RESOURCE_HUB_PATH_SIZE и RESOURCE_HUB_CREATE_PATH_FROM_ID определены в файле заголовка Reshub.h.
В следующем примере кода используется имя пути устройства для открытия дескриптора файла (именованного SpbIoTarget) для периферийного устройства, подключенного к 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
...
}
В предыдущем примере кода функция WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME инициализирует структуру WDF_IO_TARGET_OPEN_PARAMS , чтобы драйвер смог открыть логическое соединение с периферийным устройством, указав имя устройства. Переменная SpbIoTarget — это дескриптор WDFIOTARGET для целевого объекта ввода-вывода платформы. Этот дескриптор был получен из предыдущего вызова метода WdfIoTargetCreate , который не показан в примере. Если вызов метода WdfIoTargetOpen выполнен успешно, драйвер может использовать дескриптор SpbIoTarget для отправки запросов ввода-вывода на периферийное устройство.
В функции обратного вызова событий EvtDriverDeviceAdd периферийный драйвер SPB может вызвать метод WdfRequestCreate , чтобы выделить объект запроса платформы для использования драйвером. Позже, когда объект больше не нужен, драйвер вызывает метод WdfObjectDelete для удаления объекта. Драйвер может повторно использовать объект запроса платформы, полученный из вызова WdfRequestCreate несколько раз для отправки запросов ввода-вывода на периферийное устройство. Для запроса на чтение, запись или IOCTL драйвер вызывает метод WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously или WdfIoTargetSendIoctlSynchronously для отправки запроса.
В следующем примере кода драйвер вызывает WdfIoTargetSendWriteSynchronously для синхронной отправки запроса IRP_MJ_WRITE на периферийное устройство, подключенное к SPB. В начале этого примера pBuffer переменная указывает на неупакованный буфер, содержащий данные, которые записываются на периферийное устройство, и dataSize переменная указывает размер в байтах этих данных.
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
...
}
В приведенном выше примере кода выполняется следующее:
- Вызов функции WDF_MEMORY_DESCRIPTOR_INIT_BUFFER инициализирует
memoryDescriptorпеременную, которая представляет собой структуру WDF_MEMORY_DESCRIPTOR , описывающую входной буфер. Ранее драйвер вызывал подпрограмму, например ExAllocatePoolWithTag, чтобы из непагированного пула выделить буфер и скопировать данные для записи в этот буфер. - Вызов функции WDF_REQUEST_SEND_OPTIONS_INIT инициализирует
requestOptionsпеременную, которая представляет собой WDF_REQUEST_SEND_OPTIONS структуру , содержащую необязательные параметры для запроса на запись. В этом примере структура настраивает время ожидания запроса, если оно не завершается через две секунды. - Вызов метода WdfIoTargetSendWriteSynchronously отправляет запрос на запись на подключенное к SPB периферийное устройство. Метод возвращается синхронно после завершения операции записи или времени ожидания. При необходимости другой поток драйвера может вызвать WdfRequestCancelSentRequest , чтобы отменить запрос.
В вызове WdfIoTargetSendWriteSynchronous драйвер предоставляет переменную с именем SpbRequest, которая является дескриптором объекта запроса платформы, созданного ранее драйвером. После вызова WdfIoTargetSendWriteSynchronous драйвер обычно вызывает метод WdfRequestRequestReuse , чтобы подготовить объект запроса платформы, который будет использоваться снова.