CE5.0关于设备管理器的源码分析

本文深入探讨了 Windows CE 下设备的加载、协同工作、电源管理及即插即用设备检测原理,揭示了 DEVICE.EXE 程序在设备管理中的核心作用,包括其内部结构、关键代码和流程分析。

转载自ARMCE论坛

 

系统下面许多设备,比如键盘,串口,触摸屏,硬盘......这些设备什么时候被系统使用的?这些设备怎样被系统使用的?加载过程是怎么样的?设备如何加入系统协同工作的?为什么系统能检测到并使用即插即用的设备(如usb鼠标)?系统是怎么控制设备的电源的?


CE管理设备的程序叫做DEVICE.EXE,这是一个独立的用户级进程,它主要负责跟踪,维护系统的设备信息并对设备资源进行调配.设备管理器包括即插即用设备管理,电源管理,io资源管理等等.

结构示意图:

目录树:c:/WINCE500/PRIVATE/WINCEOS/COREOS/DEVICE/

[DEVCORE]设备管理器的核心代码部分.
Devapi.c
Devcore.c
Devfile.c
Devfsd.c
Devload.c
Devpnp.c
Celogdev.h


[DEVMAIN]程序入口点.
devmain.c

[INC]头文件
Devmgrif.h
Devmgrp.h
Devzones.h
Iormif.h
Pmif.h

[IORM]io资源管理
Iorm.c

[NOPMIF]电源管理模块接口(这里是不要电源管理模块的'空'接口)
Nopmif.c

[PMIF]电源管理模块接口
Pmif.c

 

源码分析:

 

在[DEVMAIN]中有一个devmain.c的代码,这是device.exe的入口点.如同标准c的main()函数所在,因为是windows所以入口点是WinMain()这样的函数.在WinMain()内没有其他内容,只调用了StartDeviceManager()这个函数.

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nCmdShow)
{

int status;

status = StartDeviceManager(hInst, hPrevInst, lpCmdLine, nCmdShow);

return status;

}

前面WinMain()调用的StartDeviceManager()函数位于devcore.c. 这个函数初始化设备管理器,电源管理器,IO资源管理,然后启动设备管理器,开始负责设备驱动的加载和卸载工作.

int WINAPI

StartDeviceManager(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nCmShow)

{

HINSTANCE hCeddkDll;

HANDLE hEvent;

if (IsAPIReady(SH_DEVMGR_APIS)) {

return 0;

}

DEBUGREGISTER(NULL);

DEBUGMSG(ZONE_BOOTSEQ, (TEXT("DEVICE: Starting boot phase 1/n")));

// PHASE 1

g_BootPhase = 1; //用一个全局变量来表示现在处于启动的哪个阶段.一共有3个阶段.

// 0表示还没有开始启动;1表示在搜索注册表的值;2表示在加载设备;

// 3表示已经开始正常运行了.

InitOOMSettings(); // 初始化OOM.

InitializeListHead(&g_DevChain);// 初始化常规状态设备列表

InitializeListHead(&g_ActivatingDevs); // 初始化激活的已经注册的设备列表

InitializeListHead(&g_DyingDevs); // 初始化消亡状态的设备列表

InitializeListHead(&g_CandidateDevs); // 初始化正在加载的设备驱动列表.

g_hCleanEvt = CreateEvent(NULL, FALSE, FALSE, NULL);

g_hDevApiHandle = CreateAPISet("WFLD", NUM_FDEV_APIS, FDevApiMethods, FDevApiSigs);// 向系统注册api

g_hDevFileApiHandle = CreateAPISet("W32D", NUM_FAPIS, DevFileApiMethods, DevFileApiSigs); // 向系统注册api

RegisterAPISet(g_hDevFileApiHandle, HT_FILE | REGISTER_APISET_TYPE);

InitializePnPNotifications(); // 这个程序将调用StartDeviceNotifyThread()

// 这将启动一个线程.它的具体实现在devpnp.c文件中.

InitializeCriticalSection(&g_devcs); // 初始化临界区.

ResourceInitModule(); // 初始化一个全局变量gdwMaxDenseResources

// 根据注册表来初始化设备管理资源

ResourceInitFromRegistry(TEXT("Drivers//Resources"));

SetPowerOffHandler((FARPROC) DevMgrPowerOffHandler);

RegisterAPISet(g_hDevApiHandle, SH_DEVMGR_APIS);

InitDeviceFilesystems();

// Indicate that the device manager is up and running

hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, _T("SYSTEM/DevMgrApiSetReady"));

DEBUGCHK(hEvent != NULL);

