diff options
| author | Adam Shapiro <[email protected]> | 2015-11-17 12:11:14 -0800 |
|---|---|---|
| committer | Adam Shapiro <[email protected]> | 2015-11-17 12:11:14 -0800 |
| commit | 5b815f85ef86b2c2522dbd79e9b1b900f8fb77e4 (patch) | |
| tree | 37a66ba8dadc5ad5d04e49d815cd19907ef8805e | |
| parent | 2c9b5b696dc2c396e6ffc9ec721d8085f279a114 (diff) | |
Add New Sample To Git Hub
Adding the new HAL Extension sample to the online repository in the
"General" Category
17 files changed, 3529 insertions, 0 deletions
diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.c b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.c new file mode 100644 index 00000000..3aade318 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.c @@ -0,0 +1,1253 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + HalExtSampleDma.c + +Abstract: + + This file implements a HAL Extension Module for the fictitious chDMA + controller. + +Author: + + Cody Hartwig (chartwig) 9-Jun-2011 + +--*/ + +// +// --------------------------------------------------------------------Includes +// + +#include <nthalext.h> + +// +// ---------------------------------------------------------------- Definitions +// + +// +// Disable warnings of features used by the standard headers +// +// Disable warning C4214: nonstandard extension used : bit field types other than int +// Disable warning C4201: nonstandard extension used : nameless struct/union +// Disable warning C4115: named type definition in parentheses +// Disable warning C4127: conditional expression is constant +// Disable warning C4200: zero-sized array in struct/union +// + +#pragma warning(disable:4214 4201 4115 4127 4200) + +// +// Define register set size. +// + +#define CH_DMA_REGISTER_SIZE (0x210) + +// +// Define maximums. +// + +#define CH_DMA_MAX_REQUEST_LINES (32) +#define CH_DMA_MAX_CHANNELS (32) + +#define CONFIGURE_ADD_REQ_CONFIG (0x80000000) + +// +// Define register offsets. +// + +#define CH_DMA_CONTROL (0x00) +#define CH_DMA_STATUS (0x04) +#define CH_DMA_INTERRUPT_MASK (0x08) + +#define CH_DMA_CHAN_CONTROL (0x00) +#define CH_DMA_CHAN_MEM_PTR (0x04) +#define CH_DMA_CHAN_DEV_PTR (0x08) +#define CH_DMA_CHAN_STATUS (0x1c) + +// +// Define device-side bus widths. +// + +#define CH_DMA_WIDTH_8_BIT (0) +#define CH_DMA_WIDTH_16_BIT (1) +#define CH_DMA_WIDTH_32_BIT (2) + +// +// Define transfer burst sizes. +// + +#define CH_DMA_BURST_1_WORD (0) +#define CH_DMA_BURST_2_WORD (1) +#define CH_DMA_BURST_4_WORD (2) +#define CH_DMA_BURST_8_WORD (3) + +// +// Define transfer directions. +// + +#define CH_DMA_DEVICE_WRITE (0) +#define CH_DMA_DEVICE_READ (1) + +// +// ------------------------------------------------------ Data Type Definitions +// + +#pragma pack(push, 1) + +// +// Register Definitions. +// + +typedef struct _CH_DMA_CONTROL_REGISTER { + union { + struct { + ULONG ControllerEnable:1; + ULONG Reserved:31; + }; + ULONG AsUlong; + }; +} CH_DMA_CONTROL_REGISTER, *PCH_DMA_CONTROL_REGISTER; + +typedef struct _CH_DMA_CHAN_CONTROL_REGISTER { + union { + struct { + ULONG ChannelEnable:1; + ULONG InterruptEnable:1; + ULONG DeviceWidth:2; + ULONG BurstSize:2; + ULONG FlowControl:1; + ULONG Loop:1; + ULONG RequestLine:5; + ULONG ReadFromDevice:1; + ULONG Reserved:2; + ULONG Length:16; + }; + ULONG AsUlong; + }; +} CH_DMA_CHAN_CONTROL_REGISTER, *PCH_DMA_CHAN_CONTROL_REGISTER; + +typedef struct _CH_DMA_CHAN_STATUS_REGISTER { + union { + struct { + ULONG Busy:1; + ULONG Reserved:15; + ULONG BytesTransferred:16; + }; + ULONG AsUlong; + }; +} CH_DMA_CHAN_STATUS_REGISTER, *PCH_DMA_CHAN_STATUS_REGISTER; + +// +// CSRT resource descriptor types for chDMA. +// + +typedef struct _CH_DMA_ADD_REQ_LINE_CONFIG { + ULONG RequestLine; + CH_DMA_CHAN_CONTROL_REGISTER Ctrl; +} CH_DMA_ADD_REQ_LINE_CONFIG, *PCH_DMA_ADD_REQ_LINE_CONFIG; + +typedef struct _RD_DMA_CONTROLLER { + CSRT_RESOURCE_DESCRIPTOR_HEADER Header; + ULONGLONG BasePhysicalAddress; + ULONG ChannelCount; + ULONG MinimumRequestLine; + ULONG MaximumRequestLine; + ULONG InterruptGsi; + BOOLEAN CacheCoherent; + ULONG ReqLineConfigCount; + CH_DMA_ADD_REQ_LINE_CONFIG ReqLineConfigs[ANYSIZE_ARRAY]; +} RD_DMA_CONTROLLER, *PRD_DMA_CONTROLLER; + +// +// Resource description matching chDMA controller described in chdmaReadme.txt: +// +// CH_DMA_CHAN_CONTROL_REGISTER UartControl; +// UartControl.AsUlong = 0; +// UartControl.DeviceWidth = CH_DMA_WIDTH_8_BIT; +// UartControl.BurstSize = CH_DMA_BURST_1_WORD; +// UartControl.FlowControl = 1; +// +// CH_DMA_ADD_REQ_LINE_CONFIG UartConfig = { +// 0x15, +// UartControl +// }; +// +// RD_DMA_CONTROLLER DmaDesc = { +// Header, +// 0x70001000, +// 32, +// 0, +// 31, +// 0x27, +// FALSE, +// 1, +// { UartConfig } +// }; +// + +typedef struct _RD_DMA_CHANNEL { + CSRT_RESOURCE_DESCRIPTOR_HEADER Header; + ULONG ChannelNumber; +} RD_DMA_CHANNEL, *PRD_DMA_CHANNEL; + +// +// Resource description matching chDMA channel described in chdmaReadme.txt: +// +// RD_DMA_CHANNEL DmaDesc = { +// Header, +// 0 +// }; +// + +#pragma pack(pop) + +// +// Extension Request line to configuration mapping. +// + +typedef struct _CH_REQ_LINE_CONFIG { + BOOLEAN Valid; + CH_DMA_CHAN_CONTROL_REGISTER Ctrl; +} CH_REQ_LINE_CONFIG, *PCH_REQ_LINE_CONFIG; + +typedef struct _CH_REQ_LINE_CONFIG_TABLE { + CH_REQ_LINE_CONFIG Entries[CH_DMA_MAX_REQUEST_LINES]; +} CH_DMA_REQ_LINE_CONFIG_TABLE, *PCH_DMA_REQ_LINE_CONFIG_TABLE; + +// +// Define extension internal data for the controller and channel. +// + +typedef struct _CH_DMA_CHANNEL { + BOOLEAN Active; + BOOLEAN AutoInit; +} CH_DMA_CHANNEL, *PCH_DMA_CHANNEL; + +typedef struct _CH_DMA_CONTROLLER { + + // + // Virtual and physical base addresses of the controller. + // + + PULONG ControllerBaseVa; + PHYSICAL_ADDRESS ControllerBasePa; + + ULONG ChannelCount; + ULONG MinimumRequestLine; + ULONG MaximumRequestLine; + + // + // Individual channel extension status. + // + + CH_DMA_CHANNEL Channels[CH_DMA_MAX_CHANNELS]; + + // + // Request line to configuration mapping. + // + + CH_DMA_REQ_LINE_CONFIG_TABLE ReqConfig; +} CH_DMA_CONTROLLER, *PCH_DMA_CONTROLLER; + +// +// ----------------------------------------------------------------- Prototypes +// + +VOID +ChInitializeController ( + __in PVOID ControllerContext + ); + +BOOLEAN +ChValidateRequestLineBinding ( + __in PVOID ControllerContext, + __in PDMA_REQUEST_LINE_BINDING_DESCRIPTION BindingDescription + ); + +ULONG +ChQueryMaxFragments ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG MaxFragmentsRequested + ); + +VOID +ChProgramChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG RequestLine, + __in PDMA_SCATTER_GATHER_LIST MemoryAddresses, + __in PHYSICAL_ADDRESS DeviceAddress, + __in BOOLEAN WriteToDevice, + __in BOOLEAN LoopTransfer + ); + +BOOLEAN +ChCancelTransfer ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ); + +NTSTATUS +ChConfigureChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG FunctionNumber, + __in PVOID Context + ); + +VOID +ChFlushChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ); + +_Success_(return != FALSE) +BOOLEAN +ChHandleInterrupt ( + __in PVOID ControllerContext, + __out PULONG ChannelNumber, + __out PDMA_INTERRUPT_TYPE InterruptType + ); + +ULONG +ChReadDmaCounter ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ); + +// +// -------------------------------------------------------------------- Globals +// + +DMA_FUNCTION_TABLE ChFunctionTable = +{ + ChInitializeController, + ChValidateRequestLineBinding, + ChQueryMaxFragments, + ChProgramChannel, + ChConfigureChannel, + ChFlushChannel, + ChHandleInterrupt, + ChReadDmaCounter, + NULL, /* ReportCommonBuffer */ + ChCancelTransfer +}; + +// +// ------------------------------------------------------------------- Routines +// + +VOID +WriteRegister ( + __in PCH_DMA_CONTROLLER Controller, + __in ULONG RegisterOffset, + __in ULONG Value + ) + +{ + + PULONG Address; + + Address = Controller->ControllerBaseVa; + + // + // Add register offset. Register offset is in bytes. Pointer addition is + // in ULONGs. + // + + Address += (RegisterOffset / 4); + + WRITE_REGISTER_ULONG(Address, Value); +} + +ULONG +ReadRegister ( + __in PCH_DMA_CONTROLLER Controller, + __in ULONG RegisterOffset + ) + +{ + + PULONG Address; + + Address = Controller->ControllerBaseVa; + + // + // Add register offset. Register offset is in bytes. Pointer addition is + // in ULONGs. + // + + Address += (RegisterOffset / 4); + + return READ_REGISTER_ULONG(Address); +} + +VOID +WriteChannelRegister ( + __in PCH_DMA_CONTROLLER Controller, + __in ULONG ChannelNumber, + __in ULONG RegisterOffset, + __in ULONG Value + ) + +{ + PULONG Address; + + Address = Controller->ControllerBaseVa; + + // + // Add channel base offset. + // Register offset is in bytes. Pointer addition is in ULONGs. + // + + Address += (0x10 / 4); + + // + // Add channel number offset. + // + + Address += ((0x10 * ChannelNumber) / 4); + + // + // Add channel register offset. + // + + Address += (RegisterOffset / 4); + + WRITE_REGISTER_ULONG(Address, Value); +} + +ULONG +ReadChannelRegister ( + __in PCH_DMA_CONTROLLER Controller, + __in ULONG ChannelNumber, + __in ULONG RegisterOffset + ) + +{ + + PULONG Address; + + Address = Controller->ControllerBaseVa; + + // + // Add channel base offset. + // Register offset is in bytes. Pointer addition is in ULONGs. + // + + Address += (0x10 / 4); + + // + // Add channel number offset. + // + + Address += ((0x10 * ChannelNumber) / 4); + + // + // Add channel register offset. + // + + Address += (RegisterOffset / 4); + + return READ_REGISTER_ULONG(Address); +} + +VOID +ChInitializeController ( + __in PVOID ControllerContext + ) + +/*++ + +Routine Description: + + This routine provides an opportunity for DMA controllers to initialize. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internel data. + +Return Value: + + None. + +--*/ + +{ + + CH_DMA_CONTROL_REGISTER CtrlRegister; + PCH_DMA_CONTROLLER Controller; + ULONG Index; + ULONG InterruptMask; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + + // + // Map the controller base iff this is the first call to init (and it is + // therefore not already mapped.) + // + + if (Controller->ControllerBaseVa == NULL) { + Controller->ControllerBaseVa = + (PULONG) HalMapIoSpace(Controller->ControllerBasePa, + PAGE_SIZE, + MmNonCached); + + NT_ASSERT(Controller->ControllerBaseVa != NULL); + } + + if (Controller->ControllerBaseVa == NULL) { + return; + } + + // + // Initialize each channel. + // + + for (Index = 0; Index < Controller->ChannelCount; Index += 1) { + WriteChannelRegister(Controller, Index, CH_DMA_CHAN_CONTROL, 0); + Controller->Channels[Index].Active = FALSE; + Controller->Channels[Index].AutoInit = FALSE; + } + + // + // Enable the DMA controller. + // + + CtrlRegister.AsUlong = 0; + CtrlRegister.ControllerEnable = 1; + WriteRegister(Controller, CH_DMA_CONTROL, CtrlRegister.AsUlong); + + // + // Enable all interrupts. + // + + InterruptMask = (1UL << Controller->ChannelCount) - 1; + WriteRegister(Controller, CH_DMA_INTERRUPT_MASK, InterruptMask); + + return; +} + +BOOLEAN +ChValidateRequestLineBinding ( + __in PVOID ControllerContext, + __in PDMA_REQUEST_LINE_BINDING_DESCRIPTION BindingDescription + ) + +/*++ + +Routine Description: + + This routine queries a DMA controller extension to test the validity of a + request line binding. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + DeviceDescription - Supplies a pointer to the request information. + +Return Value: + + TRUE if the request line binding is valid and supported by the controller. + + FALSE if the binding is invalid. + +Environment: + + PASSIVE_LEVEL. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + if (BindingDescription->ChannelNumber > Controller->ChannelCount) { + return FALSE; + } + + if ((BindingDescription->RequestLine > Controller->MaximumRequestLine) || + (BindingDescription->RequestLine < Controller->MinimumRequestLine)) { + + return FALSE; + } + + return TRUE; +} + +ULONG +ChQueryMaxFragments ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG MaxFragmentsRequested + ) + +/*++ + +Routine Description: + + This routine queries the DMA extension to determine the number of + scatter gather fragments that the next transfer can support. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the number of the channel to program. + + MaxFragmentsRequested - Supplies a hint to the maximum fragments useful to + this transfer. + +Return Value: + + Number of fragments the next transfer on this channel can support. + +--*/ + +{ + + UNREFERENCED_PARAMETER(ControllerContext); + UNREFERENCED_PARAMETER(ChannelNumber); + UNREFERENCED_PARAMETER(MaxFragmentsRequested); + + return 1; +} + +VOID +ChProgramChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG RequestLine, + __in PDMA_SCATTER_GATHER_LIST MemoryAddresses, + __in PHYSICAL_ADDRESS DeviceAddress, + __in BOOLEAN WriteToDevice, + __in BOOLEAN LoopTransfer + ) + +/*++ + +Routine Description: + + This routine programs a DMA controller channel for a specific transfer. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the number of the channel to program. + + RequestLine - Supplies the request line number to program. This request + line number is system-unique (as provided to the HAL during + registration) and must be translated by the extension. + + MemoryAddress - Supplies the address to be programmed into the memory + side of the channel configuration. + + DeviceAddress - Supplies the address to be programmed into the device + side of the channel configuration. + + WriteToDevice - Supplies the direction of the transfer. + + LoopTransfer - Supplies whether AutoInitialize has been set in the + adapter making this request. + +Return Value: + + None. + +--*/ + +{ + + CH_DMA_CHAN_CONTROL_REGISTER Ctrl; + PCH_DMA_CONTROLLER Controller; + ULONG DevPtr; + ULONG MemPtr; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + Controller->Channels[ChannelNumber].Active = TRUE; + + // + // If this request line exists in the request line config table, use + // those values. Otherwise, use the reset values. + // + + Ctrl.AsUlong = 0; + if (Controller->ReqConfig.Entries[RequestLine].Valid != 0) { + Ctrl.AsUlong = Controller->ReqConfig.Entries[RequestLine].Ctrl.AsUlong; + } + + Ctrl.ChannelEnable = 1; + Ctrl.InterruptEnable = 1; + Ctrl.Loop = (LoopTransfer == FALSE) ? 0 : 1; + Ctrl.ReadFromDevice = (WriteToDevice == FALSE) ? 1 : 0; + + // + // Request lines numbers reported by BIOS may be offset to make them + // globally unique. Request lines on the controller are based at 0. + // + + Ctrl.RequestLine = RequestLine - Controller->MinimumRequestLine; + Ctrl.Length = MemoryAddresses->Elements[0].Length; + + DevPtr = DeviceAddress.LowPart; + MemPtr = MemoryAddresses->Elements[0].Address.LowPart; + + WriteChannelRegister(Controller, + ChannelNumber, + CH_DMA_CHAN_MEM_PTR, + MemPtr); + + WriteChannelRegister(Controller, + ChannelNumber, + CH_DMA_CHAN_DEV_PTR, + DevPtr); + + // + // Channel control is written last as it will enable the channel. + // + + WriteChannelRegister(Controller, + ChannelNumber, + CH_DMA_CHAN_CONTROL, + Ctrl.AsUlong); +} + +BOOLEAN +ChCancelTransfer ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ) + +/*++ + +Routine Description: + + This routine must disable the selected channel. The channel must not be + capable of interrupting for this transfer after being cleared in this way. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the channel number. + +Return Value: + + FALSE if the channel is already idle or if the channel is already asserting + an interrupt. TRUE is the channel is active and no interrupt is asserted. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + ULONG StatusRegister; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + + // + // If the channel is not active (because the interrupt already fired or + // because the channel was never programmed, return immediately. + // + + if (Controller->Channels[ChannelNumber].Active == FALSE) { + return FALSE; + } + + // + // Disable the channel. + // + + WriteChannelRegister(Controller, ChannelNumber, CH_DMA_CHAN_CONTROL, 0); + + // + // If an interrupt is already pending on the channel, do nothing. The + // normal interrupt path will complete it. If an interrupt is not pending + // then this was successfully cancelled. In that case, mark the channel + // inactive and return TRUE. + // + + StatusRegister = ReadRegister(Controller, CH_DMA_STATUS); + if ((StatusRegister & (1UL << ChannelNumber)) != 0) { + return FALSE; + + } else { + Controller->Channels[ChannelNumber].Active = FALSE; + return TRUE; + } +} + +NTSTATUS +AddReqLineConfig ( + __in PCH_DMA_CONTROLLER Controller, + __in PCH_DMA_ADD_REQ_LINE_CONFIG Config + ) + +/*++ + +Routine Description: + + This routine updates the request line configuration table in the + extension's internal data for this controller. + +Arguments: + + Controller - Supplies a pointer to the internal data for the controller. + + Config - Supplies a pointer to the configuration to modify. + +Return Value: + + STATUS_INVALID_PARAMETER if the request line is invalid. + Else, STATUS_SUCCESS. + +--*/ + +{ + + ULONG RequestLine; + + RequestLine = Config->RequestLine; + if ((RequestLine < Controller->MinimumRequestLine) || + (RequestLine > Controller->MaximumRequestLine)) { + + return STATUS_INVALID_PARAMETER; + } + + Controller->ReqConfig.Entries[RequestLine].Ctrl.AsUlong = + Config->Ctrl.AsUlong; + + return STATUS_SUCCESS; +} + +NTSTATUS +ChConfigureChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber, + __in ULONG FunctionNumber, + __in PVOID Context + ) + +/*++ + +Routine Description: + + This routine configures the channel for a DMA extension specific operation. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the channel to configure. + + FunctionNumber - Supplies the ID of the operation to perform. + + Context - Supplies parameters for this operation. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(ChannelNumber); + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + switch (FunctionNumber) { + case CONFIGURE_ADD_REQ_CONFIG: + Status = AddReqLineConfig(Controller, + (PCH_DMA_ADD_REQ_LINE_CONFIG)Context); + + break; + default: + Status = STATUS_NOT_IMPLEMENTED; + } + + return Status; +} + +VOID +ChFlushChannel ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ) + +/*++ + +Routine Description: + + This routine flushes a previous transfer from a channel and returns the + channel to a state ready for the next ProgramChannel call. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the channel to flush. + +Return Value: + + None. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + WriteChannelRegister(Controller, ChannelNumber, CH_DMA_CHAN_CONTROL, 0); +} + +_Success_(return != FALSE) +BOOLEAN +ChHandleInterrupt ( + __in PVOID ControllerContext, + __out PULONG ChannelNumber, + __out PDMA_INTERRUPT_TYPE InterruptType + ) + +/*++ + +Routine Description: + + This routine probes a controller for interrupts, clears any interrupts + found, fills in channel and interrupt type information. This routine + will be called repeatedly until FALSE is returned. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies a placeholder for the extension to fill in which + channel is interrupting. + + InterruptType - Supplies a placeholder for the extension to fill in the + interrupt type. + +Return Value: + + TRUE if an interrupt was found on this controller. + + FALSE otherwise. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + ULONG Index; + ULONG StatusRegister; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + StatusRegister = ReadRegister(Controller, CH_DMA_STATUS); + for (Index = 0; Index < Controller->ChannelCount; Index += 1) { + if ((StatusRegister & (1UL << Index)) != 0) { + *ChannelNumber = Index; + *InterruptType = InterruptTypeCompletion; + WriteRegister(Controller, CH_DMA_STATUS, (1UL << Index)); + + // + // If the channel is not marked as active, then this interrupt + // is spurious. Probably this is the result of a transfer being + // cancelled. + // + + if (Controller->Channels[Index].Active == FALSE) { + continue; + } + + // + // If the channel is not in autoinitialize mode, is it inactive now. + // If the channel is in autoinitialize, then it will remain active + // until it receives a cancel. + // + + if (Controller->Channels[Index].AutoInit == FALSE) { + Controller->Channels[Index].Active = FALSE; + } + + return TRUE; + } + } + + return FALSE; +} + +ULONG ChReadDmaCounter ( + __in PVOID ControllerContext, + __in ULONG ChannelNumber + ) + +/*++ + +Routine Description: + + This routine determines how many bytes remain to be transferred on the + given channel. If the current transfer is set to loop, this routine + will return the number of bytes remaining in the current iteration. + +Arguments: + + ControllerContext - Supplies a pointer to the controller's internal data. + + ChannelNumber - Supplies the channel number. + +Return Value: + + Returns the number of bytes remaining to be transferred on the given + channel. + +--*/ + +{ + + PCH_DMA_CONTROLLER Controller; + CH_DMA_CHAN_CONTROL_REGISTER Ctrl; + CH_DMA_CHAN_STATUS_REGISTER StatusReg; + + Controller = (PCH_DMA_CONTROLLER)ControllerContext; + Ctrl.AsUlong = ReadChannelRegister(Controller, + ChannelNumber, + CH_DMA_CHAN_CONTROL); + + StatusReg.AsUlong = ReadChannelRegister(Controller, + ChannelNumber, + CH_DMA_CHAN_STATUS); + + return (Ctrl.Length - StatusReg.BytesTransferred); +} + +NTSTATUS +RegisterDmaController ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup, + __in PCSRT_RESOURCE_DESCRIPTOR_HEADER ResourceDescriptor, + __out PULONG ControllerId + ) + +/*++ + +Routine Description: + + This routine takes a DMA resource descriptor with subtype controller. + The controller is then registered with the HAL. + +Arguments: + + Handle - Supplies handle passed to extension. + + ResourceGroup - Supplies resource group containing this descriptor. + + ResourceDescrpitor - Supplies the resource descriptor. + + ControllerId - Supplies a placeholder for the controller ID returned from + the HAL after registration. + +Return Value: + + NTSTATUS Value. + +--*/ + +{ + + PCH_REQ_LINE_CONFIG ConfigEntry; + PCH_REQ_LINE_CONFIG ConfigTableDst; + PCH_DMA_ADD_REQ_LINE_CONFIG ConfigTableSrc; + CH_DMA_CONTROLLER Controller; + PRD_DMA_CONTROLLER DmaDesc; + DMA_INITIALIZATION_BLOCK DmaInitBlock; + ULONG Index; + ULONG RequestLine; + NTSTATUS Status; + + RtlZeroMemory(&Controller, sizeof(CH_DMA_CONTROLLER)); + + DmaDesc = (PRD_DMA_CONTROLLER)ResourceDescriptor; + Controller.ControllerBasePa.QuadPart = DmaDesc->BasePhysicalAddress; + Controller.ChannelCount = DmaDesc->ChannelCount; + Controller.MinimumRequestLine = DmaDesc->MinimumRequestLine; + Controller.MaximumRequestLine = DmaDesc->MaximumRequestLine; + + // + // Build request line configuration mapping table. + // + + ConfigTableDst = &Controller.ReqConfig.Entries[0]; + ConfigTableSrc = &DmaDesc->ReqLineConfigs[0]; + + // + // Verify the supplied configuration description will fit within + // the build maximum. + // + + if (DmaDesc->ReqLineConfigCount >= CH_DMA_MAX_REQUEST_LINES) { + Status = STATUS_INVALID_PARAMETER_1; + goto Exit; + } + + for (Index = 0; Index < DmaDesc->ReqLineConfigCount; Index += 1) { + + RequestLine = ConfigTableSrc[Index].RequestLine; + + // + // Keep PreFast happy that we're assigning within memory bounds + // + + if (RequestLine >= CH_DMA_MAX_REQUEST_LINES) { + Status = STATUS_INVALID_PARAMETER_2; + goto Exit; + } + + ConfigEntry = &ConfigTableDst[RequestLine]; + ConfigEntry->Ctrl.AsUlong = ConfigTableSrc[Index].Ctrl.AsUlong; + ConfigEntry->Valid = TRUE; + } + + INITIALIZE_DMA_HEADER(&DmaInitBlock); + DmaInitBlock.ChannelCount = Controller.ChannelCount; + DmaInitBlock.MinimumTransferUnit = 1; + DmaInitBlock.MinimumRequestLine = Controller.MinimumRequestLine; + DmaInitBlock.MaximumRequestLine = Controller.MaximumRequestLine; + DmaInitBlock.CacheCoherent = DmaDesc->CacheCoherent; + DmaInitBlock.GeneratesInterrupt = TRUE; + DmaInitBlock.InternalData = (PVOID)&Controller; + DmaInitBlock.InternalDataSize = sizeof(CH_DMA_CONTROLLER); + DmaInitBlock.DmaAddressWidth = 32; + DmaInitBlock.Gsi = DmaDesc->InterruptGsi; + DmaInitBlock.InterruptPolarity = InterruptActiveHigh; + DmaInitBlock.InterruptMode = LevelSensitive; + DmaInitBlock.Operations = &ChFunctionTable; + + // + // Register physical address space with the HAL. + // + + HalRegisterPermanentAddressUsage(Controller.ControllerBasePa, + CH_DMA_REGISTER_SIZE); + + // + // Register controller. + // + + Status = RegisterResourceDescriptor(Handle, + ResourceGroup, + ResourceDescriptor, + &DmaInitBlock); + + *ControllerId = DmaInitBlock.ControllerId; + +Exit: + return Status; + +} + +NTSTATUS +RegisterDmaChannel ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup, + __in PCSRT_RESOURCE_DESCRIPTOR_HEADER ResourceDescriptor, + __in ULONG ControllerId + ) + +/*++ + +Routine Description: + + This routine takes a DMA resource descriptor with subtype channel. + The channel is then registered with the HAL. + +Arguments: + + Handle - Supplies handle passed to extension. + + ResourceGroup - Supplies resource group containing this descriptor. + + ResourceDescrpitor - Supplies the resource descriptor. + + ControllerId - Supplies the controller ID this channel is registered with. + +Return Value: + + NTSTATUS Value. + +--*/ + +{ + + DMA_CHANNEL_INITIALIZATION_BLOCK DmaChannelInitBlock; + PRD_DMA_CHANNEL DmaDesc; + + NT_ASSERT(ControllerId != 0); + + DmaDesc = (PRD_DMA_CHANNEL)ResourceDescriptor; + INITIALIZE_DMA_CHANNEL_HEADER(&DmaChannelInitBlock); + DmaChannelInitBlock.ControllerId = ControllerId; + DmaChannelInitBlock.GeneratesInterrupt = FALSE; + DmaChannelInitBlock.ChannelNumber = DmaDesc->ChannelNumber; + DmaChannelInitBlock.CommonBufferLength = 0; + + return RegisterResourceDescriptor(Handle, + ResourceGroup, + ResourceDescriptor, + &DmaChannelInitBlock); +} + +NTSTATUS +AddResourceGroup ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ) + +{ + + ULONG ControllerId; + PCSRT_RESOURCE_DESCRIPTOR_HEADER ResourceDescriptor; + + ResourceDescriptor = NULL; + ControllerId = 0; + for (;;) { + ResourceDescriptor = + GetNextResourceDescriptor(Handle, + ResourceGroup, + ResourceDescriptor, + CSRT_RD_TYPE_DMA, + CSRT_RD_SUBTYPE_ANY, + CSRT_RD_UID_ANY); + + if (ResourceDescriptor == NULL) { + break; + } + + if (ResourceDescriptor->Subtype == CSRT_RD_SUBTYPE_DMA_CONTROLLER) { + RegisterDmaController(Handle, + ResourceGroup, + ResourceDescriptor, + &ControllerId); + + } else if (ResourceDescriptor->Subtype == CSRT_RD_SUBTYPE_DMA_CHANNEL) { + RegisterDmaChannel(Handle, + ResourceGroup, + ResourceDescriptor, + ControllerId); + } else { + + NT_ASSERT(FALSE); + } + } + + return STATUS_SUCCESS; +} diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.def b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.def new file mode 100644 index 00000000..22ecba00 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.def @@ -0,0 +1,2 @@ +LIBRARY HalExtSampleDma + diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.rc b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.rc new file mode 100644 index 00000000..534afafb --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.rc @@ -0,0 +1,10 @@ +#include "verrsrc.h" +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "HAL Extension Sample - DMA" +#define VER_INTERNALNAME_STR "HalExtSampleDma.DLL" +#define VER_ORIGINALFILENAME_STR "HalExtSampleDma.DLL" + +#include <common.ver> diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj new file mode 100644 index 00000000..4d7b340c --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Arm"> + <Configuration>Debug</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Arm"> + <Configuration>Release</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{7568DCFB-C421-478A-A809-21EF6EC243BE}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Arm</Platform> + <SampleGuid>{03525FA0-7E40-493E-AAB7-D8D73FACBC51}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetName>HalExtSampleDma</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetName>HalExtSampleDma</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleDma.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleDma.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleDma.c" /> + <ResourceCompile Include="HalExtSampleDma.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj.Filters b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj.Filters new file mode 100644 index 00000000..0e78975f --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDma.vcxproj.Filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{C49AD628-A169-4113-A58C-7869B002B124}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{4066A1BF-4F9E-4F43-B6C3-3DEEDE4A553C}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{CD82A108-349C-4687-A02C-C816B6A47307}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleDma.c"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="HalExtSampleDma.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="HalExtSampleDma.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDmaReadme.txt b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDmaReadme.txt new file mode 100644 index 00000000..05e44648 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleDma/HalExtSampleDmaReadme.txt @@ -0,0 +1,139 @@ +Copyright (c) 2011 Microsoft Corporation + +Author: Cody Hartwig (chartwig) 9-Jun-2011 + +------ + +This document describes the chDMA controller. chDMA is a 32-channel, +32-request line DMA controller for which a sample HAL extension has been +implemented. This document also describes a sample peripheral device and the +configuration required for use with the DMA controller. + +General Controller Information +============================== + +Base Physical Address 0x70001000 +GSI 0x27 +Number of Channels 32 +Number of Request Lines 32 +Cache Coherent No +Supports ReadDmaCounter Yes + + +Register Layout +=============== + +CH_DMA_CONTROL (Offset: 0x00, RW, Reset: 0x00000000) +-------------- + +This register sets controller-wide settings such as controller enable. + +Bit Function +31 Controller Enable: 1-enable, 0-disable +30-0 Reserved + +CH_DMA_STATUS (Offset: 0x04, RW1C, Reset: 0x00000000) +------------- + +This register describes which channels are currently asserting an interrupt. +Interrupts are cleared by writing 1 to the appropriate bit. + +Bit Function +31 Channel 31 interrupt status +30 Channel 30 interrupt status +.. +0 Channel 0 interrupt status + +CH_DMA_INTERRUPT_MASK (Offset: 0x08, RW, Reset: 0x00000000) +--------------------- + +A channel interrupt is unmasked by writing 1 to the corresponding bit in this +register. + +Bit Function +31 Channel 31 interrupt mask +30 Channel 30 interrupt mask +.. +0 Channel 0 interrupt mask + +CH_DMA_CHANNEL_i_CONTROL (Offset: 0x10 + (i * 0x10), RW, Reset:0x00000000) +------------------------ + +This register controls per channel operation of the controller. A transfer +may begin once the enable bit is set. When the enable bit is cleared, the +current burst is finished and the transfer will stop. + +Bit Function +31 Enable +30 Interrupt on completion +29-28 Device Width: + 0b00 - 8-bit + 0b01 - 16-bit + 0b10 - 32-bit + 0b11 - Reserved +27-26 Burst Size: + 0b00 - 1 Word + 0b01 - 2 Word + 0b10 - 4 Word + 0b11 - 8 Word +25 Flow Control: 1-enable, 0-disable +24 Loop: 1-repeat transfer, 0-transfer once +23-19 Request line trigger +18 Transfer Direction: 1-device to mem, 0-mem to device +17-16 Reserved +15-0 Transfer Length + +CH_DMA_CHANNEL_i_MEM_PTR (Offset: 0x14 + (i * 0x10), RW, Reset: 0x00000000) +------------------------ + +This register sets the memory-side address of the transfer. + +Bit Function +31-0 Memory-side address + +CH_DMA_CHANNEL_i_DEV_PTR (Offset: 0x18 + (i * 0x10), RW, Reset: 0x00000000) +------------------------ + +This register sets the device-side address of the transfer. + +Bit Function +31-0 Device-side address + +CH_DMA_CHANNEL_i_STATUS (Offset: 0x1c + (i * 0x10), RO, Reset: 0x00000000) +----------------------- + +This register reports transfer status. + +Bit Function +31 Channel Busy +30-16 Reserved +15-0 Bytes Transferred + + + +Programming Model +================= +Normal operation of the DMA controller is achieved by the following steps: + +1. Enable the controller by writing 0x80000000 to the CH_DMA_CONTROL register. +2. Unmask channel 0's interrupt by or'ing 0x1 with the CH_DMA_INTERRUPT_MASK + register +3. Program the memory-side transfer address into the CH_DMA_CHANNEL_0_MEM_PTR + register. +4. Program the device-side transfer address into the CH_DMA_CHANNEL_0_DEV_PTR + register. +5. Program the CH_DMA_CHANNEL_0_CONTROL register. This enables the channel. + + +Peripheral Information +====================== + +For the purposes of this sample, the chDMA controller is connected to a single +UAART. This UART requires the following DMA controller configuration to +work properly: + +Parameter Value +Bus Width 8 bits +Burst Size 1 Word +Request Line 0x15 +Flow Control Enabled diff --git a/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.c b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.c new file mode 100644 index 00000000..f45b32b6 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.c @@ -0,0 +1,763 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + HalExtSampleTimers.c + +Abstract: + + This file implements a HAL Extension Module for the fictitious EGTimer. + +Author: + + Evan Green (evgreen) 8-Jan-2011 + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include <nthalext.h> + +// +// -------------------------------------------------------------- Specification +// + +// +// The EGTimer is a fast-access low-latency timer designed for high-performance +// timekeeping operations. It consists of 4 timers with identical register +// definitions offset from each other. Each timer contains a 32-bit +// counter and is capable of generating one-shot or periodic interrupts using +// a "Reload Value" register. The counter counts up and generates an interrupt +// once it rolls over from its maximum value of 0xFFFFFFFF to 0 if the +// interrupt enable bit is set. The counter always runs at a frequency of +// 10MHz. The register layout of the timer appears below: +// +// Offset Register Size +// 0x00 Timer0_Control 4 +// 0x04 Timer0_ReloadValue 4 +// 0x08 Timer0_CurrentCount 4 +// 0x0C Timer0_InterruptAcknowledge 4 +// 0x10 Timer1_Control 4 +// 0x14 Timer1_ReloadValue 4 +// 0x18 Timer1_CurrentCount 4 +// 0x1C Timer1_InterruptAcknowledge 4 +// 0x20 Timer2_Control 4 +// 0x24 Timer2_ReloadValue 4 +// 0x28 Timer2_CurrentCount 4 +// 0x2C Timer2_InterruptAcknowledge 4 +// 0x30 Timer3_Control 4 +// 0x34 Timer3_ReloadValue 4 +// 0x38 Timer3_CurrentCount 4 +// 0x3C Timer3_InterruptAcknowledge 4 +// +// Register Descriptions: +// +// TimerN_Control - This is a Read/Write register containing a bitfield of +// control values that govern the timer's operation. The initial value of +// this register at reset is 0. The defined bits are listed below. 0 should +// always be written to undefined bits, and will always be read as 0. +// +// Bit Function +// 31-3 Reserved. Read as 0, always write 0 to maintain future +// compatibility. +// +// 2 Periodic. When set to 1, the timer will automatically reload +// itself with the value in the ReloadValue register. +// When set to 0, the timer's counter will be disabled by +// clearing the "Counter Enabled" bit when it overflows +// from 0xFFFFFFFF to 0. +// +// 1 Interrupt Enable. When set to 1, the timer will generate an +// interrupt when the counter overflows from 0xFFFFFFFF to 0. +// When set to 0, the timer will not generate an interrupt upon +// overflow. +// +// 0 Counter Enable. When set to 1, the counter is enabled and +// running. When set to 0, the counter is disable and will not +// run. +// +// TimerN_ReloadValue - This is a Read/Write register containing the value to +// reload the counter with when the timer is in Periodic mode and overflows +// from 0xFFFFFFFF to 0. Writing a value to this register also immediately +// writes the same value to the current count register. The initial value +// of this register at reset is 0. +// +// TimerN_CurrentCount - This is a Read/Write register containg the current +// value of the counter. This value can be overwritten at any time. It is +// also overwritten by a write to the ReloadValue register. The initial +// value of this register at reset is 0. +// +// TimerN_InterruptAcknowledge - This is a Read/Write register. On reads, it +// returns 1 if an interrupt is pending that has yet to be acknowledged. +// Writing a value of 0 clears the pending interrupt. This initial value of +// this register at reset is 0. +// + +// +// ---------------------------------------------------------------- Definitions +// + +// +// Define the total size of the register block, which is 1 page. +// + +#define EGTIMER_BLOCK_SIZE 0x1000 + +// +// Define the size of one timer's registers. +// + +#define EGTIMER_SIZE 0x10 + +// +// Define the total number of timers. +// + +#define EGTIMER_COUNT 4 + +// +// Define the timer's bit width. +// + +#define EGTIMER_BIT_WIDTH 32 + +// +// Define the counter's frequency, in Hertz. +// + +#define EGTIMER_FREQUENCY 10000000 + +// +// Define bits for the control register. +// + +#define EGTIMER_CONTROL_ENABLE 0x00000001 +#define EGTIMER_CONTROL_INTERRUPT_ENABLE 0x00000002 +#define EGTIMER_CONTROL_PERIODIC 0x00000004 + +// +// ------------------------------------------------------ Data Type Definitions +// + + +// +// Define the registers and their offsets, in ULONGs. +// + +typedef enum _EGTIMER_REGISTER { + EgTimerControl = 0, + EgTimerReloadValue = 1, + EgTimerCurrentCount = 2, + EgTimerInterruptAcknowledge = 3 +} EGTIMER_REGISTER, *PEGTIMER_REGISTER; + +// +// Define the format of the private data structure. The TimerIndex member +// allows the extension to pass the same functions for all timer an identify +// which timer is being referred to. +// + +typedef struct _EGTIMER_DATA { + ULONG TimerIndex; +} EGTIMER_DATA, *PEGTIMER_DATA; + +// +// --------------------------------------------------------------------- Macros +// + +// +// The following macros are used to read from and write to the timer. The first +// parameter is the offset from the entire timer block where this timer is +// found (which timer), and the second parameter is the register to read or +// write. For the write function, the third parameter is the value to write. +// READ_REGISTER_ULONG and WRITE_REGISTER_ULONG should always be used to +// ensure that the proper barriers and flushes are in place for doing direct +// hardware accesses. +// + +#define READ_EGTIMER(_TimerIndex, _Register) \ + READ_REGISTER_ULONG((PULONG)((PUCHAR)EgTimerBase + \ + ((_TimerIndex) * EGTIMER_SIZE)) + (_Register)) + +#define WRITE_EGTIMER(_TimerIndex, _Register, _Value) \ + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)EgTimerBase + \ + ((_TimerIndex) * EGTIMER_SIZE)) + \ + (_Register), \ + _Value) + +// +// ----------------------------------------------- Internal Function Prototypes +// + +NTSTATUS +EgTimerRegister ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ); + +_Function_class_(TIMER_INITIALIZE) +NTSTATUS +EgTimerInitialize ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_QUERY_COUNTER) +ULONGLONG +EgTimerQueryCounter ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_ACKNOWLEDGE_INTERRUPT) +VOID +EgTimerAcknowledgeInterrupt ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_ARM_TIMER) +NTSTATUS +EgTimerArm ( + __in PVOID TimerData, + __in TIMER_MODE Mode, + __in ULONGLONG TickCount + ); + +_Function_class_(TIMER_STOP) +VOID +EgTimerStop ( + __in PVOID TimerData + ); + +// +// -------------------------------------------------------------------- Globals +// + +// +// Define the physical address of the timer block. This information can either +// be hardcoded like it is here or fetched out of the CSRT resource passed to +// the extension. +// + +ULONGLONG EgTimerPhysicalAddress = 0x0BADF00D; + +// +// Define the GSIVs for each timer's interrupt. This is also a candidate for +// information to be retrieved out of the CSRT. +// + +ULONG EgTimerGsi[EGTIMER_COUNT] = { + 32, + 33, + 34, + 35 +}; + +// +// Define the mapped virtual address of the timer block. +// + +PVOID EgTimerBase = NULL; + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +AddResourceGroup ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ) + +/*++ + +Routine Description: + + This routine identifies and registers all of the Resource Descriptors + in the specified Resource Group. + +Arguments: + + Handle - Supplies the HAL Extension handle which must be passed to other + HAL Extension APIs. + + ResourceGroup - Supplies a pointer to the Resource Group which the + HAL Extension has been installed on. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + NTSTATUS Status; + + // + // Register the main timer block. + // + + Status = EgTimerRegister(Handle, ResourceGroup); + if (!NT_SUCCESS(Status)) { + goto AddResourceGroupEnd; + } + + Status = STATUS_SUCCESS; + +AddResourceGroupEnd: + return Status; +} + +// +// --------------------------------------------------------- Internal Functions +// + +NTSTATUS +EgTimerRegister ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ) + +/*++ + +Routine Description: + + This routine registers the EG Timer hardware. + +Arguments: + + Handle - Supplies the HAL Extension handle which must be passed to other + HAL Extension APIs. + + ResourceGroup - Supplies a pointer to the Resource Group which the + HAL Extension has been installed on. + +Return Value: + + NT status code. + +--*/ + +{ + + EGTIMER_DATA InternalData; + TIMER_INITIALIZATION_BLOCK NewTimer; + PHYSICAL_ADDRESS PhysicalAddress; + CSRT_RESOURCE_DESCRIPTOR_HEADER ResourceDescriptorHeader; + NTSTATUS Status; + ULONG TimerIndex; + + // + // DEV HACK: Makeup a resource type until we get correct CSRT parsing. + // + + ResourceDescriptorHeader.Type = CSRT_RD_TYPE_TIMER; + ResourceDescriptorHeader.Subtype = CSRT_RD_SUBTYPE_TIMER; + ResourceDescriptorHeader.Length = sizeof(CSRT_RESOURCE_DESCRIPTOR_HEADER); + + // + // Register the entire timer block's address usage with the HAL. This + // address space should be shown to the HAL as reserved even if the timer + // is not going to be registered or used so that the system knows that + // region of *physical* address space is occupied. + // + + PhysicalAddress.QuadPart = EgTimerPhysicalAddress; + Status = HalRegisterPermanentAddressUsage(PhysicalAddress, + EGTIMER_BLOCK_SIZE); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + + // + // Register each timer with the HAL. + // + + for (TimerIndex = 0; TimerIndex < EGTIMER_COUNT; TimerIndex += 1) { + + // + // Initialize the timer structure. + // + + RtlZeroMemory(&NewTimer, sizeof(TIMER_INITIALIZATION_BLOCK)); + RtlZeroMemory(&InternalData, sizeof(EGTIMER_DATA)); + INITIALIZE_TIMER_HEADER(&NewTimer); + NewTimer.CounterBitWidth = EGTIMER_BIT_WIDTH; + NewTimer.CounterFrequency = EGTIMER_FREQUENCY; + + // + // Set the pointer to the internal data and its size. The pointer can + // be the same for each timer (and a local variable) because a *copy* + // of this data will be made for each timer registered. This is the + // extensions only chance to dynamically allocate memory. + // + + NewTimer.InternalData = &InternalData; + NewTimer.InternalDataSize = sizeof(EGTIMER_DATA); + NewTimer.Interrupt.Mode = LevelSensitive; + NewTimer.Interrupt.Polarity = InterruptActiveHigh; + + // + // This must be set to indicate that this is a custom third-party timer. + // The HAL will fail the registration if this is not set correctly. + // + + NewTimer.KnownType = TimerUnknown; + + // + // The timer does not support a divisor. The GSI data can be hardcoded + // like it is here or pulled out of the resource from the CSRT table. + // + + NewTimer.MaxDivisor = 1; + NewTimer.Interrupt.Gsi = EgTimerGsi[TimerIndex]; + NewTimer.FunctionTable.Initialize = EgTimerInitialize; + NewTimer.FunctionTable.QueryCounter = EgTimerQueryCounter; + NewTimer.FunctionTable.AcknowledgeInterrupt = + EgTimerAcknowledgeInterrupt; + + NewTimer.FunctionTable.ArmTimer = EgTimerArm; + NewTimer.FunctionTable.Stop = EgTimerStop; + NewTimer.Capabilities = TIMER_COUNTER_READABLE | + TIMER_ONE_SHOT_CAPABLE | + TIMER_PERIODIC_CAPABLE | + TIMER_GENERATES_LINE_BASED_INTERRUPTS; + + InternalData.TimerIndex = TimerIndex; + ResourceDescriptorHeader.Uid = TimerIndex; + Status = RegisterResourceDescriptor(Handle, + ResourceGroup, + &ResourceDescriptorHeader, + &NewTimer); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + } + + Status = STATUS_SUCCESS; + +RegisterEnd: + return Status; +} + +_Function_class_(TIMER_INITIALIZE) +NTSTATUS +EgTimerInitialize ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine is responsible for initializing the timer hardware. This is + guaranteed to be the first timer routine called by the HAL. It must prepare + the timer for use by beginning the timer's counter ticking if the counter + is readable, setting the intial input clock divisor to 1 if applicable, + and masking all interrupts. If the timer's stop routine is called, this + routine will be called before the timer is queried or armed again. It will + not be called between every rearming of the timer. + + This routine will not be called concurrently with any other calls to + this HAL Timer extension. For per-processor timers, this routine will be + called once on each processor. A failure on any processor blocks the timer's + use on all processors. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context. The contents + of this pointer were specified when the timer was initially registered, + and may be modified inside this routine. The HAL does not interpret any + data deferenced from this pointer. + +Return Value: + + Returns an NT status code indicating success or failure. If a successful + status code is returned then the HAL may subsequently call further routines + in this HAL extension to query or arm the timer. If a failure code is + returned, this HAL extension will not attempt to use this timer unless the + Initialize routine is called again and succeeds. + +--*/ + +{ + + PHYSICAL_ADDRESS PhysicalAddress; + ULONG RegisterValue; + NTSTATUS Status; + PEGTIMER_DATA Timer; + + Timer = (PEGTIMER_DATA)TimerData; + + // + // Map the timer if no one has done that yet. + // + + if (EgTimerBase == NULL) { + PhysicalAddress.QuadPart = EgTimerPhysicalAddress; + EgTimerBase = HalMapIoSpace(PhysicalAddress, + EGTIMER_BLOCK_SIZE, + MmNonCached); + + if (EgTimerBase == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto InitializeEnd; + } + } + + // + // Start the counter ticking in free running mode, and mask all interrupts. + // The counter doesn't necessarily need to be reset to 0. + // + + WRITE_EGTIMER(Timer->TimerIndex, EgTimerReloadValue, 0); + RegisterValue = EGTIMER_CONTROL_ENABLE | EGTIMER_CONTROL_PERIODIC; + WRITE_EGTIMER(Timer->TimerIndex, EgTimerControl, RegisterValue); + Status = STATUS_SUCCESS; + +InitializeEnd: + return Status; +} + +_Function_class_(TIMER_QUERY_COUNTER) +ULONGLONG +EgTimerQueryCounter ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine queries the timer hardware and retrieves the current counter + value. + + Timers are assumed to always count *up*. If the actual timer hardware counts + down, then this routine should subtract the current count from the maximum + counter value so that values appear to count up. This routine may be called + concurrently on multiple processors and must be reentrant. This routine is + extremely performance sensitive, as it may be used to back the system + performance counter. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + Returns the hardware's current count. + +--*/ + +{ + + PEGTIMER_DATA Timer; + + Timer = (PEGTIMER_DATA)TimerData; + + return READ_EGTIMER(Timer->TimerIndex, EgTimerCurrentCount); +} + +_Function_class_(TIMER_ACKNOWLEDGE_INTERRUPT) +VOID +EgTimerAcknowledgeInterrupt ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine performs any actions necessary to acknowledge and quiesce a + timer interrupt. For per-processor timers, this routine may be called + concurrently on multiple processors. This routine will be called on every + timer interrupt at the hardware priority level of that interrupt, so this + routine is extremely performance sensitive. For timers running in + pseudo-periodic mode, this routine must rearm the timer for the same + interval as it was armed with without introducing delay into the interrupt + interval. Only deadline-based timers support pseudo-periodic mode. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + None. + +--*/ + +{ + + PEGTIMER_DATA Timer; + + Timer = (PEGTIMER_DATA)TimerData; + + // + // Acknowledge the interrupt by writing to the interrupt acknowledge + // register. + // + + WRITE_EGTIMER(Timer->TimerIndex, EgTimerInterruptAcknowledge, 0); + return; +} + +_Function_class_(TIMER_ARM_TIMER) +NTSTATUS +EgTimerArm ( + __in PVOID TimerData, + __in TIMER_MODE Mode, + __in ULONGLONG TickCount + ) + +/*++ + +Routine Description: + + This routine arms a timer to fire an interrupt after the given number of + timer ticks. For timers that only interrupt on rollovers, this simply + enables the interrupt, the tick count parameter is ignored. If the timer + is currently armed for a different mode or tick count, this call is + expected to replace that programming. This routine will not get called + concurrently with other timer calls, except on per-processor timers, where + it may get called concurrently on different processors. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + + Mode - Supplies the mode to arm the timer in, which will be one of the + modes the HAL extension advertised support for when registering the + timer. The modes are as follows: + + OneShot - Arms the timer to fire the given number of ticks from now. + Only one interrupt is expected to come in. The HAL does not make + assumptions on whether or not the expiration of this interrupt + causes the counter to stop. The Query Counter routine will not be + called while a timer is armed to fire an interrupt. + + Periodic - Arms the timer to fire periodically with an interval of the + given number of ticks. The first interrupt should happen + approximately the given number of ticks from when the arm timer + function was invoked. + + PseudoPeriodic - Arms the timer with the same functional behavior as + periodic mode, with the knowledge that the timer will have to rearm + itself during the acknowledge interrupt routine. This mode is + expected to have slightly worse performance than pure periodic mode, + but is expected to generate periodic interrupts at the exact rate + specified. + + TickCount - Supplies the number of ticks from now that the timer should + assert its interrupt in. For timers that are only capable of + interrupting on rollovers from their maximum value to 0, this parameter + is ignored. + +Return Value: + + Returns and NTSTATUS code indicating success or failure. If the timer + returns success, then the interrupt is expected to come in the specified + number of ticks from when the function was called, with a tolerance of + however long the function took to execute. If the routine fails, then no + timer routines will be called again until the timer Initialize routine is + called again and succeeds. In most cases, returning a failure code results + in a system bugcheck. + +--*/ + +{ + + ULONG ControlRegister; + PEGTIMER_DATA Timer; + + Timer = (PEGTIMER_DATA)TimerData; + + NT_ASSERT(TickCount != 0); + NT_ASSERT(TickCount <= 0xFFFFFFFF); + NT_ASSERT(Mode != TimerModePseudoPeriodic); + + // + // This will never occur. + // + + if ((TickCount > 0xFFFFFFFF) || (TickCount == 0)) { + return STATUS_INVALID_PARAMETER; + } + + ControlRegister = EGTIMER_CONTROL_ENABLE | EGTIMER_CONTROL_INTERRUPT_ENABLE; + if (Mode == TimerModePeriodic) { + ControlRegister |= EGTIMER_CONTROL_PERIODIC; + } + + // + // Disable the timer while it's being programmed to avoid spurious + // interrupts. + // + + WRITE_EGTIMER(Timer->TimerIndex, EgTimerControl, 0); + + // + // Write the reload register to set both the current count value and the + // reload value if the interrupt is periodic. + // + + WRITE_EGTIMER(Timer->TimerIndex, + EgTimerReloadValue, + 0 - (ULONG)TickCount); + + // + // Enable the timer. + // + + WRITE_EGTIMER(Timer->TimerIndex, EgTimerControl, ControlRegister); + return STATUS_SUCCESS; +} + +_Function_class_(TIMER_STOP) +VOID +EgTimerStop ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine stops a timer from ticking. After this function returns, the + timer should not generate any more interrupts, and reads to its counter + might return the same value every time. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + None, this function must succeed. + +--*/ + +{ + + PEGTIMER_DATA Timer; + + Timer = (PEGTIMER_DATA)TimerData; + + // + // All that technically needs to be done to stop the timer from firing is + // to clear the interrupt enable bit. Stopping the timer entirely works too. + // + + WRITE_EGTIMER(Timer->TimerIndex, EgTimerControl, 0); + return; +} + diff --git a/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.def b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.def new file mode 100644 index 00000000..786620d2 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.def @@ -0,0 +1,2 @@ +LIBRARY HalExtSampleTimers + diff --git a/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.rc b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.rc new file mode 100644 index 00000000..89069fbd --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.rc @@ -0,0 +1,10 @@ +#include "verrsrc.h" +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "HAL Extension Sample - Timers" +#define VER_INTERNALNAME_STR "HalExtSampleTimers.DLL" +#define VER_ORIGINALFILENAME_STR "HalExtSampleTimers.DLL" + +#include <common.ver> diff --git a/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj new file mode 100644 index 00000000..f03fae0d --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Arm"> + <Configuration>Debug</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Arm"> + <Configuration>Release</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{708FC03F-4440-4AF4-B68F-5ADE99A0690B}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Arm</Platform> + <SampleGuid>{74DA088F-D0F8-4839-9BFF-3C72F7D46E64}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetName>HalExtSampleTimers</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetName>HalExtSampleTimers</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleTimers.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleTimers.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleTimers.c" /> + <ResourceCompile Include="HalExtSampleTimers.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj.Filters b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj.Filters new file mode 100644 index 00000000..3948916a --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers/HalExtSampleTimers.vcxproj.Filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{5E4E1ADC-DD92-4AFF-B021-09A73A1D08DF}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{E29262B9-C497-404D-A5E0-5E6064005E48}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{E3A98D78-1B54-4D53-AFC0-D44C9D37EC38}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleTimers.c"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="HalExtSampleTimers.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="HalExtSampleTimers.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.c b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.c new file mode 100644 index 00000000..9a580fea --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.c @@ -0,0 +1,825 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + HalExtSampleTimers2.c + +Abstract: + + This file implements a HAL Extension Module for the fictitious EG2Timer. + +Author: + + Evan Green (evgreen) 8-Jan-2011 + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include <nthalext.h> + +// +// -------------------------------------------------------------- Specification +// + +// +// The EG2Timer is a fast-access low latency deadline-based timer designed +// for high-performance timekeeping operations. It consists of one counter +// counter and three match registers against that counter. Each match register +// consists of a 32-bit value that is compared on each clock cycle to the +// current count value. If the values match and the match register is enabled +// for generating an interrupt, an edge triggered interrupt will be fired. The +// register can also be configured with an "interval" value such that the +// match register is automatically moved forward by the given interval to +// generate periodic interrupts. The main counter is 32 bits and runs at 15MHz. +// The register layout of the timer block appears below. +// +// Offset Register Size +// 0x00 GlobalControl 4 +// 0x04 CounterValue 4 +// 0x08 Timer0_Match 4 +// 0x0C Timer0_Interval 4 +// 0x10 Timer0_Control 4 +// 0x14 Timer1_Match 4 +// 0x18 Timer1_Interval 4 +// 0x1C Timer1_Control 4 +// 0x20 Timer2_Match 4 +// 0x24 Timer2_Interval 4 +// 0x28 Timer2_Control 4 +// +// Register Descriptions: +// +// GlobalControl - Controls global state relating to the timer block. On reset, +// this register's value is 0. This register is Read/Write. +// +// Bit Function +// 31-1 Reserved. Read as 0, always write 0 to maintain future +// compatibility. +// +// 0 Enabled. When set to 1, the main counter is enabled and will +// count. When set to 0, the main counter is disabled and will +// not run. +// +// CounterValue - A Read/Write register containing the current value of the +// counter. Writes to this register must be done with caution as they do +// not alter or adjust the contents of the match registers. On reset, the +// value of this register is 0. +// +// TimerN_Match - A Read/Write register that contains the match value that this +// interrupt is primed against. When the global counter equals the match +// value, an interrupt will be generated. If the interrupt is set for +// periodic mode, the Interval value will be automatically added to the +// match value when the interrupt occurs. On reset, the value of this +// register is 0. +// +// TimerN_Interval - A Read/Write register that contains the periodic interval +// to add to the match register if the interrupt is armed for periodic +// mode. On reset, the value of this register is 0. +// +// TimerN_Control - A Read/Write register containing a bitfield that controls +// the behavior of the match register and associated interrupt. On reset, +// the value of this register is 0. +// +// Bit Function +// 31-2 Reserved. Read as 0, always write 0 to maintain future +// compatibility. +// +// 1 Periodic. When set to 1, the value in the Interval register will +// automatically be added to the value in the Match register and +// written back to the Match register when a match occurs. When +// set to 0, the Match register will not change when a match +// occurs. +// +// 0 InterruptEnable. When set to 1, an interrupt will be generated +// when a match occurs. When set to 0, no interrupt will be +// generated when a match occurs. The periodic bit is still live +// however, the Match register will continue to get accumulated +// with the Interval register on matches. +// + +// +// ---------------------------------------------------------------- Definitions +// + +// +// Define the total size of the register block, which is 1 page. +// + +#define EG2TIMER_BLOCK_SIZE 0x1000 + +// +// Define the size of one timer's match register block, in ULONGs. +// + +#define EG2MATCH_SIZE 3 + +// +// Define the total number of match registers. +// + +#define EG2MATCH_COUNT 3 + +// +// Define the timer's bit width. +// + +#define EG2TIMER_BIT_WIDTH 32 + +// +// Define the counter's frequency, in Hertz. +// + +#define EG2TIMER_FREQUENCY 15000000 + +// +// Define the global control bits. +// + +#define EG2TIMER_GLOBAL_CONTROL_ENABLE 0x00000001 + +// +// Define bits for the control register. +// + +#define EG2TIMER_MATCH_INTERRUPT_ENABLE 0x00000001 +#define EG2TIMER_MATCH_PERIODIC 0x00000002 + +// +// Define the special offset used to indicate this timer is the counter +// itself. +// + +#define EG2TIMER_COUNTER_OFFSET 0xFFFFFFFF + +// +// ------------------------------------------------------ Data Type Definitions +// + + +// +// Define the registers and their offsets, in ULONGs. +// + +typedef enum _EG2TIMER_REGISTER { + Eg2TimerGlobalControl = 0, + Eg2TimerCurrentCount = 1, + Eg2TimerMatch = 2, + Eg2TimerInterval = 3, + Eg2TimerControl = 4 +} EG2TIMER_REGISTER, *PEG2TIMER_REGISTER; + +// +// Define the format of the private data structure. The offset member stores the +// offset to the match register, in ULONGs. The value 0 is reserved for the +// global counter. +// + +typedef struct _EG2TIMER_DATA { + ULONG Offset; + TIMER_MODE Mode; + ULONG Period; +} EG2TIMER_DATA, *PEG2TIMER_DATA; + +// +// --------------------------------------------------------------------- Macros +// + +// +// The following macros are used to read from and write to the timer. The first +// parameter is the offset in ULONGs to apply to the requested register. The +// second parameter is the register to read or write. For write functions, the +// third parameter is the value to write. +// +// READ_REGISTER_ULONG and WRITE_REGISTER_ULONG should always be used to +// ensure that the proper barriers and flushes are in place for doing direct +// hardware accesses. +// + +#define READ_EG2TIMER(_TimerOffset, _Register) \ + READ_REGISTER_ULONG((PULONG)Eg2TimerBase + (_TimerOffset) + (_Register)) + +#define WRITE_EG2TIMER(_TimerOffset, _Register, _Value) \ + WRITE_REGISTER_ULONG((PULONG)Eg2TimerBase + (_TimerOffset) + (_Register), \ + (_Value)) + +// +// ----------------------------------------------- Internal Function Prototypes +// + +NTSTATUS +Eg2TimerRegister ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ); + +_Function_class_(TIMER_INITIALIZE) +NTSTATUS +Eg2TimerInitialize ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_QUERY_COUNTER) +ULONGLONG +Eg2TimerQueryCounter ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_ACKNOWLEDGE_INTERRUPT) +VOID +Eg2TimerAcknowledgeInterrupt ( + __in PVOID TimerData + ); + +_Function_class_(TIMER_ARM_TIMER) +NTSTATUS +Eg2TimerArm ( + __in PVOID TimerData, + __in TIMER_MODE Mode, + __in ULONGLONG TickCount + ); + +_Function_class_(TIMER_STOP) +VOID +Eg2TimerStop ( + __in PVOID TimerData + ); + +// +// -------------------------------------------------------------------- Globals +// + +// +// Define the physical address of the timer block. This information can either +// be hardcoded like it is here or fetched out of the CSRT resource passed to +// the extension. +// + +ULONGLONG Eg2TimerPhysicalAddress = 0xBEEF7AC0; + +// +// Define the GSIVs for each timer's interrupt. This is also a candidate for +// information to be retrieved out of the CSRT. +// + +ULONG Eg2TimerGsi[EG2MATCH_COUNT] = { + 40, + 41, + 42, +}; + +// +// Define the mapped virtual address of the timer block. +// + +PVOID Eg2TimerBase = NULL; + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +AddResourceGroup ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ) + +/*++ + +Routine Description: + + This routine identifies and registers all of the Resource Descriptors + in the specified Resource Group. + +Arguments: + + Handle - Supplies the HAL Extension handle which must be passed to other + HAL Extension APIs. + + ResourceGroup - Supplies a pointer to the Resource Group which the + HAL Extension has been installed on. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + NTSTATUS Status; + + // + // Register the main timer block. + // + + Status = Eg2TimerRegister(Handle, ResourceGroup); + if (!NT_SUCCESS(Status)) { + goto AddResourceGroupEnd; + } + + Status = STATUS_SUCCESS; + +AddResourceGroupEnd: + return Status; +} + +// +// --------------------------------------------------------- Internal Functions +// + +NTSTATUS +Eg2TimerRegister ( + __in ULONG Handle, + __in PCSRT_RESOURCE_GROUP_HEADER ResourceGroup + ) + +/*++ + +Routine Description: + + This routine registers the EG2 Timer hardware. + +Arguments: + + Handle - Supplies the HAL Extension handle which must be passed to other + HAL Extension APIs. + + ResourceGroup - Supplies a pointer to the Resource Group which the + HAL Extension has been installed on. + +Return Value: + + NT status code. + +--*/ + +{ + + EG2TIMER_DATA InternalData; + TIMER_INITIALIZATION_BLOCK NewTimer; + PHYSICAL_ADDRESS PhysicalAddress; + CSRT_RESOURCE_DESCRIPTOR_HEADER ResourceDescriptorHeader; + NTSTATUS Status; + ULONG TimerIndex; + + // + // DEV HACK: Makeup a resource type until we get correct CSRT parsing. + // + + ResourceDescriptorHeader.Type = CSRT_RD_TYPE_TIMER; + ResourceDescriptorHeader.Subtype = CSRT_RD_SUBTYPE_TIMER; + ResourceDescriptorHeader.Length = sizeof(CSRT_RESOURCE_DESCRIPTOR_HEADER); + + // + // Register the entire timer block's address usage with the HAL. This + // address space should be shown to the HAL as reserved even if the timer + // is not going to be registered or used so that the system knows that + // region of *physical* address space is occupied. + // + + PhysicalAddress.QuadPart = Eg2TimerPhysicalAddress; + Status = HalRegisterPermanentAddressUsage(PhysicalAddress, + EG2TIMER_BLOCK_SIZE); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + + // + // Register the main counter as a non-interrupt generating timer, as it can + // be used completely independently of the match registers as long as it + // is never written to. + // + + RtlZeroMemory(&NewTimer, sizeof(TIMER_INITIALIZATION_BLOCK)); + RtlZeroMemory(&InternalData, sizeof(EG2TIMER_DATA)); + INITIALIZE_TIMER_HEADER(&NewTimer); + NewTimer.CounterBitWidth = EG2TIMER_BIT_WIDTH; + NewTimer.CounterFrequency = EG2TIMER_FREQUENCY; + + // + // Set the pointer to the internal data and its size. The pointer can + // be the same for each timer (and a local variable) because a *copy* + // of this data will be made for each timer registered. This is the + // extensions only chance to dynamically allocate memory. + // + + NewTimer.InternalData = &InternalData; + NewTimer.InternalDataSize = sizeof(EG2TIMER_DATA); + NewTimer.Interrupt.Mode = Latched; + NewTimer.Interrupt.Polarity = InterruptActiveHigh; + + // + // This must be set to indicate that this is a custom third-party timer. + // The HAL will fail the registration if this is not set correctly. + // + + NewTimer.KnownType = TimerUnknown; + + // + // The timer does not support a divisor. The GSI data can be hardcoded + // like it is here or pulled out of the resource from the CSRT table. + // Filling in extra functions doesn't hurt as the HAL will never call + // anything but Initialize and QueryCounter on timers that don't + // generate interrupts. + // + + NewTimer.MaxDivisor = 1; + NewTimer.FunctionTable.Initialize = Eg2TimerInitialize; + NewTimer.FunctionTable.QueryCounter = Eg2TimerQueryCounter; + NewTimer.FunctionTable.AcknowledgeInterrupt = Eg2TimerAcknowledgeInterrupt; + NewTimer.FunctionTable.ArmTimer = Eg2TimerArm; + NewTimer.FunctionTable.Stop = Eg2TimerStop; + NewTimer.Capabilities = TIMER_COUNTER_READABLE; + InternalData.Offset = EG2TIMER_COUNTER_OFFSET; + ResourceDescriptorHeader.Uid = EG2TIMER_COUNTER_OFFSET; + Status = RegisterResourceDescriptor(Handle, + ResourceGroup, + &ResourceDescriptorHeader, + &NewTimer); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + + + // + // Register each match register as a separate non-readable timer with the + // HAL. Since this timer is deadline-based, it can do pseudo-periodic + // mode and lossless rate transitions. + // + + NewTimer.Capabilities = TIMER_ONE_SHOT_CAPABLE | + TIMER_PERIODIC_CAPABLE | + TIMER_PSEUDO_PERIODIC_CAPABLE | + TIMER_GENERATES_LINE_BASED_INTERRUPTS; + + for (TimerIndex = 0; TimerIndex < EG2MATCH_COUNT; TimerIndex += 1) { + NewTimer.Interrupt.Gsi = Eg2TimerGsi[TimerIndex]; + InternalData.Offset = EG2MATCH_SIZE * TimerIndex; + ResourceDescriptorHeader.Uid = EG2MATCH_SIZE * TimerIndex; + Status = RegisterResourceDescriptor(Handle, + ResourceGroup, + &ResourceDescriptorHeader, + &NewTimer); + + if (!NT_SUCCESS(Status)) { + goto RegisterEnd; + } + } + + Status = STATUS_SUCCESS; + +RegisterEnd: + return Status; +} + +_Function_class_(TIMER_INITIALIZE) +NTSTATUS +Eg2TimerInitialize ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine is responsible for initializing the timer hardware. This is + guaranteed to be the first timer routine called by the HAL. It must prepare + the timer for use by beginning the timer's counter ticking if the counter + is readable, setting the intial input clock divisor to 1 if applicable, + and masking all interrupts. If the timer's stop routine is called, this + routine will be called before the timer is queried or armed again. It will + not be called between every rearming of the timer. + + This routine will not be called concurrently with any other calls to + this HAL Timer extension. For per-processor timers, this routine will be + called once on each processor. A failure on any processor blocks the timer's + use on all processors. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context. The contents + of this pointer were specified when the timer was initially registered, + and may be modified inside this routine. The HAL does not interpret any + data deferenced from this pointer. + +Return Value: + + Returns an NT status code indicating success or failure. If a successful + status code is returned then the HAL may subsequently call further routines + in this HAL extension to query or arm the timer. If a failure code is + returned, this HAL extension will not attempt to use this timer unless the + Initialize routine is called again and succeeds. + +--*/ + +{ + + PHYSICAL_ADDRESS PhysicalAddress; + NTSTATUS Status; + PEG2TIMER_DATA Timer; + + Timer = (PEG2TIMER_DATA)TimerData; + + // + // Map the timer if no one has done that yet. + // + + if (Eg2TimerBase == NULL) { + PhysicalAddress.QuadPart = Eg2TimerPhysicalAddress; + Eg2TimerBase = HalMapIoSpace(PhysicalAddress, + EG2TIMER_BLOCK_SIZE, + MmNonCached); + + if (Eg2TimerBase == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto InitializeEnd; + } + } + + // + // Start the counter ticking in free running mode, and mask all interrupts. + // The counter must *not* be reset here, otherwise an Initialize call on the + // counter would affect the match register timers, which as far as the HAL + // is concerned are completely independent from one another. + // + + WRITE_EG2TIMER(0, Eg2TimerGlobalControl, EG2TIMER_GLOBAL_CONTROL_ENABLE); + if (Timer->Offset != EG2TIMER_COUNTER_OFFSET) { + WRITE_EG2TIMER(Timer->Offset, Eg2TimerControl, 0); + } + + Status = STATUS_SUCCESS; + +InitializeEnd: + return Status; +} + +_Function_class_(TIMER_QUERY_COUNTER) +ULONGLONG +Eg2TimerQueryCounter ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine queries the timer hardware and retrieves the current counter + value. + + Timers are assumed to always count *up*. If the actual timer hardware counts + down, then this routine should subtract the current count from the maximum + counter value so that values appear to count up. This routine may be called + concurrently on multiple processors and must be reentrant. This routine is + extremely performance sensitive, as it may be used to back the system + performance counter. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + Returns the hardware's current count. + +--*/ + +{ + + PEG2TIMER_DATA Timer; + + Timer = (PEG2TIMER_DATA)TimerData; + + NT_ASSERT(Timer->Offset == EG2TIMER_COUNTER_OFFSET); + + return READ_EG2TIMER(Timer->Offset, Eg2TimerCurrentCount); +} + +_Function_class_(TIMER_ACKNOWLEDGE_INTERRUPT) +VOID +Eg2TimerAcknowledgeInterrupt ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine performs any actions necessary to acknowledge and quiesce a + timer interrupt. For per-processor timers, this routine may be called + concurrently on multiple processors. This routine will be called on every + timer interrupt at the hardware priority level of that interrupt, so this + routine is extremely performance sensitive. For timers running in + pseudo-periodic mode, this routine must rearm the timer for the same + interval as it was armed with without introducing delay into the interrupt + interval. Only deadline-based timers support pseudo-periodic mode. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + None. + +--*/ + +{ + + ULONG MatchValue; + PEG2TIMER_DATA Timer; + + // + // No action is necessary here as far as acknowledging the interrupt. + // If the current mode is pseudo-periodic, the next interrupt must be + // armed now. + // + + Timer = (PEG2TIMER_DATA)TimerData; + + NT_ASSERT(Timer->Offset != EG2TIMER_COUNTER_OFFSET); + NT_ASSERT(Timer->Mode != TimerModeInvalid); + + if (Timer->Mode == TimerModePseudoPeriodic) { + + NT_ASSERT(Timer->Period != 0); + + // + // Read the deadline that just passed, add the period, and then write + // the new deadline. + // + + MatchValue = READ_EG2TIMER(Timer->Offset, Eg2TimerMatch); + MatchValue += Timer->Period; + WRITE_EG2TIMER(Timer->Offset, Eg2TimerMatch, MatchValue); + } + + return; +} + +_Function_class_(TIMER_ARM_TIMER) +NTSTATUS +Eg2TimerArm ( + __in PVOID TimerData, + __in TIMER_MODE Mode, + __in ULONGLONG TickCount + ) + +/*++ + +Routine Description: + + This routine arms a timer to fire an interrupt after the given number of + timer ticks. For timers that only interrupt on rollovers, this simply + enables the interrupt, the tick count parameter is ignored. If the timer + is currently armed for a different mode or tick count, this call is + expected to replace that programming. This routine will not get called + concurrently with other timer calls, except on per-processor timers, where + it may get called concurrently on different processors. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + + Mode - Supplies the mode to arm the timer in, which will be one of the + modes the HAL extension advertised support for when registering the + timer. The modes are as follows: + + OneShot - Arms the timer to fire the given number of ticks from now. + Only one interrupt is expected to come in. The HAL does not make + assumptions on whether or not the expiration of this interrupt + causes the counter to stop. The Query Counter routine will not be + called while a timer is armed to fire an interrupt. + + Periodic - Arms the timer to fire periodically with an interval of the + given number of ticks. The first interrupt should happen + approximately the given number of ticks from when the arm timer + function was invoked. + + PseudoPeriodic - Arms the timer with the same functional behavior as + periodic mode, with the knowledge that the timer will have to rearm + itself during the acknowledge interrupt routine. This mode is + expected to have slightly worse performance than pure periodic mode, + but is expected to generate periodic interrupts at the exact rate + specified. + + TickCount - Supplies the number of ticks from now that the timer should + assert its interrupt in. For timers that are only capable of + interrupting on rollovers from their maximum value to 0, this parameter + is ignored. + +Return Value: + + Returns and NTSTATUS code indicating success or failure. If the timer + returns success, then the interrupt is expected to come in the specified + number of ticks from when the function was called, with a tolerance of + however long the function took to execute. If the routine fails, then no + timer routines will be called again until the timer Initialize routine is + called again and succeeds. In most cases, returning a failure code results + in a system bugcheck. + +--*/ + +{ + + ULONG ControlRegister; + ULONG MatchValue; + PEG2TIMER_DATA Timer; + + Timer = (PEG2TIMER_DATA)TimerData; + + NT_ASSERT(TickCount != 0); + NT_ASSERT(TickCount <= 0xFFFFFFFF); + NT_ASSERT(Timer->Offset != EG2TIMER_COUNTER_OFFSET); + + // + // This will never occur. + // + + if ((TickCount > 0xFFFFFFFF) || (TickCount == 0)) { + return STATUS_INVALID_PARAMETER; + } + + // + // Disable the timer while it's being programmed to avoid spurious + // interrupts. + // + + WRITE_EG2TIMER(Timer->Offset, Eg2TimerControl, 0); + ControlRegister = EG2TIMER_MATCH_INTERRUPT_ENABLE; + + // + // For periodic mode, set the periodic interval register. + // + + if (Mode == TimerModePeriodic) { + ControlRegister |= EG2TIMER_MATCH_PERIODIC; + WRITE_EG2TIMER(Timer->Offset, Eg2TimerInterval, (ULONG)TickCount); + } + + Timer->Mode = Mode; + Timer->Period = (ULONG)TickCount; + + // + // Calculate and write in the first match value. + // + + MatchValue = READ_EG2TIMER(Timer->Offset, Eg2TimerCurrentCount); + MatchValue += (ULONG)TickCount; + WRITE_EG2TIMER(Timer->Offset, Eg2TimerMatch, MatchValue); + + // + // Enable the interrupt. + // + + WRITE_EG2TIMER(Timer->Offset, Eg2TimerControl, ControlRegister); + return STATUS_SUCCESS; +} + +_Function_class_(TIMER_STOP) +VOID +Eg2TimerStop ( + __in PVOID TimerData + ) + +/*++ + +Routine Description: + + This routine stops a timer from ticking. After this function returns, the + timer should not generate any more interrupts, and reads to its counter + might return the same value every time. + +Arguments: + + TimerData - Supplies a pointer to the timer's private context, whose + initial content was supplied when the timer was registered. + +Return Value: + + None, this function must succeed. + +--*/ + +{ + + PEG2TIMER_DATA Timer; + + Timer = (PEG2TIMER_DATA)TimerData; + + // + // Clear the interrupt enable bit. + // + + WRITE_EG2TIMER(Timer->Offset, Eg2TimerControl, 0); + return; +} diff --git a/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.def b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.def new file mode 100644 index 00000000..685129d2 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.def @@ -0,0 +1,2 @@ +LIBRARY HalExtSampleTimers2 + diff --git a/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.rc b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.rc new file mode 100644 index 00000000..c82378e0 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.rc @@ -0,0 +1,10 @@ +#include "verrsrc.h" +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "HAL Extension Sample - Timers 2" +#define VER_INTERNALNAME_STR "HalExtSampleTimers2.DLL" +#define VER_ORIGINALFILENAME_STR "HalExtSampleTimers2.DLL" + +#include <common.ver> diff --git a/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj new file mode 100644 index 00000000..dc581c00 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Arm"> + <Configuration>Debug</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Arm"> + <Configuration>Release</Configuration> + <Platform>Arm</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{A99E0448-5F6C-42B3-99AA-C43464989418}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Arm</Platform> + <SampleGuid>{E9FE899A-F0DB-43B5-8DFD-65D5D790DD76}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">HalExtensionInit@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">HalExtensionInit</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <TargetName>HalExtSampleTimers2</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <TargetName>HalExtSampleTimers2</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleTimers2.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Arm'"> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /J</AdditionalOptions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);halextlib.lib;libcntpr.lib;bufferoverflowfastfailk.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) -merge:PAGECONST=PAGE -merge:INITCONST=INIT /LARGEADDRESSAWARE</AdditionalOptions> + <ModuleDefinitionFile>HalExtSampleTimers2.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleTimers2.c" /> + <ResourceCompile Include="HalExtSampleTimers2.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj.Filters b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj.Filters new file mode 100644 index 00000000..d752a638 --- /dev/null +++ b/general/HalExtensionSample/HalExtSampleTimers2/HalExtSampleTimers2.vcxproj.Filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{CE019AA9-89F5-4A8E-87D1-ED585767F789}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{EF3411D2-5BFE-41A9-906C-0E6611142568}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{07F31510-2D3D-4110-85A6-F39922948329}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="HalExtSampleTimers2.c"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="HalExtSampleTimers2.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="HalExtSampleTimers2.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/general/HalExtensionSample/HalExtensionSample.sln b/general/HalExtensionSample/HalExtensionSample.sln new file mode 100644 index 00000000..b284bd36 --- /dev/null +++ b/general/HalExtensionSample/HalExtensionSample.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HalExtSampleDma", "HalExtSampleDma", "{C258909E-9676-4DCA-AB5E-4134876CC0F1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HalExtSampleTimers", "HalExtSampleTimers", "{2F891F34-1182-4F77-8923-AF73DF4844C2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HalExtSampleTimers2", "HalExtSampleTimers2", "{20ADC56F-63BE-4671-81B1-D2AC009F7E3C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HalExtSampleDma", "HalExtSampleDma\HalExtSampleDma.vcxproj", "{7568DCFB-C421-478A-A809-21EF6EC243BE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HalExtSampleTimers", "HalExtSampleTimers\HalExtSampleTimers.vcxproj", "{708FC03F-4440-4AF4-B68F-5ADE99A0690B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HalExtSampleTimers2", "HalExtSampleTimers2\HalExtSampleTimers2.vcxproj", "{A99E0448-5F6C-42B3-99AA-C43464989418}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Arm = Debug|Arm + Release|Arm = Release|Arm + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7568DCFB-C421-478A-A809-21EF6EC243BE}.Debug|Arm.ActiveCfg = Debug|Arm + {7568DCFB-C421-478A-A809-21EF6EC243BE}.Debug|Arm.Build.0 = Debug|Arm + {7568DCFB-C421-478A-A809-21EF6EC243BE}.Release|Arm.ActiveCfg = Release|Arm + {7568DCFB-C421-478A-A809-21EF6EC243BE}.Release|Arm.Build.0 = Release|Arm + {708FC03F-4440-4AF4-B68F-5ADE99A0690B}.Debug|Arm.ActiveCfg = Debug|Arm + {708FC03F-4440-4AF4-B68F-5ADE99A0690B}.Debug|Arm.Build.0 = Debug|Arm + {708FC03F-4440-4AF4-B68F-5ADE99A0690B}.Release|Arm.ActiveCfg = Release|Arm + {708FC03F-4440-4AF4-B68F-5ADE99A0690B}.Release|Arm.Build.0 = Release|Arm + {A99E0448-5F6C-42B3-99AA-C43464989418}.Debug|Arm.ActiveCfg = Debug|Arm + {A99E0448-5F6C-42B3-99AA-C43464989418}.Debug|Arm.Build.0 = Debug|Arm + {A99E0448-5F6C-42B3-99AA-C43464989418}.Release|Arm.ActiveCfg = Release|Arm + {A99E0448-5F6C-42B3-99AA-C43464989418}.Release|Arm.Build.0 = Release|Arm + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7568DCFB-C421-478A-A809-21EF6EC243BE} = {C258909E-9676-4DCA-AB5E-4134876CC0F1} + {708FC03F-4440-4AF4-B68F-5ADE99A0690B} = {2F891F34-1182-4F77-8923-AF73DF4844C2} + {A99E0448-5F6C-42B3-99AA-C43464989418} = {20ADC56F-63BE-4671-81B1-D2AC009F7E3C} + EndGlobalSection +EndGlobal |
