Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Заметка Этот раздел относится только к Kernel-Mode Driver Framework (KMDF) версии 1.13 и более ранних версий.
Многие устройства имеют аппаратные регистры, которые управляют созданием прерываний и маскированием. Как правило, драйверы KMDF и UMDF для таких устройств используют встроенную поддержку прерываний платформы.
Однако простое устройство на аппаратной платформе System on a Chip (SoC) может не иметь аппаратных регистров для прерываний. В результате драйвер для такого устройства может не иметь возможности контролировать момент генерации прерывания или не быть способным маскировать прерывание на уровне аппаратного обеспечения. Если устройство генерирует прерывание сразу после подключения, и драйвер использует поддержку прерываний фреймворка, возможно, прерывание сработает до того, как фреймворк полностью инициализировал объект прерывания. В результате драйвер KMDF должен вызывать подпрограммы WDM непосредственно для подключения и отключения прерываний. Так как драйвер UMDF не может вызывать эти методы, вы не можете написать драйвер UMDF для такого устройства.
В этом разделе описывается, как драйвер KMDF может обрабатывать прерывания для такого устройства.
На аппаратных платформах SoC активные с обоих сторон прерывания обычно используются для очень простых устройств, таких как аппаратные кнопки. Когда пользователь нажимает кнопку, линия сигнала прерывания от устройства переходит от низкого к высокому или от высокого до низкого. Когда пользователь освобождает кнопку, линия прерывания переходит в противоположное направление. Контакт GPIO, настроенный как активно реагирующий на оба перехода прерываний, создает прерывания как при переходе с низкого на высокий, так и с высокого на низкий уровень, что приводит к тому, что система вызывает программу обслуживания прерываний (ISR) драйвера периферийных устройств в обоих этих случаях. Однако драйвер не получает сведения о том, является ли переход из низкого в высокий или из высокого в низкий.
Чтобы различать переходы от низкого к высокому и от высокого к низкому, драйвер должен отслеживать состояние каждого прерывания. Для этого драйвер может поддерживать булевое состояние прерывания, которое равно FALSE, если состояние линии прерывания низкое, и TRUE, если состояние линии высокое.
Рассмотрим пример, в котором состояние линии по умолчанию устанавливается на низкий уровень при запуске системы. Драйвер инициализирует значение состояния false в функции обратного вызова EvtDevicePrepareHardware . Затем каждый раз, когда вызывается ISR драйвера, и это сигнализирует об изменении состояния, драйвер инвертирует значение состояния в своём ISR.
Если состояние линии высокое при запуске системы, прерывание срабатывает сразу после включения. Так как драйвер вызывает подпрограмму IoConnectInterruptEx напрямую, а не вызывает WdfInterruptCreate, он гарантирует получение возможного немедленного прерывания.
Для этого решения требуется, чтобы контроллер GPIO поддерживал активные прерывания в оборудовании или что драйвер контроллера GPIO эмулирует активные прерывания в программном обеспечении. Сведения об эмуляции прерываний active-both см. в описании элемента EmulateActiveBoth структуры CONTROLLER_ATTRIBUTE_FLAGS.
В следующем примере кода показано, как драйвер KMDF для периферийного устройства может отслеживать полярность прерываний.
typedef struct _INTERRUPT_CONTEXT INTERRUPT_CONTEXT, *PINTERRUPT_CONTEXT;
typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;
struct _INTERRUPT_CONTEXT
{
BOOLEAN State;
PDEVICE_CONTEXT DeviceContext;
};
struct _DEVICE_CONTEXT
{
PKINTERRUPT Interrupt;
INTERRUPT_CONTEXT InterruptContext;
PDEVICE_OBJECT PhysicalDeviceObject;
KSPIN_LOCK SpinLock;
};
...
BOOLEAN
YourInterruptIsr(
__in PKINTERRUPT Interrupt,
__in PVOID ServiceContext
)
{
PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ServiceContext;
PDEVICE_CONTEXT DeviceContext = InterruptContext->DeviceContext;
//
// Flip the state.
//
InterruptContext->State = !InterruptContext->State;
IoRequestDpc(DeviceContext->PhysicalDeviceObject, DeviceContext->PhysicalDeviceObject->CurrentIrp, InterruptContext);
}
VOID
YourInterruptDpc(
__in PKDPC Dpc,
__in PDEVICE_OBJECT DeviceObject,
__inout PIRP Irp,
__in_opt PVOID ContextPointer
)
{
PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ContextPointer;
...
}
NTSTATUS
EvtDriverDeviceAdd(
__in WDFDRIVER Driver,
__in PWDFDEVICE_INIT DeviceInit
)
{
WDFDEVICE Device;
PDEVICE_CONTEXT DeviceContext;
...
DeviceContext->Interrupt = NULL;
DeviceContext->PhysicalDeviceObject = WdfDeviceWdmGetPhysicalDevice(Device);
KeInitializeSpinLock(&DeviceContext->SpinLock);
IoInitializeDpcRequest(DeviceContext->PhysicalDeviceObject, YourInterruptDpc);
}
NTSTATUS
EvtDevicePrepareHardware(
__in WDFDEVICE Device,
__in WDFCMRESLIST ResourcesRaw,
__in WDFCMRESLIST ResourcesTranslated
)
{
PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);
for (ULONG i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);
if (descriptor->Type == CmResourceTypeInterrupt)
{
IO_CONNECT_INTERRUPT_PARAMETERS params;
RtlZeroMemory(¶ms, sizeof(params));
params.Version = CONNECT_FULLY_SPECIFIED;
params.FullySpecified.PhysicalDeviceObject = DeviceContext->PhysicalDeviceObject;
params.FullySpecified.InterruptObject = &DeviceContext->Interrupt;
params.FullySpecified.ServiceRoutine = YourInterruptIsr;
params.FullySpecified.ServiceContext = (PVOID)&DeviceContext->InterruptContext;
params.FullySpecified.SpinLock = &DeviceContext->SpinLock;
params.FullySpecified.Vector = descriptor->u.Interrupt.Vector;
params.FullySpecified.Irql = (KIRQL)descriptor->u.Interrupt.Level;
params.FullySpecified.SynchronizeIrql = (KIRQL)descriptor->u.Interrupt.Level;
params.FullySpecified.InterruptMode = (descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
params.FullySpecified.ProcessorEnableMask = descriptor->u.Interrupt.Affinity;
params.FullySpecified.ShareVector = descriptor->ShareDisposition;
//
// Default state is low.
//
DeviceContext->InterruptContext.State = 0;
DeviceContext->InterruptContext.DeviceContext = DeviceContext;
return IoConnectInterruptEx(¶ms);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
EvtDeviceReleaseHardware(
__in WDFDEVICE Device,
__in WDFCMRESLIST ResourcesTranslated
)
{
PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);
if (NULL != DeviceContext->Interrupt)
{
IO_DISCONNECT_INTERRUPT_PARAMETERS params;
params.Version = CONNECT_FULLY_SPECIFIED;
params.ConnectionContext.InterruptObject = DeviceContext->Interrupt;
IoDisconnectInterruptEx(¶ms);
}
return STATUS_SUCCESS;
}
В предыдущем примере кода функция обратного вызова EvtDriverDeviceAdd настраивает контекст устройства, а затем вызывает IoInitializeDpcRequest для регистрации подпрограммы DpcForIsr .
Рутина InterruptService драйвера инвертирует значение состояния прерывания, а затем вызывает IoRequestDpc для постановки DPC в очередь.
В функции обратного вызова EvtDevicePrepareHardware драйвер инициализирует значение состояния в FALSE, а затем вызывает IoConnectInterruptEx. В функции обратного вызова EvtDeviceReleaseHardware драйвер вызывает IoDisconnectInterruptEx, чтобы отменить регистрацию своего ISR.