if (hEvent != NULL) {

SetEvent(hEvent);// 设置一个全局事件.通知别的程序设备管理器api已经准备好了.

// 在其他的应用里面,使用设备管理器api前,如果希望确认设备管理器

// 的api已经注册好,它会等待这样一个事件.

CloseHandle(hEvent);

}

// Calibrate stall counter that is used for StallExecution

// 以下代码调用ceddk.dll里面的fnCalibrateStall()函数.

hCeddkDll = LoadLibrary (TEXT("ceddk.dll"));

if (NULL != hCeddkDll) {

pCalibrateStallFn fnCalibrateStall = (pCalibrateStallFn)/

GetProcAddress(hCeddkDll, TEXT("CalibrateStallCounter"));

if (!fnCalibrateStall) {

DEBUGMSG(ZONE_BOOTSEQ,(L"GetProcAddress failed on ceddk.dll/r/n"));

FreeLibrary(hCeddkDll);

}

else {

fnCalibrateStall();

}

}

// Call the power manager initialization entry point

PM_Init(); // 初始化电源管理模块.电源管理模块是一个pm.dll.它也被device.exe调用.这里// 暂时不打算涉及电源管理部分.简单来说:在PM_Init()函数里面会启动3个线程,

// PnpThreadProc,ResumeThreadProc, ActivityTimersThreadProc

// 它们各自负责监控即插即用,Resume,和激活定时器

PM_SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);//设置系统电源开.

// See if we are going to have two boot phases

// 根据注册表内容,决定采用怎样的加载过程.

// 如果存在SYSTEM/BootPhase1.分2个步骤来加载:

// 先DevloadInit()加载一次,然后等待通知后,继续调用InitDevice()加载.

// 如果没有SYSTEM/BootPhase1,则调用DevloadInit()一次完成.

// 也许是满足一些需要2次加载设备的情况,所以留下这个功能.

hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, _T("SYSTEM/BootPhase1"));

if (hEvent != NULL) {

// Load phase 1 drivers from the boot registry

DevloadInit(); // 加载设备.DevloadInit()函数位于devload.c,它首先删除注册表里面

// Device/Active,然后调用InitDevices()函数加载设备驱动.

// Signal boot phase 1 complete

SetEvent(hEvent); // 发出通知,阶段1已经完成.

CloseHandle(hEvent);

// Wait for phase 2 of the boot to begin

// 等待通知后进入阶段2

hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("SYSTEM/BootPhase2"));

DEBUGCHK(hEvent);

if (hEvent) {

DEBUGMSG(ZONE_BOOTSEQ, (TEXT("DEVICE: Started, waiting for boot phase 2/r/n")));

WaitForSingleObject(hEvent, INFINITE);

CloseHandle(hEvent);

}

// Load any new drivers from the persistent registry.Since the

// registry may have changed, update the power state for any devices

// that need it.

DEBUGMSG(ZONE_BOOTSEQ, (TEXT("DEVICE: Second-phase driver load/r/n")));

g_BootPhase = 2;

PM_SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);

InitDevices(NULL); // DevInit()加载会先清空注册表Acitve下的内容,

// 第二次加载不能把前面一次的清空,所以这里不能用DevInit().

DEBUGMSG(ZONE_BOOTSEQ, (TEXT("DEVICE: Startup sequence complete/r/n")));

SignalStartedUsingReg(); // SignalStarted call with the right args

} else {

DEBUGMSG(ZONE_BOOTSEQ, (TEXT("DEVICE: No boot registry - skipping to boot phase 2/n")));

g_BootPhase = 2;

DevloadInit();

SignalStarted(_wtol(lpCmdLine));

}

// Boot phase 3 isn't any different from phase 2; just marks that we got here.

DEBUGMSG(ZONE_BOOTSEQ,

(TEXT("DEVICE: Finished loading primary drivers - entering boot phase 3/n")));

g_BootPhase = 3; // 进入阶段3,同时也标志设备管理器开始运行了.

CELOG_DeviceFinished (); // 记录日志.

while (1) {

WaitForSingleObject(g_hCleanEvt, INFINITE);

// check for auto-deregister devs first as they may end up queuing

// themselves on the dying devs list

ProcessAutoDeregisterDevs();

ProcessDyingDevs();

ProcessDyingOpens();

}

return 1;// should not ever be reached

}

加载设备的函数,DevloadInit()事实上也是调用InitDevices来实现,只不过事先清空了Acitve下的注册表内容

void DevloadInit(void)

