Windows on Snapdragon 使用指南(4)
3 怎么做
以下示例介绍了如何在 Snapdragon 上实现和测试 Windows 的现代待机模式。
3.1 现代(连接)待机模式
在 Snapdragon 上实施和测试将设备从 Windows 的现代待机模式唤醒
使用 GitHub 中的两个 Windows 驱动程序示例和以下说明,您可以在 Snapdragon 平台上实现网络通信并将设备从现代待机模式唤醒。驱动程序示例需要针对 Snapdragon 上的 Windows 进行修改,如下所示。
笔记
这是桌面应用程序的一个特殊用例,需要能够在现代待机期间进行通信,例如在现代待机期间唤醒设备。大多数应用程序不需要此功能,因此无需进行任何修改,桌面应用程序将继续正常工作。Microsoft Store 应用程序可以利用后台任务进行现代待机。您可以在“将应用程序与现代待机集成| Microsoft Learn ”以及 Microsoft 文档中的“什么是现代待机”中的其他背景信息。
安装驱动程序
如果驱动程序在开发过程中未签名,请使用测试证书或测试签名对其进行签名。请务必禁用驱动程序签名强制。
- 安装 WSK 驱动程序。
sc create echosrv type=kernel binpath=\echosrv.sy
-
从以下位置下载ndisprot630.inf https://github.com/Microsoft/Windows-driver-samples/tree/master/network/ndis/ndisprot/6x/sys/630
-
要安装 NDIS 协议驱动程序,请转至“网络连接”,选择一个适配器并打开“属性”。单击安装>协议> 添加>从磁盘安装。然后指向 .inf和驱动程序的位置。选择示例 NDIS 协议驱动程序。
echosrv — Winsock 内核 (WSK) 驱动程序
Microsoft 的 Echo 示例内核驱动程序使用 Windows 内核套接字。驱动程序侦听传入的 TCP IPv4/IPv6 请求并回显收到的数据包。GitHub 版本的唯一修改是用于将系统从睡眠状态唤醒的API ( PoSetSystemState )。 -
从https://github.com/microsoft/Windows-driver-samples/tree/master/network/wsk/echosrv获取驱动程序。
-
将以下行添加到\network\wsk\echosrv\wsksmple.c。API PoSetSystemState() 应添加到处理接收数据包的函数中。在本例中,这将是WskSampleOpReceive() 。可以在任何位置添加检查是否启用了 AoAC,以检测是否支持现代待机。
PoSetSystemState(ES_DISPLAY_REQUIRED | ES_USER_PRESENT | ES_SYSTEM_REQUIRED );
// Check if Always on Always connected is supported
POWER_PLATFORM_INFORMATION PlatformInfo = {
0};
NTSTATUS Status = ZwPowerInformation(PlatformInformation,
NULL,
0,
&PlatformInfo,
sizeof(PlatformInfo));
if (NT_SUCCESS(Status) && PlatformInfo.AoAc)
{
; // "AoAC is enabled!
}
else
{
; // "AoAC is NOT enabled!
}
- 修改后重新编译驱动。
ndisprot — NDIS 协议驱动程序
对此驱动程序的修改支持 LAN 唤醒 (WOL) 位图模式。其中包括使用OID_PM_ADD_WOL_PATTERN将特定位图模式添加到网络接口卡 (NIC),以及使用类似的 OID 来查询电源管理功能、查询模式并在需要时删除它们。这些更改是必要的,因为当 NIC 进入较低功耗状态 (D2/D3) 时,它会侦听具有特定预配置模式的数据包并丢弃所有其他数据包。
- 获取驱动程序 https://github.com/microsoft/Windows-driver-samples/tree/master/network/ndis/ndisprot/6x
- 将以下行添加到\network\ndis\ndisprot\6x\sys\ntdisp.c中的 switch (FunctionCode) 语句 :
case IOCTL_NDISPROT_REMOVE_WOL:
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL)
{
Status = ndisprotSetOidValue(
pOpenContext,
pIrp->AssociatedIrp.SystemBuffer,
pIrpSp->Parameters.DeviceIoControl.InputBufferLength
);
BytesReturned = 0;
NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus);
}
else
{
NtStatus = STATUS_DEVICE_NOT_CONNECTED;
}
break;
case IOCTL_NDISPROT_ADD_PAT:
PBYTE pBegin;
PBYTE pMask ;
PBYTE pPattern;
ULONG MaskSize = 6;
ULONG PatternSize = 48;
pBegin = (PBYTE)pIrp->AssociatedIrp.SystemBuffer + FIELD_OFFSET(NDISPROT_SET_OID, Data);
PNDIS_PM_WOL_PATTERN NdisPattern;
NdisPattern = (PNDIS_PM_WOL_PATTERN) pBegin;
memset(NdisPattern, 0, sizeof(NDIS_PM_WOL_PATTERN));
NdisPattern->Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
NdisPattern->Header.Revision = NDIS_PM_WOL_PATTERN_REVISION_2 ;
NdisPattern->Header.Size = NDIS_SIZEOF_NDIS_PM_WOL_PATTERN_REVISION_2 ;
NdisPattern->Priority = NDIS_PM_WOL_PRIORITY_NORMAL;
NdisPattern->WoLPacketType = NdisPMWoLPacketBitmapPattern;
WCHAR str[] = L"QCPat";
USHORT strLen = 10;
NdisPattern->FriendlyName.Length = strLen;
RtlCopyMemory(NdisPattern->FriendlyName.String, str, strLen);
// Put the mask buffer right after the structure
// The pattern buffer is located after the mask
NdisPattern->NextWoLPatternOffset = 0 ;
NdisPattern->WoLPattern.WoLBitMapPattern.MaskOffset = sizeof(NDIS_PM_WOL_PATTERN);
NdisPattern->WoLPattern.WoLBitMapPattern.PatternOffset = sizeof(NDIS_PM_WOL_PATTERN) + MaskSize;
NdisPattern->WoLPattern.WoLBitMapPattern.MaskSize = MaskSize;
NdisPattern->WoLPattern.WoLBitMapPattern.PatternSize = PatternSize;
// Each bit masks 1 byte in the pattern buffer.
// Here only the last 6 bytes in the pattern are unmasked
pMask = pBegin + sizeof(NDIS_PM_WOL_PATTERN);
memset(pMask , 0, MaskSize);
pMask[5] = 0xfc;
pPattern = pBegin + sizeof(NDIS_PM_WOL_PATTERN) + MaskSize ;
// Ethernet Header: 14 Bytes, IP Header: 20 Bytes, UDP Header : 8 Bytes
// Combined headers: 42 Bytes, so the payload is starting @ offset 42
memset(pPattern, 0, PatternSize);
pPattern[42] = 0x11;
pPattern[43] = 0x20;
pPattern[44] = 0x30;
pPattern[45] = 0x12;
pPattern[46] = 0x40;
pPattern[47] = 0x50;
NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED);
if (pOpenContext != NULL)
{
Status = ndisprotSetOidValue(
pOpenContext,
pIrp->AssociatedIrp.SystemBuffer,
pIrpSp->Parameters.DeviceIoControl.InputBufferLength
启动驱动程序
您可以选择通过修改.inf文件来自动启用驱动程序,也可以使用以下命令:
sc start echosrv
net start ndisprot
添加 WOL 模式
-
prottest.exe与 NDIS 驱动程序位于同一目录中。在 PowerShell 中,运行prottest -e以枚举设备 ID,如下所示。
-
要将 WOL 位图模式添加到 NIC,请运行prottest -a。在下面的示例中,prottest -a将位图模式添加到设备 ID 3。
- 不带参数的prottest显示帮助。
- prottest -q查询当前指定的任何模式。
- prottest -c返回支持的 WOL 数据包类型。
此时,echosrv 正在端口 40007 上监听 TCP 数据包,并且 WOL 位图模式已设置。当计算机处于睡眠模式时,与 WOL 模式匹配的数据包可以将其唤醒。
-
要进行测试,请在目标设备处于睡眠(S0 低功耗空闲连接)模式时,使用 PowerShell 脚本将 WOL 数据包和 TCP 数据包从远程设备发送到目标设备。例如:
Wakeup-Target -IPAddress "fe80::cd1d:936f:fcbf:2fc%12"
停止
- 使用以下命令停止驱动程序:
sc stop echosrv
net stop ndisprot
- 使用以下命令从 NIC 适配器属性中卸载 ndisprot:
sc delete echosrv
检查与排除故障
现代待机状态— 使用远程设备检查目标设备的现代待机状态。
- 确保两个设备可以通信;例如,通过互相 ping 通。
- 确保目标设备处于现代(连接)待机状态。它不应处于 S4(休眠)或 S5(关机)模式。
- Get-Service echosrv在目标设备上运行以确保 WSK 驱动程序正在运行。
- echosrv 驱动程序侦听端口 40007。在目标设备上运行以下命令以确认该端口上有侦听套接字。
Get-NetTCPConnection -LocalPort 40007
prottest.exe — 如果 prottest.exe 不起作用,您可能需要安装 Visual C++。
现代待机支持— 运行以下命令以确认支持并启用现代(连接)待机(S0 低功耗空闲连接):
Powercfg /a
例如:
c:\>powercfg /a
The following sleep state are available on this system:
Standby (S0 Low Power Idle) Network Connected
Fast Startup
电源状态— 运行以下命令来研究系统电源状态:
Powercfg /sleepstudy
例如:
c:\>powercfg /sleepstudy
Sleep Study report saved to file path c:\sleepstudy-report.html.
如下面的摘录所示,报告显示了每种模式(活动、待机、休眠、睡眠等)所花费的时间以及进入和退出每种状态的原因。