#include "drv_common.h"
#pragma warning(disable: 4127)
#if (defined(ALLOC_PRAGMA) && defined(PAGING_ENABLED))
#pragma alloc_text(PAGE, Policy_InitPipe)
#pragma alloc_text(PAGE, Policy_InitPower)
#pragma alloc_text(PAGE, Policy_SetPower)
#pragma alloc_text(PAGE, Policy_GetPower)
#endif
#define mPipe_CheckValueLength(mStatus,mPolicyType,mPipeID,mMinValueLength,mCheckValueLength,mCheckValueUpdateLength,mErrorAction) do { \
if ((int)(mMinValueLength) > (int)(mCheckValueLength)) \
{ \
mStatus = STATUS_INVALID_BUFFER_SIZE; \
USBERRN("PipeID=%02Xh Invalid policy length %u for policy type %u. MinimumLength=%u", \
mPipeID,mCheckValueLength,mPolicyType,mMinValueLength); \
\
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
mErrorAction; \
} \
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
}while(0)
#define mPower_CheckValueLength(mStatus,mPolicyType,mMinValueLength,mCheckValueLength,mCheckValueUpdateLength,mErrorAction) do { \
if ((int)(mMinValueLength) > (int)(mCheckValueLength)) \
{ \
mStatus = STATUS_INVALID_BUFFER_SIZE; \
USBERRN("Invalid policy length %u for power policy type %u. MinimumLength=%u", \
mCheckValueLength,mPolicyType,mMinValueLength); \
\
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
mErrorAction; \
} \
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
}while(0)
#define mDevice_CheckValueLength(mStatus,mPolicyType,mMinValueLength,mCheckValueLength,mCheckValueUpdateLength,mErrorAction) do { \
if ((int)(mMinValueLength) > (int)(mCheckValueLength)) \
{ \
mStatus = STATUS_INVALID_BUFFER_SIZE; \
USBERRN("Invalid length %u for device information type %u. MinimumLength=%u", \
mCheckValueLength,mPolicyType,mMinValueLength); \
\
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
mErrorAction; \
} \
if (mCheckValueUpdateLength) ((PULONG)(mCheckValueUpdateLength))[0]=(ULONG)(mCheckValueLength); \
}while(0)
VOID Policy_SetAllPipesToDefault(__in PDEVICE_CONTEXT deviceContext)
{
PPIPE_CONTEXT pipeContext = NULL;
UCHAR interfaceIndex, pipeIndex;
UCHAR interfaceCount, pipeCount;
PAGED_CODE();
interfaceCount = deviceContext->InterfaceCount;
for (interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++)
{
pipeCount = deviceContext->InterfaceContext[interfaceIndex].PipeCount;
for (pipeIndex = 0; pipeIndex < pipeCount; pipeIndex++)
{
pipeContext = deviceContext->InterfaceContext[interfaceIndex].PipeContextByIndex[pipeIndex];
Policy_InitPipe(deviceContext, pipeContext);
}
}
}
VOID Policy_InitPipe(
__in PDEVICE_CONTEXT deviceContext,
__inout PPIPE_CONTEXT pipeContext)
{
if (pipeContext && pipeContext->PipePoliciesInitialized == FALSE)
{
PIPE_POLICIES defaultPolicies;
defaultPolicies.values = 0;
defaultPolicies.AllowPartialReads = TRUE;
defaultPolicies.IsoStartLatency = IsHighSpeedDevice(deviceContext) ? 32 : 8;
defaultPolicies.IsoAlwaysStartAsap = USB_ENDPOINT_DIRECTION_IN(pipeContext->PipeInformation.EndpointAddress) ? FALSE : TRUE;
pipeContext->Policies.values = defaultPolicies.values;
pipeContext->PipePoliciesInitialized = TRUE;
}
}
NTSTATUS Policy_GetPipe(__in PDEVICE_CONTEXT deviceContext,
__in UCHAR pipeID,
__in ULONG policyType,
__out PVOID value,
__inout PULONG valueLength)
{
PPIPE_CONTEXT pipeContext = NULL;
NTSTATUS status = STATUS_SUCCESS;
PIPE_POLICIES pipePolicies;
pipeContext = GetPipeContextByID(deviceContext, pipeID);
if (!pipeContext->IsValid || !pipeContext->Pipe || !pipeContext->Queue)
{
status = STATUS_INVALID_PARAMETER;
USBERRN("Invalid PipeID=%02Xh", pipeID);
return status;
}
if (!valueLength)
{
status = STATUS_INVALID_BUFFER_SIZE;
USBERRN("PipeID=%02Xh Invalid BufferLength.", pipeID);
return status;
}
RtlCopyMemory(&pipePolicies, &pipeContext->Policies, sizeof(pipePolicies));
switch(policyType)
{
case SHORT_PACKET_TERMINATE: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.ShortPacketTerminate;
break;
case AUTO_CLEAR_STALL: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.AutoClearStall;
break;
case PIPE_TRANSFER_TIMEOUT: mPipe_CheckValueLength(status, policyType, pipeID, sizeof(ULONG), valueLength[0], valueLength, goto Done);
if (value) ((PULONG)value)[0] = pipeContext->TimeoutPolicy;
break;
case IGNORE_SHORT_PACKETS: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.IgnoreShortPackets;
break;
case ALLOW_PARTIAL_READS: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.AllowPartialReads;
break;
case AUTO_FLUSH: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.AutoFlush;
break;
case RAW_IO: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.RawIO;
break;
case MAXIMUM_TRANSFER_SIZE: mPipe_CheckValueLength(status, policyType, pipeID, sizeof(ULONG), valueLength[0], valueLength, goto Done);
if (value) ((PULONG)value)[0] = pipeContext->PipeInformation.MaximumTransferSize;
break;
case RESET_PIPE_ON_RESUME: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.ResetPipeOnResume;
break;
case ISO_START_LATENCY: mPipe_CheckValueLength(status, policyType, pipeID, 2, valueLength[0], valueLength, goto Done);
if (value) ((PUSHORT)value)[0] = (USHORT)pipePolicies.IsoStartLatency;
break;
case ISO_ALWAYS_START_ASAP: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)pipePolicies.IsoAlwaysStartAsap;
break;
case ISO_NUM_FIXED_PACKETS: mPipe_CheckValueLength(status, policyType, pipeID, 2, valueLength[0], valueLength, goto Done);
if (value) ((PUSHORT)value)[0] = (USHORT)pipePolicies.IsoNumFixedPackets;
break;
case SIMUL_PARALLEL_REQUESTS: mPipe_CheckValueLength(status, policyType, pipeID, sizeof(ULONG), valueLength[0], valueLength, goto Done);
if (value) ((PULONG)value)[0] = pipeContext->SimulParallelRequests;
break;
default:
status = STATUS_INVALID_PARAMETER;
}
Done:
if (NT_SUCCESS(status))
{
if (value && valueLength)
{
USBMSGN("PipeID=%02Xh returned value of length %u for policy type %u",
pipeContext->PipeInformation.EndpointAddress, valueLength[0], policyType);
}
}
else
{
USBERRN("PipeID=%02Xh Failed appying policy type %u. Status=%08Xh",
pipeID, policyType, status);
}
return status;
}
NTSTATUS Policy_InitPower(__in PDEVICE_CONTEXT deviceContext)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
PAGED_CODE();
if (deviceContext->UsbVersionInfo.USBDI_Version < 0x600)
deviceContext->DeviceRegSettings.DeviceIdleIgnoreWakeEnable = FALSE;
if (deviceContext->RemoteWakeCapable || deviceContext->DeviceRegSettings.DeviceIdleIgnoreWakeEnable)
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleUsbSelectiveSuspend);
else
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0);
if (!deviceContext->DeviceRegSettings.UserSetDeviceIdleEnabled)
idleSettings.UserControlOfIdleSettings = IdleDoNotAllowUserControl;
else
idleSettings.UserControlOfIdleSettings = IdleAllowUserControl;
if (deviceContext->DeviceRegSettings.DeviceIdleEnabled && deviceContext->DeviceRegSettings.DefaultIdleState)
idleSettings.Enabled = WdfTrue;
else
idleSettings.Enabled = WdfFalse;
idleSettings.IdleTimeout = deviceContext->DeviceRegSettings.DefaultIdleTimeout;
if (idleSettings.Enabled || (!idleSettings.Enabled && deviceContext->IsIdleSettingsInitialized))
{
USBMSGN("Assigning IdleSettings. Enabled=%u IdleTimeout=%u UserControl=%s IdleCaps=%s",
idleSettings.Enabled,
idleSettings.IdleTimeout,
(idleSettings.UserControlOfIdleSettings == IdleAllowUserControl) ? "Allow" : "DoNotAllow",
(idleSettings.IdleCaps == IdleUsbSelectiveSuspend) ? "SelectiveSuspend" : "CannotWakeFromS0");
status = WdfDeviceAssignS0IdleSettings(deviceContext->WdfDevice, &idleSettings);
if ( !NT_SUCCESS(status))
{
USBERR("WdfDeviceSetPowerPolicyS0IdlePolicy failed. status=%Xh\n", status);
return status;
}
deviceContext->IsIdleSettingsInitialized = TRUE;
}
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
wakeSettings.Enabled = deviceContext->DeviceRegSettings.SystemWakeEnabled ? WdfTrue : WdfFalse;
wakeSettings.UserControlOfWakeSettings = wakeSettings.Enabled;
if (wakeSettings.Enabled)
{
USBMSGN("Assigning WakeSettings. Enabled=%u UserControl=%u",
wakeSettings.Enabled, wakeSettings.UserControlOfWakeSettings);
status = WdfDeviceAssignSxWakeSettings(deviceContext->WdfDevice, &wakeSettings);
if (!NT_SUCCESS(status))
{
USBERR("WdfDeviceAssignSxWakeSettings failed. status=%Xh\n", status);
return status;
}
}
return status;
}
NTSTATUS Policy_SetPower(__inout PDEVICE_CONTEXT deviceContext,
__in ULONG policyType,
__in PVOID value,
__in ULONG valueLength)
{
ULONG polBackup;
NTSTATUS status = STATUS_SUCCESS;
switch(policyType)
{
case AUTO_SUSPEND:
mPower_CheckValueLength(status, policyType, 1, valueLength, NULL, goto Done);
polBackup = deviceContext->DeviceRegSettings.DefaultIdleState;
deviceContext->DeviceRegSettings.DefaultIdleState = ((PUCHAR)value)[0] ? 1 : 0;
status = Policy_InitPower(deviceContext);
if (!NT_SUCCESS(status))
deviceContext->DeviceRegSettings.DefaultIdleState = polBackup;
break;
case SUSPEND_DELAY:
mPower_CheckValueLength(status, policyType, 4, valueLength, NULL, goto Done);
polBackup = deviceContext->DeviceRegSettings.DefaultIdleTimeout;
deviceContext->DeviceRegSettings.DefaultIdleTimeout = ((PULONG)value)[0];
status = Policy_InitPower(deviceContext);
if (!NT_SUCCESS(status))
deviceContext->DeviceRegSettings.DefaultIdleTimeout = polBackup;
break;
default:
USBERR("unknown power policy %d\n", policyType);
status = STATUS_INVALID_PARAMETER;
}
Done:
return status;
}
NTSTATUS Policy_GetPower(__in PDEVICE_CONTEXT deviceContext,
__in ULONG policyType,
__out PVOID value,
__inout PULONG valueLength)
{
NTSTATUS status = STATUS_SUCCESS;
switch(policyType)
{
case AUTO_SUSPEND:
mPower_CheckValueLength(status, policyType, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)deviceContext->DeviceRegSettings.DefaultIdleState;
break;
case SUSPEND_DELAY:
mPower_CheckValueLength(status, policyType, 4, valueLength[0], valueLength, goto Done);
if (value) ((PULONG)value)[0] = deviceContext->DeviceRegSettings.DefaultIdleTimeout;
break;
default:
USBERR("unknown power policy %d\n", policyType);
status = STATUS_INVALID_PARAMETER;
}
Done:
return status;
}
NTSTATUS Policy_GetDevice(__in PDEVICE_CONTEXT deviceContext,
__in ULONG policyType,
__out PVOID value,
__inout PULONG valueLength)
{
NTSTATUS status = STATUS_SUCCESS;
switch(policyType)
{
case DEVICE_SPEED:
mPower_CheckValueLength(status, policyType, 1, valueLength[0], valueLength, goto Done);
if (value) ((PUCHAR)value)[0] = (UCHAR)deviceContext->DeviceSpeed + 1;
break;
default:
USBERR("unknown device information type %d\n", policyType);
status = STATUS_INVALID_PARAMETER;
}
Done:
return status;
}
NTSTATUS Policy_SetPipe(
__inout PDEVICE_CONTEXT deviceContext,
__in UCHAR pipeID,
__in ULONG policyType,
__in PVOID value,
__in ULONG valueLength)
{
PPIPE_CONTEXT pipeContext = NULL;
NTSTATUS status = STATUS_SUCCESS;
PIPE_POLICIES pipePolicies;
ULONG tempVal;
pipeContext = GetPipeContextByID(deviceContext, pipeID);
if (!pipeContext->IsValid)
{
status = STATUS_INVALID_PARAMETER;
USBERRN("Invalid PipeID=%02Xh", pipeID);
return status;
}
pipePolicies.values = pipeContext->Policies.values;
switch(policyType)
{
case SHORT_PACKET_TERMINATE: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.ShortPacketTerminate = ((PUCHAR)value)[0] ? 1 : 0;
break;
case AUTO_CLEAR_STALL: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.AutoClearStall = ((PUCHAR)value)[0] ? 1 : 0;
break;
case PIPE_TRANSFER_TIMEOUT: mPipe_CheckValueLength(status, policyType, pipeID, sizeof(ULONG), sizeof(ULONG), NULL, goto Done);
pipeContext->TimeoutPolicy = ((PULONG)value)[0];
break;
case IGNORE_SHORT_PACKETS: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.IgnoreShortPackets = ((PUCHAR)value)[0] ? 1 : 0;
break;
case ALLOW_PARTIAL_READS: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.AllowPartialReads = ((PUCHAR)value)[0] ? 1 : 0;
break;
case AUTO_FLUSH: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.AutoFlush = ((PUCHAR)value)[0] ? 1 : 0;
break;
case RAW_IO: if (pipeContext->PipeInformation.PipeType != WdfUsbPipeTypeBulk &&
pipeContext->PipeInformation.PipeType != WdfUsbPipeTypeInterrupt)
{
status = STATUS_INVALID_PARAMETER;
break;
}
mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
tempVal = ((PUCHAR)value)[0];
if ((tempVal & 0x1) != pipePolicies.RawIO)
{
pipePolicies.RawIO = ((PUCHAR)value)[0] ? 1 : 0;
pipeContext->IsQueueDirty = TRUE;
}
break;
case RESET_PIPE_ON_RESUME: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.ResetPipeOnResume = ((PUCHAR)value)[0] ? 1 : 0;
break;
case ISO_START_LATENCY: mPipe_CheckValueLength(status, policyType, pipeID, 2, valueLength, NULL, goto Done);
pipePolicies.IsoStartLatency = ((PUSHORT)value)[0];
break;
case ISO_ALWAYS_START_ASAP: mPipe_CheckValueLength(status, policyType, pipeID, 1, valueLength, NULL, goto Done);
pipePolicies.IsoAlwaysStartAsap = ((PUCHAR)value)[0] ? 1 : 0;
break;
case ISO_NUM_FIXED_PACKETS: mPipe_CheckValueLength(status, policyType, pipeID, 2, valueLength, NULL, goto Done);
pipePolicies.IsoNumFixedPackets = ((PUSHORT)value)[0] & 0x7FF;
if (pipePolicies.IsoNumFixedPackets)
{
if (IsHighSpeedDevice(deviceContext))
{
if (pipePolicies.IsoNumFixedPackets % 8)
{
USBERRN("Number of fixed packets must be an interval of 8.");
status = STATUS_INVALID_PARAMETER;
break;
}
if (pipePolicies.IsoNumFixedPackets > 1024)
{
USBERRN("Number of fixed packets cannot be greater than 1024.");
status = STATUS_INVALID_PARAMETER;
break;
}
}
else
{
if (pipePolicies.IsoNumFixedPackets > 256)
{
USBERRN("Number of fixed packets cannot be greater than 256.");
status = STATUS_INVALID_PARAMETER;
break;
}
}
}
break;
case SIMUL_PARALLEL_REQUESTS:
mPipe_CheckValueLength(status, policyType, pipeID, 4, valueLength, NULL, goto Done);
if (pipeContext->SimulParallelRequests != ((PULONG)value)[0])
{
pipeContext->SimulParallelRequests = ((PULONG)value)[0];
pipeContext->IsQueueDirty = TRUE;
}
break;
default:
status = STATUS_INVALID_PARAMETER;
}
Done:
if (NT_SUCCESS(status))
{
USBMSGN("PipeID=%02Xh Applied value of length %u to policy type %u",
pipeContext->PipeInformation.EndpointAddress, valueLength, policyType);
pipeContext->Policies.values = pipePolicies.values;
}
else
{
USBERRN("PipeID=%02Xh Failed appying policy type %u. Status=%08Xh",
pipeID, policyType, status);
}
return status;
}