{

DEBUGMSG(ZONE_INIT, (TEXT("DEVICE!DevloadInit/r/n")));

#define PHASE_1_BUSNAME TEXT("BuiltInPhase1")

//

// Delete the HLM/Drivers/Active key since there are no active devices at

// init time.

// 清空注册表Active下的内容.

RegDeleteKey(HKEY_LOCAL_MACHINE, s_ActiveKey);

#ifdef DEBUG

v_NextDeviceNum = 0xFFFFFFF0;// expect wraparound

#else// DEBUG

v_NextDeviceNum = 1;

#endif// DEBUG

g_bSystemInitd = FALSE;

InitDevices(PHASE_1_BUSNAME);

}

InitDevices(LPCTSTR lpBusName )

{

HKEY RootKey;

DWORD status;

DWORD ValType;

DWORD ValLen;

TCHAR RootKeyPath[REG_PATH_LEN];

TCHAR BusName[DEVKEY_LEN];

REGINI reg[1];

DWORDdwRegCount=0;

//

// Open HLM/Drivers key

//打开注册表HLM/Drivers.

status = RegOpenKeyEx(

HKEY_LOCAL_MACHINE,

DEVLOAD_DRIVERS_KEY, //DEVLOAD_DRIVERS_KEY= "Drivers"

0,

0,

&RootKey);

if (status != ERROR_SUCCESS) {

DEBUGMSG(ZONE_ROOT|ZONE_ERROR,

(TEXT("DEVICE!InitDevices RegOpenKeyEx(%s) returned %d./r/n"),

DEVLOAD_DRIVERS_KEY, status));

return;

}

//

// Look for root key value; if not found use current Root Key as default,

// otherwise open new root key

// 查询注册表中 RootKey的值,一般这个值是Devices/BuildIn.将查询的值保存到RootKeyPath.

// 如果没有指定,则使用Devices来替代.

ValLen = sizeof(RootKeyPath);

status = RegQueryValueEx(

RootKey,

DEVLOAD_ROOTKEY_VALNAME, //DEVLOAD_ROOTKEY_VALNAME =RootKey

NULL,

&ValType,

(PUCHAR)RootKeyPath,

&ValLen);

RootKeyPath[ARRAYSIZE(RootKeyPath) - 1] = 0;

if (status != ERROR_SUCCESS) {

// Root key value not found, thus root key is Drivers

_tcscpy(RootKeyPath, DEVLOAD_DRIVERS_KEY); // 没有找到,使用Devices来替代.

}

// Close previous root key

RegCloseKey(RootKey);

DEBUGMSG(1,

(L"DEVICE!InitDevices: Root Key is %s./r/n",

RootKeyPath));

// 为ActiveDeviceEx准备参数reg. 如果输入参数lpBusName指定了,则从参数lpBusName获得,

// 否则从注册表中读取.

if (lpBusName!=NULL) {

reg[0].lpszVal = DEVLOAD_BUSNAME_VALNAME;

reg[0].dwType= DEVLOAD_BUSNAME_VALTYPE;

reg[0].pData= (PBYTE) lpBusName ;

reg[0].dwLen= (_tcslen( lpBusName ) + 1) * sizeof(TCHAR);

dwRegCount = 1;

}

else {

status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RootKeyPath,0,0, &RootKey);

if (status == ERROR_SUCCESS ) {

ValLen = sizeof( BusName);

status = RegQueryValueEx(

RootKey,

DEVLOAD_BUSNAME_VALNAME,

NULL,

&ValType,

(PUCHAR)BusName,

&ValLen);

if (status == ERROR_SUCCESS && ValType==DEVLOAD_BUSNAME_VALTYPE) {

// We found Bus Name. This is new bus driver model. So we use new format.

BusName[DEVKEY_LEN-1] = 0;

reg[0].lpszVal = DEVLOAD_BUSNAME_VALNAME;

reg[0].dwType= DEVLOAD_BUSNAME_VALTYPE;

reg[0].pData= (PBYTE)BusName;

reg[0].dwLen=ValLen;

dwRegCount = 1;

}

// Close previous root key

RegCloseKey(RootKey);

}

}

// Someday we'll want to track the handle returned by ActivateDevice so that

// we can potentially deactivate it later. But since this code refers only to

// builtin (ie, static) devices, we can just throw away the key.

if (dwRegCount)

ActivateDeviceEx(RootKeyPath,reg,dwRegCount,NULL);

else

ActivateDevice(RootKeyPath, 0);

}// InitDevices

至此,已经明确了系统是通过InitDevice()来加载驱动,更具体的InitDevice()会调用ActivateDeviceEx()来加载驱动,但是,这里只加载了一个驱动啊,难道系统就只加载一次?

