diff options
Diffstat (limited to 'spb/SkeletonI2C/device.cpp')
| -rw-r--r-- | spb/SkeletonI2C/device.cpp | 2268 |
1 files changed, 2268 insertions, 0 deletions
diff --git a/spb/SkeletonI2C/device.cpp b/spb/SkeletonI2C/device.cpp new file mode 100644 index 00000000..5666465f --- /dev/null +++ b/spb/SkeletonI2C/device.cpp @@ -0,0 +1,2268 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + device.cpp + +Abstract: + + This module contains WDF device initialization + and SPB callback functions for the controller driver. + +Environment: + + kernel-mode only + +Revision History: + +--*/ + +#include "internal.h" +#include "device.h" +#include "controller.h" + +#include "device.tmh" + + +///////////////////////////////////////////////// +// +// WDF and SPB DDI callbacks. +// +///////////////////////////////////////////////// + +NTSTATUS +OnPrepareHardware( + _In_ WDFDEVICE FxDevice, + _In_ WDFCMRESLIST FxResourcesRaw, + _In_ WDFCMRESLIST FxResourcesTranslated + ) +/*++ + + Routine Description: + + This routine maps the hardware resources to the SPB + controller register structure. + + Arguments: + + FxDevice - a handle to the framework device object + FxResourcesRaw - list of translated hardware resources that + the PnP manager has assigned to the device + FxResourcesTranslated - list of raw hardware resources that + the PnP manager has assigned to the device + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + NT_ASSERT(pDevice != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(FxResourcesRaw); + + // + // Get the register base for the I2C controller. + // + + { + ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); + + for(ULONG i = 0; i < resourceCount; i++) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR res; + + res = WdfCmResourceListGetDescriptor(FxResourcesTranslated, i); + + if (res->Type == CmResourceTypeMemory) + { + pDevice->pRegisters = + (PSKELETONI2C_REGISTERS) MmMapIoSpace( + res->u.Memory.Start, + res->u.Memory.Length, + MmNonCached); + + pDevice->RegistersCb = res->u.Memory.Length; + + if (pDevice->pRegisters == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_WDFLOADING, + "Error mapping controller registers (PA:%I64x, length:%d) " + "for WDFDEVICE %p - %!STATUS!", + res->u.Memory.Start.QuadPart, + res->u.Memory.Length, + pDevice->FxDevice, + status); + + NT_ASSERT(pDevice->pRegisters != NULL); + + goto exit; + } + + // + // Save the physical address to help identify + // the underlying controller while debugging. + // + + pDevice->pRegistersPhysicalAddress = res->u.Memory.Start; + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WDFLOADING, + "I2C controller @ paddr %I64x vaddr @ %p for WDFDEVICE %p", + pDevice->pRegistersPhysicalAddress.QuadPart, + pDevice->pRegisters, + pDevice->FxDevice); + } + } + } + +exit: + + FuncExit(TRACE_FLAG_WDFLOADING); + + return status; +} + +NTSTATUS +OnReleaseHardware( + _In_ WDFDEVICE FxDevice, + _In_ WDFCMRESLIST FxResourcesTranslated + ) +/*++ + + Routine Description: + + This routine unmaps the SPB controller register structure. + + Arguments: + + FxDevice - a handle to the framework device object + FxResourcesRaw - list of translated hardware resources that + the PnP manager has assigned to the device + FxResourcesTranslated - list of raw hardware resources that + the PnP manager has assigned to the device + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + NT_ASSERT(pDevice != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(FxResourcesTranslated); + + if (pDevice->pRegisters != NULL) + { + MmUnmapIoSpace(pDevice->pRegisters, pDevice->RegistersCb); + + pDevice->pRegisters = NULL; + pDevice->RegistersCb = 0; + } + + FuncExit(TRACE_FLAG_WDFLOADING); + + return status; +} + +NTSTATUS +OnD0Entry( + _In_ WDFDEVICE FxDevice, + _In_ WDF_POWER_DEVICE_STATE FxPreviousState + ) +/*++ + + Routine Description: + + This routine allocates objects needed by the driver + and initializes the controller hardware. + + Arguments: + + FxDevice - a handle to the framework device object + FxPreviousState - previous power state + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + NT_ASSERT(pDevice != NULL); + + UNREFERENCED_PARAMETER(FxPreviousState); + + // + // Initialize controller. + // + + pDevice->pCurrentTarget = NULL; + + ControllerInitialize(pDevice); + + FuncExit(TRACE_FLAG_WDFLOADING); + + return STATUS_SUCCESS; +} + +NTSTATUS +OnD0Exit( + _In_ WDFDEVICE FxDevice, + _In_ WDF_POWER_DEVICE_STATE FxPreviousState + ) +/*++ + + Routine Description: + + This routine destroys objects needed by the driver + and uninitializes the controller hardware. + + Arguments: + + FxDevice - a handle to the framework device object + FxPreviousState - previous power state + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + NT_ASSERT(pDevice != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(FxPreviousState); + + // + // Uninitialize controller. + // + + ControllerUninitialize(pDevice); + + pDevice->pCurrentTarget = NULL; + + FuncExit(TRACE_FLAG_WDFLOADING); + + return status; +} + +NTSTATUS +OnSelfManagedIoInit( + _In_ WDFDEVICE FxDevice + ) +/*++ + + Routine Description: + + Initializes and starts the device's self-managed I/O operations. + + Arguments: + + FxDevice - a handle to the framework device object + + Return Value: + + None + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + NTSTATUS status; + + // + // Register for monitor power setting callback. This will be + // used to dynamically set the idle timeout delay according + // to the monitor power state. + // + + NT_ASSERT(pDevice->pMonitorPowerSettingHandle == NULL); + + status = PoRegisterPowerSettingCallback( + WdfDeviceWdmGetDeviceObject(pDevice->FxDevice), + &GUID_MONITOR_POWER_ON, + OnMonitorPowerSettingCallback, + (PVOID)pDevice->FxDevice, + &pDevice->pMonitorPowerSettingHandle); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_WDFLOADING, + "Failed to register monitor power setting callback - %!STATUS!", + status); + + goto exit; + } + +exit: + + FuncExit(TRACE_FLAG_WDFLOADING); + + return status; +} + +VOID +OnSelfManagedIoCleanup( + _In_ WDFDEVICE FxDevice + ) +/*++ + + Routine Description: + + Cleanup for the device's self-managed I/O operations. + + Arguments: + + FxDevice - a handle to the framework device object + + Return Value: + + None + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + PPBC_DEVICE pDevice = GetDeviceContext(FxDevice); + + // + // Unregister for monitor power setting callback. + // + + if (pDevice->pMonitorPowerSettingHandle != NULL) + { + PoUnregisterPowerSettingCallback(pDevice->pMonitorPowerSettingHandle); + pDevice->pMonitorPowerSettingHandle = NULL; + } + + FuncExit(TRACE_FLAG_WDFLOADING); +} + +__drv_functionClass(POWER_SETTING_CALLBACK) +_IRQL_requires_same_ +NTSTATUS +OnMonitorPowerSettingCallback( + _In_ LPCGUID SettingGuid, + _In_reads_bytes_(ValueLength) PVOID Value, + _In_ ULONG ValueLength, + _Inout_opt_ PVOID Context + ) +/*++ + + Routine Description: + + This routine updates the idle timeout delay according + to the current monitor power setting. + + Arguments: + + SettingGuid - the setting GUID + Value - pointer to the new value of the power setting that changed + ValueLength - value of type ULONG that specifies the size, in bytes, + of the new power setting value + Context - the WDFDEVICE pointer context + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_WDFLOADING); + + UNREFERENCED_PARAMETER(ValueLength); + + WDFDEVICE Device; + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + BOOLEAN isMonitorOff; + NTSTATUS status = STATUS_SUCCESS; + + if (Context == NULL) + { + status = STATUS_INVALID_PARAMETER; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_WDFLOADING, + "%!FUNC! parameter Context is NULL - %!STATUS!", + status); + + goto exit; + } + + Device = (WDFDEVICE)Context; + + // + // We only expect GUID_MONITOR_POWER_ON notifications + // in this callback, but let's check just to be sure. + // + + if (IsEqualGUID(*SettingGuid, GUID_MONITOR_POWER_ON)) + { + NT_ASSERT(Value != NULL); + NT_ASSERT(ValueLength == sizeof(ULONG)); + + // + // Determine power setting. + // + + isMonitorOff = ((*(PULONG)Value) == MONITOR_POWER_OFF); + + // + // Update the idle timeout delay. + // + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT( + &idleSettings, + IdleCannotWakeFromS0); + + idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint; + + if (isMonitorOff) + { + idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_OFF; + } + else + { + idleSettings.IdleTimeout = IDLE_TIMEOUT_MONITOR_ON; + + } + + status = WdfDeviceAssignS0IdleSettings( + Device, + &idleSettings); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_WDFLOADING, + "Failed to assign S0 idle settings - %!STATUS!", + status); + + goto exit; + } + } + +exit: + + FuncExit(TRACE_FLAG_WDFLOADING); + + return status; +} + +NTSTATUS +OnTargetConnect( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget + ) +/*++ + + Routine Description: + + This routine is invoked whenever a peripheral driver opens + a target. It retrieves target-specific settings from the + Resource Hub and saves them in the target's context. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + PPBC_DEVICE pDevice = GetDeviceContext(SpbController); + PPBC_TARGET pTarget = GetTargetContext(SpbTarget); + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + // + // Get target connection parameters. + // + + SPB_CONNECTION_PARAMETERS params; + SPB_CONNECTION_PARAMETERS_INIT(¶ms); + + SpbTargetGetConnectionParameters(SpbTarget, ¶ms); + + // + // Retrieve target settings. + // + + status = PbcTargetGetSettings(pDevice, + params.ConnectionParameters, + &pTarget->Settings + ); + + // + // Initialize target context. + // + + if (NT_SUCCESS(status)) + { + pTarget->SpbTarget = SpbTarget; + pTarget->pCurrentRequest = NULL; + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Connected to SPBTARGET %p at address 0x%lx from WDFDEVICE %p", + pTarget->SpbTarget, + pTarget->Settings.Address, + pDevice->FxDevice); + } + + FuncExit(TRACE_FLAG_SPBDDI); + + return status; +} + +VOID +OnControllerLock( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest + ) +/*++ + + Routine Description: + + This routine is invoked whenever the controller is to + be locked for a single target. The request is only completed + if there is an error configuring the transfer. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + + Return Value: + + None. The request is completed synchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + PPBC_DEVICE pDevice = GetDeviceContext(SpbController); + PPBC_TARGET pTarget = GetTargetContext(SpbTarget); + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Assign current target. + // + + NT_ASSERT(pDevice->pCurrentTarget == NULL); + + pDevice->pCurrentTarget = pTarget; + + WdfSpinLockRelease(pDevice->Lock); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Controller locked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)", + pTarget->SpbTarget, + pTarget->Settings.Address, + pDevice->FxDevice); + + // + // Complete lock request. + // + + SpbRequestComplete(SpbRequest, STATUS_SUCCESS); + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnControllerUnlock( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest + ) +/*++ + + Routine Description: + + This routine is invoked whenever the controller is to + be unlocked for a single target. The request is only completed + if there is an error configuring the transfer. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + PPBC_DEVICE pDevice = GetDeviceContext(SpbController); + PPBC_TARGET pTarget = GetTargetContext(SpbTarget); + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // TODO: Check if there is an active sequence + // and if so perform any action necessary + // to stop the transfer in process. + + // + // Remove current target. + // + + NT_ASSERT(pDevice->pCurrentTarget == pTarget); + + pDevice->pCurrentTarget = NULL; + + WdfSpinLockRelease(pDevice->Lock); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Controller unlocked for SPBTARGET %p at address 0x%lx (WDFDEVICE %p)", + pTarget->SpbTarget, + pTarget->Settings.Address, + pDevice->FxDevice); + + // + // Complete lock request. + // + + SpbRequestComplete(SpbRequest, STATUS_SUCCESS); + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnRead( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest, + _In_ size_t Length + ) +/*++ + + Routine Description: + + This routine sets up a read from the target device using + the supplied buffers. The request is only completed + if there is an error configuring the transfer. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + Length - the number of bytes to read from the target + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Received read request %p of length %Iu for SPBTARGET %p " + "(WDFDEVICE %p)", + SpbRequest, + Length, + SpbTarget, + SpbController); + + PbcRequestConfigureForNonSequence( + SpbController, + SpbTarget, + SpbRequest, + Length); + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnWrite( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest, + _In_ size_t Length + ) +/*++ + + Routine Description: + + This routine sets up a write to the target device using + the supplied buffers. The request is only completed + if there is an error configuring the transfer. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + Length - the number of bytes to write to the target + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Received write request %p of length %Iu for SPBTARGET %p " + "(WDFDEVICE %p)", + SpbRequest, + Length, + SpbTarget, + SpbController); + + PbcRequestConfigureForNonSequence( + SpbController, + SpbTarget, + SpbRequest, + Length); + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnSequence( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest, + _In_ ULONG TransferCount + ) +/*++ + + Routine Description: + + This routine sets up a sequence of reads and writes. It + validates parameters as necessary. The request is only + completed if there is an error configuring the transfer. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + TransferCount - number of individual transfers in the sequence + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + PPBC_DEVICE pDevice = GetDeviceContext(SpbController); + PPBC_TARGET pTarget = GetTargetContext(SpbTarget); + PPBC_REQUEST pRequest = GetRequestContext(SpbRequest); + BOOLEAN completeRequest = FALSE; + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + NT_ASSERT(pRequest != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + // + // Get request parameters. + // + + SPB_REQUEST_PARAMETERS params; + SPB_REQUEST_PARAMETERS_INIT(¶ms); + SpbRequestGetParameters(SpbRequest, ¶ms); + + NT_ASSERT(params.Position == SpbRequestSequencePositionSingle); + NT_ASSERT(params.Type == SpbRequestTypeSequence); + + // + // Initialize request context. + // + + pRequest->SpbRequest = SpbRequest; + pRequest->Type = params.Type; + pRequest->TotalInformation = 0; + pRequest->TransferCount = TransferCount; + pRequest->TransferIndex = 0; + pRequest->bIoComplete = FALSE; + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_SPBDDI, + "Received sequence request %p with transfer count %d for SPBTARGET %p " + "(WDFDEVICE %p)", + pRequest->SpbRequest, + pRequest->TransferCount, + SpbTarget, + SpbController); + + // + // Validate the request before beginning the transfer. + // + + status = PbcRequestValidate(pRequest); + + if (!NT_SUCCESS(status)) + { + goto exit; + } + + // + // Configure the request. + // + + status = PbcRequestConfigureForIndex(pRequest, 0); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPBDDI, + "Error configuring request context for SPBREQUEST %p " + "(SPBTARGET %p) - %!STATUS!", + pRequest->SpbRequest, + SpbTarget, + status); + + goto exit; + } + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Mark request cancellable (if cancellation supported). + // + + status = WdfRequestMarkCancelableEx( + pRequest->SpbRequest, OnCancel); + + if (!NT_SUCCESS(status)) + { + // + // WdfRequestMarkCancelableEx should only fail if the request + // has already been cancelled. If it does fail the request + // must be completed with the corresponding status. + // + + NT_ASSERTMSG("WdfRequestMarkCancelableEx should only fail if the request has already been cancelled", + status == STATUS_CANCELLED); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Failed to mark SPBREQUEST %p cancellable - %!STATUS!", + pRequest->SpbRequest, + status); + + WdfSpinLockRelease(pDevice->Lock); + goto exit; + } + + // + // Update device and target contexts. + // + + NT_ASSERT(pDevice->pCurrentTarget == NULL); + NT_ASSERT(pTarget->pCurrentRequest == NULL); + + pDevice->pCurrentTarget = pTarget; + pTarget->pCurrentRequest = pRequest; + + // + // Configure controller and kick-off read. + // Request will be completed asynchronously. + // + + PbcRequestDoTransfer(pDevice, pRequest); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (pRequest->bIoComplete) + { + completeRequest = TRUE; + } + + WdfSpinLockRelease(pDevice->Lock); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (completeRequest) + { + PbcRequestComplete(pRequest); + } + +exit: + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPBDDI, + "Error configuring sequence, completing " + "SPBREQUEST %p synchronously - %!STATUS!", + pRequest->SpbRequest, + status); + + SpbRequestComplete(SpbRequest, status); + } + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnOtherInCallerContext( + _In_ WDFDEVICE SpbController, + _In_ WDFREQUEST FxRequest + ) +/*++ + + Routine Description: + + This routine preprocesses custom IO requests before the framework + places them in an IO queue. For requests using the SPB transfer list + format, it calls SpbRequestCaptureIoOtherTransferList to capture the + client's buffers. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbRequest - a handle to the SPBREQUEST object + + Return Value: + + None. The request is either completed or enqueued asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + NTSTATUS status; + + // + // Check for custom IOCTLs that this driver handles. If + // unrecognized mark as STATUS_NOT_SUPPORTED and complete. + // + + WDF_REQUEST_PARAMETERS fxParams; + WDF_REQUEST_PARAMETERS_INIT(&fxParams); + + WdfRequestGetParameters(FxRequest, &fxParams); + + if ((fxParams.Type != WdfRequestTypeDeviceControl) && + (fxParams.Type != WdfRequestTypeDeviceControlInternal)) + { + status = STATUS_NOT_SUPPORTED; + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPBDDI, + "FxRequest %p is of unsupported request type - %!STATUS!", + FxRequest, + status + ); + goto exit; + } + + // + // TODO: verify the driver supports this DeviceIoContol code, + // otherwise mark as STATUS_NOT_SUPPORTED and complete. + // + + // + // For custom IOCTLs that use the SPB transfer list format + // (i.e. sequence formatting), call SpbRequestCaptureIoOtherTransferList + // so that the driver can leverage other SPB DDIs for this request. + // + + status = SpbRequestCaptureIoOtherTransferList((SPBREQUEST)FxRequest); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPBDDI, + "Failed to capture transfer list for custom SpbRequest %p" + " - %!STATUS!", + FxRequest, + status + ); + goto exit; + } + + // + // Preprocessing has succeeded, enqueue the request. + // + + status = WdfDeviceEnqueueRequest(SpbController, FxRequest); + + if (!NT_SUCCESS(status)) + { + goto exit; + } + +exit: + + if (!NT_SUCCESS(status)) + { + WdfRequestComplete(FxRequest, status); + } + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnOther( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ + + Routine Description: + + This routine processes custom IO requests that are not natively + supported by the SPB framework extension. For requests using the + SPB transfer list format, SpbRequestCaptureIoOtherTransferList + must have been called in the driver's OnOtherInCallerContext routine. + + Arguments: + + SpbController - a handle to the framework device object + representing an SPB controller + SpbTarget - a handle to the SPBTARGET object + SpbRequest - a handle to the SPBREQUEST object + OutputBufferLength - the request's output buffer length + InputBufferLength - the requests input buffer length + IoControlCode - the device IO control code + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_SPBDDI); + + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(SpbController); + UNREFERENCED_PARAMETER(SpbTarget); + UNREFERENCED_PARAMETER(SpbRequest); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(IoControlCode); + + // + // TODO: the driver should take the following steps + // + // 1. Verify this specific DeviceIoContol code is supported, + // otherwise mark as STATUS_NOT_SUPPORTED and complete. + // + // 2. If this IOCTL uses SPB_TRANSFER_LIST and the driver has + // called SpbRequestCaptureIoOtherTransferList previously, + // validate the request format. The driver can make use of + // SpbRequestGetTransferParameters to retrieve each transfer + // descriptor. + // + // If this IOCTL uses some proprietary buffer formating + // instead of SPB_TRANSFER_LIST, validate appropriately. + // + // 3. Setup the device, target, and request contexts as necessary, + // and program the hardware for the transfer. + // + + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. Note this must be done outside + // of any locked code. + SpbRequestComplete(SpbRequest, status); + + FuncExit(TRACE_FLAG_SPBDDI); +} + +VOID +OnCancel( + _In_ WDFREQUEST FxRequest +) + +/*++ + + Routine Description: + + This routine cancels an outstanding request. It + must synchronize with other driver callbacks. + + Arguments: + + wdfRequest - a handle to the WDFREQUEST object + + Return Value: + + None. The request is completed with status. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + SPBREQUEST spbRequest = (SPBREQUEST) FxRequest; + PPBC_DEVICE pDevice; + PPBC_TARGET pTarget; + PPBC_REQUEST pRequest; + BOOLEAN bTransferCompleted = FALSE; + + // + // Get the contexts. + // + + pDevice = GetDeviceContext(SpbRequestGetController(spbRequest)); + pTarget = GetTargetContext(SpbRequestGetTarget(spbRequest)); + pRequest = GetRequestContext(spbRequest); + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + NT_ASSERT(pRequest != NULL); + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Make sure the current target and request + // are valid. + // + + if (pTarget != pDevice->pCurrentTarget) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "Cancel callback without a valid current target for WDFDEVICE %p, " + "this should only occur if SPBREQUEST %p was already completed", + pDevice->FxDevice, + spbRequest + ); + + goto exit; + } + + if (pRequest != pTarget->pCurrentRequest) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "Cancel callback without a valid current request for SPBTARGET %p, " + "this should only occur if SPBREQUEST %p was already completed", + pTarget->SpbTarget, + spbRequest); + + goto exit; + } + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Cancel callback with outstanding SPBREQUEST %p, " + "stop IO and complete it", + spbRequest); + + // + // Stop delay timer. + // + + if(WdfTimerStop(pDevice->DelayTimer, FALSE)) + { + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Delay timer previously schedule, now stopped"); + } + + // + // Disable interrupts and clear saved stat for DPC. + // Must synchronize with ISR. + // + + NT_ASSERT(pDevice->InterruptObject != NULL); + + // TODO: Uncomment when using interrupts. + //WdfInterruptAcquireLock(pDevice->InterruptObject); + + ControllerDisableInterrupts(pDevice); + pDevice->InterruptStatus = 0; + + // + // TODO: Implement any necessary logic to abort the + // current IO operation. For I2C this requires + // driving a stop bit on the bus. + // + + // TODO: Uncomment when using interrupts. + //WdfInterruptReleaseLock(pDevice->InterruptObject); + + // + // Mark request as cancelled and complete. + // + + pRequest->Status = STATUS_CANCELLED; + + ControllerCompleteTransfer(pDevice, pRequest, TRUE); + NT_ASSERT(pRequest->bIoComplete == TRUE); + bTransferCompleted = TRUE; + +exit: + + // + // Release the device lock. + // + + WdfSpinLockRelease(pDevice->Lock); + + // + // Complete the request. There shouldn't be more IO. + // This must be done outside of the locked code. + // + + if (bTransferCompleted) + { + PbcRequestComplete(pRequest); + } + + FuncExit(TRACE_FLAG_SPBDDI); +} + + +///////////////////////////////////////////////// +// +// Interrupt handling functions. +// +///////////////////////////////////////////////// + +BOOLEAN +OnInterruptIsr( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) +/*++ + + Routine Description: + + This routine responds to interrupts generated by the + controller. If one is recognized, it queues a DPC for + processing. The interrupt is acknowledged and subsequent + interrupts are temporarily disabled. + + Arguments: + + Interrupt - a handle to a framework interrupt object + MessageID - message number identifying the device's + hardware interrupt message (if using MSI) + + Return Value: + + TRUE if interrupt recognized. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + BOOLEAN interruptRecognized = FALSE; + ULONG stat; + PPBC_DEVICE pDevice = GetDeviceContext( + WdfInterruptGetDevice(Interrupt)); + + UNREFERENCED_PARAMETER(MessageID); + + NT_ASSERT(pDevice != NULL); + + // + // Queue a DPC if the device's interrupt + // is enabled and active. + // + + stat = ControllerGetInterruptStatus( + pDevice, + PbcDeviceGetInterruptMask(pDevice)); + + if (stat > 0) + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_TRANSFER, + "Interrupt with status 0x%lx for WDFDEVICE %p", + stat, + pDevice->FxDevice); + + // + // Save the interrupt status and disable all other + // interrupts for now. They will be re-enabled + // in OnInterruptDpc. Queue the DPC. + // + + interruptRecognized = TRUE; + + pDevice->InterruptStatus |= (stat); + ControllerDisableInterrupts(pDevice); + + if(!WdfInterruptQueueDpcForIsr(Interrupt)) + { + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Interrupt with status 0x%lx occurred with " + "DPC already queued for WDFDEVICE %p", + stat, + pDevice->FxDevice); + } + } + + FuncExit(TRACE_FLAG_TRANSFER); + + return interruptRecognized; +} + +VOID +OnInterruptDpc( + _In_ WDFINTERRUPT Interrupt, + _In_ WDFOBJECT WdfDevice + ) +/*++ + + Routine Description: + + This routine processes interrupts from the controller. + When finished it reenables interrupts as appropriate. + + Arguments: + + Interrupt - a handle to a framework interrupt object + WdfDevice - a handle to the framework device object + + Return Value: + + None. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + PPBC_DEVICE pDevice; + PPBC_TARGET pTarget; + PPBC_REQUEST pRequest = NULL; + ULONG stat; + BOOLEAN bInterruptsProcessed = FALSE; + BOOLEAN completeRequest = FALSE; + + UNREFERENCED_PARAMETER(Interrupt); + + pDevice = GetDeviceContext(WdfDevice); + NT_ASSERT(pDevice != NULL); + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Make sure the target and request are + // still valid. + // + + pTarget = pDevice->pCurrentTarget; + + if (pTarget == NULL) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "DPC scheduled without a valid current target for WDFDEVICE %p, " + "this should only occur if the request was already cancelled", + pDevice->FxDevice); + + goto exit; + } + + pRequest = pTarget->pCurrentRequest; + + if (pRequest == NULL) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "DPC scheduled without a valid current request for SPBTARGET %p, " + "this should only occur if the request was already cancelled", + pTarget->SpbTarget); + + goto exit; + } + + NT_ASSERT(pRequest->SpbRequest != NULL); + + // + // Synchronize shared data buffers with ISR. + // Copy interrupt status and clear shared buffer. + // If there is a current target and request, + // a DPC should never occur with interrupt status 0. + // + + // TODO: Uncomment when using interrupts. + //WdfInterruptAcquireLock(Interrupt); + + stat = pDevice->InterruptStatus; + pDevice->InterruptStatus = 0; + + // TODO: Uncomment when using interrupts. + //WdfInterruptReleaseLock(Interrupt); + + if (stat == 0) + { + goto exit; + } + + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_TRANSFER, + "DPC for interrupt with status 0x%lx for WDFDEVICE %p", + stat, + pDevice->FxDevice); + + // + // Acknowledge and process interrupts. + // + + ControllerAcknowledgeInterrupts(pDevice, stat); + + ControllerProcessInterrupts(pDevice, pRequest, stat); + bInterruptsProcessed = TRUE; + if (pRequest->bIoComplete) + { + completeRequest = TRUE; + } + + // + // Re-enable interrupts if necessary. Synchronize with ISR. + // + + // TODO: Uncomment when using interrupts. + //WdfInterruptAcquireLock(Interrupt); + + ULONG mask = PbcDeviceGetInterruptMask(pDevice); + + if (mask > 0) + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_TRANSFER, + "Re-enable interrupts with mask 0x%lx for WDFDEVICE %p", + mask, + pDevice->FxDevice); + + ControllerEnableInterrupts(pDevice, mask); + } + + // TODO: Uncomment when using interrupts. + //WdfInterruptReleaseLock(Interrupt); + +exit: + + // + // Release the device lock. + // + + WdfSpinLockRelease(pDevice->Lock); + + // + // Complete the request if necessary. + // This must be done outside of the locked code. + // + + if (bInterruptsProcessed) + { + if (completeRequest) + { + PbcRequestComplete(pRequest); + } + } + + FuncExit(TRACE_FLAG_TRANSFER); +} + + +///////////////////////////////////////////////// +// +// PBC functions. +// +///////////////////////////////////////////////// + +NTSTATUS +PbcTargetGetSettings( + _In_ PPBC_DEVICE pDevice, + _In_ PVOID ConnectionParameters, + _Out_ PPBC_TARGET_SETTINGS pSettings + ) +/*++ + + Routine Description: + + This routine populates the target's settings. + + Arguments: + + pDevice - a pointer to the PBC device context + ConnectionParameters - a pointer to a blob containing the + connection parameters + Settings - a pointer the the target's settings + + Return Value: + + Status + +--*/ +{ + FuncEntry(TRACE_FLAG_PBCLOADING); + + UNREFERENCED_PARAMETER(pDevice); + + NT_ASSERT(ConnectionParameters != nullptr); + NT_ASSERT(pSettings != nullptr); + + PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER connection; + PPNP_SERIAL_BUS_DESCRIPTOR descriptor; + PPNP_I2C_SERIAL_BUS_DESCRIPTOR i2cDescriptor; + + connection = (PRH_QUERY_CONNECTION_PROPERTIES_OUTPUT_BUFFER) + ConnectionParameters; + + if (connection->PropertiesLength < sizeof(PNP_SERIAL_BUS_DESCRIPTOR)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_PBCLOADING, + "Invalid connection properties (length = %lu, " + "expected = %Iu)", + connection->PropertiesLength, + sizeof(PNP_SERIAL_BUS_DESCRIPTOR)); + + return STATUS_INVALID_PARAMETER; + } + + descriptor = (PPNP_SERIAL_BUS_DESCRIPTOR) + connection->ConnectionProperties; + + if (descriptor->SerialBusType != I2C_SERIAL_BUS_TYPE) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_PBCLOADING, + "Bus type %c not supported, only I2C", + descriptor->SerialBusType); + + return STATUS_INVALID_PARAMETER; + } + + i2cDescriptor = (PPNP_I2C_SERIAL_BUS_DESCRIPTOR) + connection->ConnectionProperties; + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PBCLOADING, + "I2C Connection Descriptor %p " + "ConnectionSpeed:%lu " + "Address:0x%hx", + i2cDescriptor, + i2cDescriptor->ConnectionSpeed, + i2cDescriptor->SlaveAddress); + + // Target address + pSettings->Address = (ULONG)i2cDescriptor->SlaveAddress; + + // Address mode + USHORT i2cFlags = i2cDescriptor->SerialBusDescriptor.TypeSpecificFlags; + pSettings->AddressMode = + ((i2cFlags & I2C_SERIAL_BUS_SPECIFIC_FLAG_10BIT_ADDRESS) == 0) ? + AddressMode7Bit : AddressMode10Bit; + + // Clock speed + pSettings->ConnectionSpeed = i2cDescriptor->ConnectionSpeed; + + FuncExit(TRACE_FLAG_PBCLOADING); + + return STATUS_SUCCESS; +} + +NTSTATUS +PbcRequestValidate( + _In_ PPBC_REQUEST pRequest) +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + SPB_TRANSFER_DESCRIPTOR descriptor; + NTSTATUS status = STATUS_SUCCESS; + + // + // Validate each transfer descriptor. + // + + for (ULONG i = 0; i < pRequest->TransferCount; i++) + { + // + // Get transfer parameters for index. + // + + SPB_TRANSFER_DESCRIPTOR_INIT(&descriptor); + + SpbRequestGetTransferParameters( + pRequest->SpbRequest, + i, + &descriptor, + nullptr); + + // + // Validate the transfer length. + // + + if (descriptor.TransferLength > SI2C_MAX_TRANSFER_LENGTH) + { + status = STATUS_INVALID_PARAMETER; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_TRANSFER, + "Transfer length %Iu is too large for controller driver, " + "max supported is %d (SPBREQUEST %p, index %lu) - %!STATUS!", + descriptor.TransferLength, + SI2C_MAX_TRANSFER_LENGTH, + pRequest->SpbRequest, + i, + status); + + goto exit; + } + } + +exit: + + FuncExit(TRACE_FLAG_TRANSFER); + + return status; +} + +VOID +PbcRequestConfigureForNonSequence( + _In_ WDFDEVICE SpbController, + _In_ SPBTARGET SpbTarget, + _In_ SPBREQUEST SpbRequest, + _In_ size_t Length + ) +/*++ + + Routine Description: + + This is a generic helper routine used to configure + the request context and controller hardware for a non- + sequence SPB request. It validates parameters and retrieves + the transfer buffer as necessary. + + Arguments: + + pDevice - a pointer to the PBC device context + pTarget - a pointer to the PBC target context + pRequest - a pointer to the PBC request context + Length - the number of bytes to read from the target + Direction - direction of the transfer + + Return Value: + + STATUS + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + PPBC_DEVICE pDevice = GetDeviceContext(SpbController); + PPBC_TARGET pTarget = GetTargetContext(SpbTarget); + PPBC_REQUEST pRequest = GetRequestContext(SpbRequest); + BOOLEAN completeRequest = FALSE; + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pTarget != NULL); + NT_ASSERT(pRequest != NULL); + + UNREFERENCED_PARAMETER(Length); + + NTSTATUS status; + + // + // Get the request parameters. + // + + SPB_REQUEST_PARAMETERS params; + SPB_REQUEST_PARAMETERS_INIT(¶ms); + SpbRequestGetParameters(SpbRequest, ¶ms); + + // + // Initialize request context. + // + + pRequest->SpbRequest = SpbRequest; + pRequest->Type = params.Type; + pRequest->SequencePosition = params.Position; + pRequest->TotalInformation = 0; + pRequest->TransferCount = 1; + pRequest->TransferIndex = 0; + pRequest->bIoComplete = FALSE; + + // + // Validate the request before beginning the transfer. + // + + status = PbcRequestValidate(pRequest); + + if (!NT_SUCCESS(status)) + { + goto exit; + } + + // + // Configure the request. + // + + status = PbcRequestConfigureForIndex( + pRequest, + pRequest->TransferIndex); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPBDDI, + "Error configuring request context for SPBREQUEST %p (SPBTARGET %p)" + "- %!STATUS!", + pRequest->SpbRequest, + SpbTarget, + status); + + goto exit; + } + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Mark request cancellable (if cancellation supported). + // + + status = WdfRequestMarkCancelableEx( + pRequest->SpbRequest, OnCancel); + + if (!NT_SUCCESS(status)) + { + // + // WdfRequestMarkCancelableEx should only fail if the request + // has already been cancelled. If it does fail the request + // must be completed with the corresponding status. + // + + NT_ASSERTMSG("WdfRequestMarkCancelableEx should only fail if the request has already been cancelled", + status == STATUS_CANCELLED); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Failed to mark SPBREQUEST %p cancellable - %!STATUS!", + pRequest->SpbRequest, + status); + + WdfSpinLockRelease(pDevice->Lock); + goto exit; + } + + // + // If sequence position is... + // - single: ensure there is not a current target + // - not single: ensure that the current target is the + // same as this target + // + + if (params.Position == SpbRequestSequencePositionSingle) + { + NT_ASSERT(pDevice->pCurrentTarget == NULL); + } + else + { + NT_ASSERT(pDevice->pCurrentTarget == pTarget); + } + + // + // Ensure there is not a current request. + // + + NT_ASSERT(pTarget->pCurrentRequest == NULL); + + // + // Update the device and target contexts. + // + + if (pRequest->SequencePosition == SpbRequestSequencePositionSingle) + { + pDevice->pCurrentTarget = pTarget; + } + + pTarget->pCurrentRequest = pRequest; + + // + // Configure controller and kick-off read. + // Request will be completed asynchronously. + // + + PbcRequestDoTransfer(pDevice, pRequest); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (pRequest->bIoComplete) + { + completeRequest = TRUE; + } + + WdfSpinLockRelease(pDevice->Lock); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (completeRequest) + { + PbcRequestComplete(pRequest); + } + +exit: + + if (!NT_SUCCESS(status)) + { + SpbRequestComplete(SpbRequest, status); + } + + FuncExit(TRACE_FLAG_TRANSFER); +} + +NTSTATUS +PbcRequestConfigureForIndex( + _Inout_ PPBC_REQUEST pRequest, + _In_ ULONG Index + ) +/*++ + + Routine Description: + + This is a helper routine used to configure the request + context and controller hardware for a transfer within a + sequence. It validates parameters and retrieves + the transfer buffer as necessary. + + Arguments: + + pRequest - a pointer to the PBC request context + Index - index of the transfer within the sequence + + Return Value: + + STATUS + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + NT_ASSERT(pRequest != NULL); + + NTSTATUS status = STATUS_SUCCESS; + + // + // Get transfer parameters for index. + // + + SPB_TRANSFER_DESCRIPTOR descriptor; + PMDL pMdl; + + SPB_TRANSFER_DESCRIPTOR_INIT(&descriptor); + + SpbRequestGetTransferParameters( + pRequest->SpbRequest, + Index, + &descriptor, + &pMdl); + + NT_ASSERT(pMdl != NULL); + + // + // Configure request context. + // + + pRequest->pMdlChain = pMdl; + pRequest->Length = descriptor.TransferLength; + pRequest->Information = 0; + pRequest->Direction = descriptor.Direction; + pRequest->DelayInUs = descriptor.DelayInUs; + + // + // Update sequence position if request is type sequence. + // + + if (pRequest->Type == SpbRequestTypeSequence) + { + if (pRequest->TransferCount == 1) + { + pRequest->SequencePosition = SpbRequestSequencePositionSingle; + } + else if (Index == 0) + { + pRequest->SequencePosition = SpbRequestSequencePositionFirst; + } + else if (Index == (pRequest->TransferCount - 1)) + { + pRequest->SequencePosition = SpbRequestSequencePositionLast; + } + else + { + pRequest->SequencePosition = SpbRequestSequencePositionContinue; + } + } + + PPBC_TARGET pTarget = GetTargetContext(SpbRequestGetTarget(pRequest->SpbRequest)); + NT_ASSERT(pTarget != NULL); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Request context configured for %s (index %lu) " + "to address 0x%lx (SPBTARGET %p)", + pRequest->Direction == SpbTransferDirectionFromDevice ? "read" : "write", + Index, + pTarget->Settings.Address, + pTarget->SpbTarget); + + FuncExit(TRACE_FLAG_TRANSFER); + + return status; +} + +VOID +PbcRequestDoTransfer( + _In_ PPBC_DEVICE pDevice, + _In_ PPBC_REQUEST pRequest + ) +/*++ + + Routine Description: + + This routine either starts the delay timer or + kicks off the transfer depending on the request + parameters. + + Arguments: + + pDevice - a pointer to the PBC device context + pRequest - a pointer to the PBC request context + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + NT_ASSERT(pDevice != NULL); + NT_ASSERT(pRequest != NULL); + + // + // Start delay timer if necessary for this request, + // otherwise continue transfer. + // + // NOTE: Note using a timer to implement IO delay is only + // applicable for sufficiently long delays (> 15ms). + // For shorter delays, especially on the order of + // microseconds, consider using a different mechanism. + // + + if (pRequest->DelayInUs > 0) + { + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Delaying %lu us before configuring transfer for WDFDEVICE %p", + pRequest->DelayInUs, + pDevice->FxDevice); + + BOOLEAN bTimerAlreadyStarted; + + bTimerAlreadyStarted = WdfTimerStart( + pDevice->DelayTimer, + WDF_REL_TIMEOUT_IN_US(pRequest->DelayInUs)); + + // + // There should never be another request + // scheduled for delay. + // + + if (bTimerAlreadyStarted == TRUE) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_TRANSFER, + "The delay timer should not be started"); + } + } + else + { + ControllerConfigureForTransfer(pDevice, pRequest); + } + + FuncExit(TRACE_FLAG_TRANSFER); +} + +VOID +OnDelayTimerExpired( + _In_ WDFTIMER Timer + ) +/*++ + + Routine Description: + + This routine is invoked whenever the driver's delay + timer expires. It kicks off the transfer for the request. + + Arguments: + + Timer - a handle to a framework timer object + + Return Value: + + None. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + WDFDEVICE fxDevice; + PPBC_DEVICE pDevice; + PPBC_TARGET pTarget = NULL; + PPBC_REQUEST pRequest = NULL; + BOOLEAN completeRequest = FALSE; + + fxDevice = (WDFDEVICE) WdfTimerGetParentObject(Timer); + pDevice = GetDeviceContext(fxDevice); + + NT_ASSERT(pDevice != NULL); + + // + // Acquire the device lock. + // + + WdfSpinLockAcquire(pDevice->Lock); + + // + // Make sure the target and request are + // still valid. + // + + pTarget = pDevice->pCurrentTarget; + + if (pTarget == NULL) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "Delay timer expired without a valid current target for WDFDEVICE %p, " + "this should only occur if the request was already completed", + pDevice->FxDevice); + + goto exit; + } + + pRequest = pTarget->pCurrentRequest; + + if (pRequest == NULL) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_TRANSFER, + "Delay timer expired without a valid current request for SPBTARGET %p, " + "this should only occur if the request was already cancelled", + pTarget->SpbTarget); + + goto exit; + } + + NT_ASSERT(pRequest->SpbRequest != NULL); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Delay timer expired, ready to configure transfer for WDFDEVICE %p", + pDevice->FxDevice); + + ControllerConfigureForTransfer(pDevice, pRequest); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (pRequest->bIoComplete) + { + completeRequest = TRUE; + } + +exit: + + // + // Release the device lock. + // + + WdfSpinLockRelease(pDevice->Lock); + + // TODO: Remove this block. For the purpose of this + // skeleton sample, simply complete the request + // synchronously. This must be done outside of + // the locked code. + if (completeRequest) + { + PbcRequestComplete(pRequest); + } + + FuncExit(TRACE_FLAG_TRANSFER); +} + +VOID +PbcRequestComplete( + _In_ PPBC_REQUEST pRequest + ) +/*++ + + Routine Description: + + This routine completes the SpbRequest associated with + the PBC_REQUEST context. + + Arguments: + + pRequest - a pointer to the PBC request context + + Return Value: + + None. The request is completed asynchronously. + +--*/ +{ + FuncEntry(TRACE_FLAG_TRANSFER); + + NT_ASSERT(pRequest != NULL); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_TRANSFER, + "Completing SPBREQUEST %p with %!STATUS!, transferred %Iu bytes", + pRequest->SpbRequest, + pRequest->Status, + pRequest->TotalInformation); + + WdfRequestSetInformation( + pRequest->SpbRequest, + pRequest->TotalInformation); + + SpbRequestComplete( + pRequest->SpbRequest, + pRequest->Status); + + FuncExit(TRACE_FLAG_TRANSFER); +} |
