_Use_decl_annotations_ NTSTATUS PrepareHardware( WDFDEVICE Device, WDFCMRESLIST ResourceList, WDFCMRESLIST ResourceListTranslated ) /*++ Routine Description: PrepareHardware parses the device resource description and assigns default values in device context. Arguments: Device - Pointer to the device object ResourceList - Pointer to the devices resource list ResourceListTranslated - Ponter to the translated resource list of the device Return Value: Status --*/ { PAGED_CODE(); UNREFERENCED_PARAMETER(ResourceList); PDEVICE_CONTEXT deviceContext; NTSTATUS status = STATUS_SUCCESS; ULONG resourceCount; ULONG memoryResources = 0; ULONG interruptResources = 0; ULONG dmaResources = 0; deviceContext = GetContext(Device); resourceCount = WdfCmResourceListGetCount(ResourceListTranslated); for (ULONG i = 0; i < resourceCount && NT_SUCCESS(status); ++i) { PCM_PARTIAL_RESOURCE_DESCRIPTOR res; res = WdfCmResourceListGetDescriptor(ResourceListTranslated, i); switch (res->Type) { case CmResourceTypeMemory: if (memoryResources == 0) { // // DMA channel registers // if (res->u.Memory.Length < sizeof(DMA_CHANNEL_REGS)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "DMA channel memory region too small (start:0x%llX, length:0x%X)", res->u.Memory.Start.QuadPart, res->u.Memory.Length ); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } deviceContext->dmaChannelRegs = (PDMA_CHANNEL_REGS)MmMapIoSpaceEx( res->u.Memory.Start, res->u.Memory.Length, PAGE_READWRITE | PAGE_NOCACHE ); if (deviceContext->dmaChannelRegs == NULL) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Unable to map DMA channel registers."); status = STATUS_INSUFFICIENT_RESOURCES; break; } deviceContext->dmaChannelRegsPa = res->u.Memory.Start; } else if (memoryResources == 1) { // // PWM registers // if (res->u.Memory.Length < sizeof(PWM_REGS)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "PWM control register memory region too small (start:0x%llX, length:0x%X)", res->u.Memory.Start.QuadPart, res->u.Memory.Length ); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } deviceContext->pwmRegs = (PPWM_REGS)MmMapIoSpaceEx( res->u.Memory.Start, res->u.Memory.Length, PAGE_READWRITE | PAGE_NOCACHE ); if (deviceContext->pwmRegs == NULL) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Unable to map PWM control registers."); status = STATUS_INSUFFICIENT_RESOURCES; break; } deviceContext->pwmRegsPa = res->u.Memory.Start; } else if (memoryResources == 2) { // // PWM control registers bus address // deviceContext->pwmRegsBusPa = res->u.Memory.Start; } else if (memoryResources == 3) { // // PWM control registers uncached address // PHYSICAL_ADDRESS pa = res->u.Memory.Start; deviceContext->memUncachedOffset = pa.LowPart - deviceContext->pwmRegsPa.LowPart; } else if (memoryResources == 4) { // // PWM clock registers // if (res->u.Memory.Length < sizeof(CM_PWM_REGS)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "PWM clock register memory region too small (start:0x%llX, length:0x%X)", res->u.Memory.Start.QuadPart, res->u.Memory.Length ); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } deviceContext->cmPwmRegs = (PCM_PWM_REGS)MmMapIoSpaceEx( res->u.Memory.Start, res->u.Memory.Length, PAGE_READWRITE | PAGE_NOCACHE ); if (deviceContext->cmPwmRegs == NULL) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Unable to map PWM clock registers."); status = STATUS_INSUFFICIENT_RESOURCES; break; } deviceContext->cmPwmRegsPa = res->u.Memory.Start; } else { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Too many ACPI memory entries. Only 5 memory entries are allowed. Please verify ACPI configuration."); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } memoryResources++; break; case CmResourceTypeInterrupt: // // Interrupt for used DMA channel. No setup required. // interruptResources++; break; case CmResourceTypeDma: // // Get the used DMA channel. // deviceContext->dmaChannel = res->u.DmaV3.Channel; deviceContext->dmaDreq = res->u.DmaV3.RequestLine; deviceContext->dmaTransferWidth = (DMA_WIDTH)res->u.DmaV3.TransferWidth; // // Sanity check DREQ, transfer width. // if (deviceContext->dmaDreq != DMA_DREQ_PWM) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "PWM DREQ configuration invalid (DREQ:%d)", res->u.DmaV3.RequestLine); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } if (deviceContext->dmaTransferWidth != Width32Bits) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "PWM DMA transfer width setting (width setting:%d)", (ULONG)res->u.DmaV3.TransferWidth); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } // // Allocate and initialize contiguous DMA buffer logic. // status = AllocateDmaBuffer(deviceContext); if (NT_ERROR(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Error allocating DMA buffer (0x%08x)", status); } dmaResources++; break; default: TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Resource type not allowed for PWM. Please verify ACPI configuration."); status = STATUS_DEVICE_CONFIGURATION_ERROR; break; } } // // Sanity check ACPI resources. // if (memoryResources != 5) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Too less ACPI memory entries. 5 memory entries are required. Please verify ACPI configuration."); status = STATUS_DEVICE_CONFIGURATION_ERROR; } if (interruptResources != 1) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Exactly 1 interrupt entry is required and allowed. Please verify ACPI configuration."); status = STATUS_DEVICE_CONFIGURATION_ERROR; } if (dmaResources != 1) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_INIT, "Exactly 1 DMA entry is required and allowed. Please verify ACPI configuration."); status = STATUS_DEVICE_CONFIGURATION_ERROR; } if (NT_SUCCESS(status)) { // // Initialize device context and set default values. // deviceContext->pwmClockConfig.ClockSource = BCM_PWM_CLOCKSOURCE_PLLC; deviceContext->pwmClockConfig.Divisor = CM_PWMCTL_DIVI_PLLC_1MHZ; deviceContext->pwmChannel1Config.Channel = BCM_PWM_CHANNEL_CHANNEL1; deviceContext->pwmChannel1Config.Range = 0x20; deviceContext->pwmChannel1Config.DutyMode = BCM_PWM_DUTYMODE_PWM; deviceContext->pwmChannel1Config.Mode = BCM_PWM_MODE_PWM; deviceContext->pwmChannel1Config.Polarity = BCM_PWM_POLARITY_NORMAL; deviceContext->pwmChannel1Config.Repeat = BCM_PWM_REPEATMODE_OFF; deviceContext->pwmChannel1Config.Silence = BCM_PWM_SILENCELEVEL_LOW; deviceContext->pwmDuty1 = 0; deviceContext->pwmChannel2Config.Channel = BCM_PWM_CHANNEL_CHANNEL2; deviceContext->pwmChannel2Config.Range = 0x20; deviceContext->pwmChannel2Config.DutyMode = BCM_PWM_DUTYMODE_PWM; deviceContext->pwmChannel2Config.Mode = BCM_PWM_MODE_PWM; deviceContext->pwmChannel2Config.Polarity = BCM_PWM_POLARITY_NORMAL; deviceContext->pwmChannel2Config.Repeat = BCM_PWM_REPEATMODE_OFF; deviceContext->pwmChannel2Config.Silence = BCM_PWM_SILENCELEVEL_LOW; deviceContext->pwmDuty2 = 0; deviceContext->pwmMode = PWM_MODE_REGISTER; InitializeListHead(&deviceContext->notificationList); deviceContext->dmaDpcForIsrErrorCount = 0; deviceContext->dmaUnderflowErrorCount = 0; deviceContext->dmaLastKnownCompletedPacket = NO_LAST_COMPLETED_PACKET; deviceContext->dmaPacketsInUse = 0; deviceContext->dmaPacketsToPrime = 0; deviceContext->dmaPacketsToPrimePreset = 0; deviceContext->dmaPacketsProcessed = 0; deviceContext->dmaAudioNotifcationCount = 0; deviceContext->dmaRestartRequired = FALSE; } else { ReleaseHardware(Device, ResourceListTranslated); } return status; }
/*++ Routine Description: In this callback, the driver does whatever is necessary to make the hardware ready to use. Arguments: Device - A handle to a framework device object. ResourcesRaw - A handle to a framework resource-list object that identifies the raw hardware resources that the Plug and Play manager has assigned to the device. ResourcesTranslated - A handle to a framework resource-list object that identifies the translated hardware resources that the Plug and Play manager has assigned to the device. Return Value: NTSTATUS value --*/ _Use_decl_annotations_ NTSTATUS VchiqPrepareHardware ( WDFDEVICE Device, WDFCMRESLIST ResourcesRaw, WDFCMRESLIST ResourcesTranslated ) { NTSTATUS status; DEVICE_CONTEXT* deviceContextPtr; ULONG resourceCount; ULONG MemoryResourceCount = 0; ULONG InterruptResourceCount = 0; PAGED_CODE(); deviceContextPtr = VchiqGetDeviceContext(Device); resourceCount = WdfCmResourceListGetCount(ResourcesTranslated); for (ULONG i = 0; i < resourceCount; ++i) { PCM_PARTIAL_RESOURCE_DESCRIPTOR res; res = WdfCmResourceListGetDescriptor(ResourcesTranslated, i); switch (res->Type) { case CmResourceTypeMemory: { VCHIQ_LOG_INFORMATION( "Memory Resource Start: 0x%08x, Length: 0x%08x", res->u.Memory.Start.LowPart, res->u.Memory.Length); deviceContextPtr->VchiqRegisterPtr = MmMapIoSpaceEx( res->u.Memory.Start, res->u.Memory.Length, PAGE_READWRITE | PAGE_NOCACHE); if (deviceContextPtr->VchiqRegisterPtr == NULL) { VCHIQ_LOG_ERROR("Failed to map VCHIQ register"); status = STATUS_UNSUCCESSFUL; goto End; } deviceContextPtr->VchiqRegisterLength = res->u.Memory.Length; ++MemoryResourceCount; } break; case CmResourceTypeInterrupt: { VCHIQ_LOG_INFORMATION( "Interrupt Level: 0x%08x, Vector: 0x%08x\n", res->u.Interrupt.Level, res->u.Interrupt.Vector); WDF_INTERRUPT_CONFIG interruptConfig; WDF_INTERRUPT_CONFIG_INIT ( &interruptConfig, VchiqIsr, VchiqDpc); interruptConfig.InterruptRaw = WdfCmResourceListGetDescriptor (ResourcesRaw, i); interruptConfig.InterruptTranslated = res; status = WdfInterruptCreate( Device, &interruptConfig, WDF_NO_OBJECT_ATTRIBUTES, &deviceContextPtr->VchiqIntObj); if (!NT_SUCCESS (status)) { VCHIQ_LOG_ERROR ( "Fail to initialize VCHIQ interrupt object"); return status; } ++InterruptResourceCount; } break; default: { VCHIQ_LOG_WARNING("Unsupported resources, ignoring"); } break; } if (MemoryResourceCount && InterruptResourceCount) break; } if (MemoryResourceCount != VCHIQ_MEMORY_RESOURCE_TOTAL && InterruptResourceCount != VCHIQ_INT_RESOURCE_TOTAL) { status = STATUS_UNSUCCESSFUL; VCHIQ_LOG_ERROR("Unknown resource assignment"); goto End; } status = STATUS_SUCCESS; End: if (!NT_SUCCESS(status)) { VCHIQ_LOG_ERROR("VchiqPrepareHardware failed %!STATUS!", status); } return status; }
NTSTATUS RegistersCreate( _In_ WDFDEVICE Device, _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RegistersResource ) /*++ Routine Description: Helper function to map the memory resources to the HW registers. Arguments: Device - Wdf device object corresponding to the FDO RegisterResource - Raw resource for the memory Return Value: Appropriate NTSTATUS value --*/ { NTSTATUS Status; PREGISTERS_CONTEXT Context; WDF_OBJECT_ATTRIBUTES Attributes; TraceEntry(); PAGED_CODE(); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, REGISTERS_CONTEXT); Status = WdfObjectAllocateContext(Device, &Attributes, &Context); if (Status == STATUS_OBJECT_NAME_EXISTS) { // // In the case of a resource rebalance, the context allocated // previously still exists. // Status = STATUS_SUCCESS; RtlZeroMemory(Context, sizeof(*Context)); } CHK_NT_MSG(Status, "Failed to allocate context for registers"); Context->RegisterBase = MmMapIoSpaceEx( RegistersResource->u.Memory.Start, RegistersResource->u.Memory.Length, PAGE_NOCACHE | PAGE_READWRITE); if (Context->RegisterBase == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; CHK_NT_MSG(Status, "MmMapIoSpaceEx failed"); } Context->RegistersLength = RegistersResource->u.Memory.Length; End: TraceExit(); return Status; }