这个唯一被加载的驱动是BusEnum.dll(在CE4.2是RegEnum.dll)这个驱动位于public下,它枚举了BuiltIn下所有的设备,逐个加载,并在Active下记录成功加载的设备.

 

 

 

附:

device.exe其实只加载一个流驱动,他就是busenum.dll,后者会遍历自己旗下的所有流驱动,然后按照order来建立需要active的列表,完后按照列表一个一个activedevice。
RAM based和Hive Base是注册表的两种实现方式。
RAM based就是实现用romimage把regini.ini注册表编译成二进制的default.fdf,然后bootloader加载CE后就常驻内存了,所以读写都在内存中,无法在coldboot后保存修改。
Hive based就是romimage把regini.ini编译成为三个hv文件,分别是boot.hv user.hv和system.hv:里面分别存放的是boot phase0/1需要的关键注册表,/HKEY_USERS下的注册表,和/HKEY_LOCAL_MACHINE等其他键值不在boot phase0需要的注册表。后面两个hv文件会在ce启动到phase2的时候在非易失存储介质上保留备份,注册表操作依然在RAM里面,但是如果有改动会flush到外部介质保存,下次启动CE会在phase2开始前检查外部介质中的hv并把修改过后的注册表覆盖内存中默认的注册表,实现注册表的修改功能。

cheat engine 7.3 源码 CE7.3源码 添加暗模式支持(更改设置时重新启动 CE) 所有保存的结果现在显示在发现列表中(可以关闭) 组现在可以支持指点通配符。(只有当字段是适当的指点时才有效) 如果重复定时器尚未完成,可以通过释放密钥和抑制来重复热键 结构解剖添加到地址列表使用地址弦而不是数字,因此符号将被保留 结构解剖现在有一个选项来保存列的先前状态,并显示更易于更改 中鼠标单击现在将结构元素的值复制到剪贴板中 添加 [$LUACODE] 块用于内联 Lua 编码 向 CE 添加了 c 编译器 在自动装配器中添加 [$C] 块。所有 [$C] 方块在执行前合并成一个脚本 添加 [$CCODE] 内联 C 编码的方块 (检查论坛、维基、CE 帕特里翁不和谐或 CE 的 youtube) 添加了 C#编译器(编译器) 添加例程以进行。NET(和单体)方法绕道而行。.NET 信息有一个新的上下文, 您可以在那里为自动组装器创建一个绕行模板 将调用方法添加到 .NET 信息窗口 [禁用] 部分现在可以参考标签、定义、AOBScan 结果和在 [ENABLE] 部分创建的分配 用户定义的符号列表具有 CCode 符号的次要列表 更改地址窗口现在也支持相对偏移 DBVM 速度改进 DBVM 具有额外的安全级别,并添加了dbvm_setKeys,以便轻松更改访问代码 DBVM 现在有一些嵌套 VM 的基本支持 (只有这样你才能运行它们, 还没有修改) 新的调试界面:DBVM 级调试器 改进"查找此地址的访问/写入"性能 解剖代码现在允许您指定自定义范围 如果记录是字符串类型,则地址列表值排序现在按字母顺序对值进行排序 多个条目的下拉列表现在可以同时更改 独立注册窗口现在也显示标志值 如果第一个值大于第二个值,则扫描之间的值现在自动摇号顺序 修复: 修复一些游戏冻结 CE 时,符号访问 Lua 调试现在显示循环变量 几个窗口现在保存他们的位置, 不会被损坏, 如果你不显示他们第一次运行 Ce 使用超时时固定创建阅读和停止 固定拆解 vcvtsi2s 固定比较第一次扫描, 如果它是一个大块, 并使它更有效 切共享:注销已修复 固定组装模组 固定的 ultimap ret 过滤器 固定卢阿管从不呼叫奥纳罗尔 64 位 CE 中的固定 vehdebug 在 32 位目标中将 FPU 寄存器归零 固定 DBVM 查找 AMD 上的访问/写入点 使用单行编辑器时不处理内存记录的固定撤消 加载表时固定隐藏儿童组选项 在中断和跟踪窗口中修复了一些字体问题 固定粘贴六角视图中的其他类型 修复符号加载器完全崩溃在未知 pdb 符号数据 卢亚: 保存表不再要求在表上签名 如果省略按钮列表,消息对话将有效。(然后默认到 mbok) 添加更多自定义按钮 注册系统不再错误出整个脚本的失败。它现在覆盖现有符号 还有很多其他的事情。 新功能: 形式. 保存到流编译 () 编译 () 签名扩展签名表符号列表. 获取模态列表符号列表. 获取模拟列表 memscan. 获取保存的执行处理器 memscan. 获取保存的签名列表保存的处理器类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值