Skip to content

Commit

Permalink
SPI: workaround parent device initialization delay from S3 wakeup
Browse files Browse the repository at this point in the history
Fixes #203.
  • Loading branch information
imbushuo committed May 30, 2021
1 parent d0dcef1 commit e92581b
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 75 deletions.
101 changes: 72 additions & 29 deletions src/AmtPtpDeviceSpiKm/Device.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ AmtPtpDeviceSpiKmCreateDevice(
)
{
WDF_OBJECT_ATTRIBUTES DeviceAttributes;
WDF_OBJECT_ATTRIBUTES TimerAttributes;
PDEVICE_CONTEXT pDeviceContext;
WDF_TIMER_CONFIG TimerConfig;
WDFDEVICE Device;
NTSTATUS Status;

Expand All @@ -48,6 +50,8 @@ AmtPtpDeviceSpiKmCreateDevice(
pnpPowerCallbacks.EvtDevicePrepareHardware = AmtPtpEvtDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceD0Entry = AmtPtpEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = AmtPtpEvtDeviceD0Exit;
pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = AmtPtpEvtDeviceSelfManagedIoInitOrRestart;
pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = AmtPtpEvtDeviceSelfManagedIoInitOrRestart;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

// Create WDF device object
Expand Down Expand Up @@ -95,6 +99,16 @@ AmtPtpDeviceSpiKmCreateDevice(
goto exit;
}

//
// Create power-on recovery timer
//
WDF_TIMER_CONFIG_INIT(&TimerConfig, AmtPtpPowerRecoveryTimerCallback);
TimerConfig.AutomaticSerialization = TRUE;
WDF_OBJECT_ATTRIBUTES_INIT(&TimerAttributes);
TimerAttributes.ParentObject = Device;
TimerAttributes.ExecutionLevel = WdfExecutionLevelPassive;
Status = WdfTimerCreate(&TimerConfig, &TimerAttributes, &pDeviceContext->PowerOnRecoveryTimer);

//
// Retrieve IO target.
//
Expand Down Expand Up @@ -312,37 +326,11 @@ AmtPtpEvtDeviceD0Entry(

pDeviceContext = DeviceGetContext(Device);

// Attempt to enable SPI trackpad
// this might fail but can be retried later
// The state retains unless the power rail of trackpad
// has been cut
Status = AmtPtpSpiSetState(
Device,
TRUE
);

if (!NT_SUCCESS(Status))
{
TraceEvents(
TRACE_LEVEL_ERROR,
TRACE_DRIVER,
"%!FUNC! AmtPtpSpiSetState failed with %!STATUS!. Ignored anyway.",
Status
);

// Ignore anyway, but set unconfigured status
pDeviceContext->DeviceStatus = D0ActiveAndUnconfigured;
Status = STATUS_SUCCESS;
}
else
{
pDeviceContext->DeviceStatus = D0ActiveAndConfigured;
}
// We will configure the device in Self Managed IO init / restart routine
pDeviceContext->DeviceStatus = D0ActiveAndUnconfigured;

// Set time
KeQueryPerformanceCounter(
&pDeviceContext->LastReportTime
);
KeQueryPerformanceCounter(&pDeviceContext->LastReportTime);

TraceEvents(
TRACE_LEVEL_INFORMATION,
Expand Down Expand Up @@ -397,6 +385,38 @@ AmtPtpEvtDeviceD0Exit(
return Status;
}

NTSTATUS
AmtPtpEvtDeviceSelfManagedIoInitOrRestart(
_In_ WDFDEVICE Device
)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_CONTEXT pDeviceContext;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
pDeviceContext = DeviceGetContext(Device);

Status = AmtPtpSpiSetState(Device, TRUE);
if (!NT_SUCCESS(Status))
{
// In this case, we will retry after 5 seconds. Block any incoming requests.
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "%!FUNC! AmtPtpSpiSetState failed with %!STATUS!. Retry after 5 seconds", Status);
Status = STATUS_SUCCESS;
WdfTimerStart(pDeviceContext->PowerOnRecoveryTimer, WDF_REL_TIMEOUT_IN_SEC(5));
goto exit;
}
else
{
// Set time and status
pDeviceContext->DeviceStatus = D0ActiveAndConfigured;
KeQueryPerformanceCounter(&pDeviceContext->LastReportTime);
}

exit:
TraceEvents(TRACE_LEVEL_INFORMATION,TRACE_DRIVER, "%!FUNC! Exit, Status = %!STATUS!", Status);
return Status;
}

PCHAR
DbgDevicePowerString(
_In_ WDF_POWER_DEVICE_STATE Type
Expand Down Expand Up @@ -508,3 +528,26 @@ AmtPtpSpiSetState(

return Status;
}

void AmtPtpPowerRecoveryTimerCallback(
WDFTIMER Timer
)
{
WDFDEVICE Device;
PDEVICE_CONTEXT pDeviceContext;
NTSTATUS Status = STATUS_SUCCESS;

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
Device = WdfTimerGetParentObject(Timer);
pDeviceContext = DeviceGetContext(Device);

Status = AmtPtpSpiSetState(Device, TRUE);
if (NT_SUCCESS(Status))
{
// Triage request and set status
AmtPtpSpiInputIssueRequest(Device);
pDeviceContext->DeviceStatus = D0ActiveAndConfigured;
}

TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit, Status = %!STATUS!", Status);
}
10 changes: 10 additions & 0 deletions src/AmtPtpDeviceSpiKm/Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ typedef struct _DEVICE_CONTEXT

// Timer
LARGE_INTEGER LastReportTime;
WDFTIMER PowerOnRecoveryTimer;

// List of buffers
WDFLOOKASIDE HidReadBufferLookaside;
Expand Down Expand Up @@ -111,6 +112,11 @@ EVT_WDF_DEVICE_PREPARE_HARDWARE AmtPtpEvtDevicePrepareHardware;
EVT_WDF_DEVICE_D0_ENTRY AmtPtpEvtDeviceD0Entry;
EVT_WDF_DEVICE_D0_EXIT AmtPtpEvtDeviceD0Exit;

NTSTATUS
AmtPtpEvtDeviceSelfManagedIoInitOrRestart(
_In_ WDFDEVICE Device
);

PCHAR
DbgDevicePowerString(
_In_ WDF_POWER_DEVICE_STATE Type
Expand All @@ -122,4 +128,8 @@ AmtPtpSpiSetState(
_In_ BOOLEAN DesiredState
);

void AmtPtpPowerRecoveryTimerCallback(
WDFTIMER Timer
);

EXTERN_C_END
71 changes: 26 additions & 45 deletions src/AmtPtpDeviceSpiKm/Input.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,8 @@ AmtPtpSpiInputRoutineWorker(
{
NTSTATUS Status;
PDEVICE_CONTEXT pDeviceContext;
WDF_OBJECT_ATTRIBUTES Attributes;
BOOLEAN RequestStatus = FALSE;
WDFREQUEST SpiHidReadRequest;
WDFMEMORY SpiHidReadOutputMemory;
PWORKER_REQUEST_CONTEXT RequestContext;
pDeviceContext = DeviceGetContext(Device);

// This call is expected to happen after D0 entrance
if (pDeviceContext->DeviceStatus == D3) {
TraceEvents(
TRACE_LEVEL_WARNING,
TRACE_QUEUE,
"%!FUNC! Unexpected call while device is in D3 status"
);

WdfRequestComplete(PtpRequest, STATUS_UNSUCCESSFUL);
return;
}

Status = WdfRequestForwardToIoQueue(
PtpRequest,
pDeviceContext->HidQueue
Expand All @@ -45,6 +28,27 @@ AmtPtpSpiInputRoutineWorker(
return;
}

// Only issue request when fully configured.
// Otherwise we will let power recovery process to triage it
if (pDeviceContext->DeviceStatus == D0ActiveAndConfigured) {
AmtPtpSpiInputIssueRequest(Device);
}
}

VOID
AmtPtpSpiInputIssueRequest(
WDFDEVICE Device
)
{
NTSTATUS Status;
PDEVICE_CONTEXT pDeviceContext;
WDF_OBJECT_ATTRIBUTES Attributes;
BOOLEAN RequestStatus = FALSE;
WDFREQUEST SpiHidReadRequest;
WDFMEMORY SpiHidReadOutputMemory;
PWORKER_REQUEST_CONTEXT RequestContext;
pDeviceContext = DeviceGetContext(Device);

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, WORKER_REQUEST_CONTEXT);
Attributes.ParentObject = Device;

Expand All @@ -58,7 +62,7 @@ AmtPtpSpiInputRoutineWorker(
{
TraceEvents(
TRACE_LEVEL_INFORMATION,
TRACE_DRIVER,
TRACE_DEVICE,
"%!FUNC! WdfRequestCreate fails, status = %!STATUS!",
Status
);
Expand All @@ -75,7 +79,7 @@ AmtPtpSpiInputRoutineWorker(
{
TraceEvents(
TRACE_LEVEL_INFORMATION,
TRACE_DRIVER,
TRACE_DEVICE,
"%!FUNC! WdfMemoryCreateFromLookaside fails, status = %!STATUS!",
Status
);
Expand Down Expand Up @@ -104,7 +108,7 @@ AmtPtpSpiInputRoutineWorker(
{
TraceEvents(
TRACE_LEVEL_INFORMATION,
TRACE_DRIVER,
TRACE_DEVICE,
"%!FUNC! WdfIoTargetFormatRequestForInternalIoctl fails, status = %!STATUS!",
Status
);
Expand All @@ -116,7 +120,7 @@ AmtPtpSpiInputRoutineWorker(
if (SpiHidReadRequest != NULL) {
WdfObjectDelete(SpiHidReadRequest);
}

return;
}

Expand All @@ -136,7 +140,7 @@ AmtPtpSpiInputRoutineWorker(
{
TraceEvents(
TRACE_LEVEL_INFORMATION,
TRACE_DRIVER,
TRACE_DEVICE,
"%!FUNC! AmtPtpSpiInputRoutineWorker request failed to sent"
);

Expand Down Expand Up @@ -204,29 +208,6 @@ AmtPtpRequestCompletionRoutine(
SpiRequestLength
);

if (pDeviceContext->DeviceStatus == D0ActiveAndUnconfigured) {
Status = AmtPtpSpiSetState(
pDeviceContext->SpiDevice,
TRUE
);

if (!NT_SUCCESS(Status))
{
TraceEvents(
TRACE_LEVEL_ERROR,
TRACE_DRIVER,
"%!FUNC! AmtPtpSpiSetState failed with %!STATUS!.",
Status
);
}
else {
pDeviceContext->DeviceStatus = D0ActiveAndConfigured;
AmtPtpSpiInputRoutineWorker(pDeviceContext->SpiDevice, PtpRequest);
// Bypass PTP request completion
goto cleanup;
}
}

Status = STATUS_DEVICE_DATA_ERROR;
goto exit;
}
Expand Down
7 changes: 6 additions & 1 deletion src/AmtPtpDeviceSpiKm/Input.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ VOID
AmtPtpSpiInputRoutineWorker(
WDFDEVICE Device,
WDFREQUEST PtpRequest
);
);

VOID
AmtPtpSpiInputIssueRequest(
WDFDEVICE Device
);

0 comments on commit e92581b

Please sign in to comment.