您的位置:首页 > 编程语言

WinCE 编程实验(第12 章 驱动程序加载机制)

2009-01-08 09:55 316 查看
第12章

驱动程序加载机制

驱动程序加载机制在装置管理模块中是联系装置驱动程序和系统装置管理的中枢。从程序的角度讲,也可以印证这种关系:一,这个模块使用了大部分装置管理的数据结构,包括装置链接表、注册表和装置事件机制;二,它的实作相对于其它和驱动程序控制相关的函式的实作要复杂一些,并且是部分其它函式的基础;最后,它的实作被引入装置管理部分向系统导出的A P I函式。
本章涉及到的原始码包括在[CEROOT] / Private / Winceos / Coreos / Device / Lib / Devload. c中。

12.1 概述

Windows CE的实作是透过StartOneDriver函式把一个具体的驱动程序挂载到系统中,这个函式纯粹是一个内部函式,例如, Win32 API函式ActivateDeviceEx将透过这个函式实作。类似的还有几个其它的函式用于实作装置信息更新的通知、广播、实作装置的注销等,它们相对比较简单。
下面看一下系统在初始化期间,装置管理部分所做的工作。
系统初始化之后将加载device.exe(被放在slot 2中,请参考第2章的相关内容),在这个程序启动之后,系统会呼叫WinMain函式(device.c,参考第6章)。在WinMain函式中,系统将启动InitDevices函式(参考本章所附原始码L1005-L1061),这个函式基本上是线性的,没有什么分支,所做的工作主要是搜寻需要的注册根键,准备参数呼叫Win32 API接口ActivateDevice(L10 59)。ActivateDevice是定义在winbase.h (标准windows标头档之一)中的一个宏,它透过WIN32_DEV_CALL宏映像装置管理部分注册的Win32系统函式(注册用数据结构请参考第6章程式清单6 - 6),从程序中知道它的序号为7,函式指标被映像为0,这是因为系统直接将它转化为了对应1 2号接口(ActivateDeviceEx)的呼叫。这个呼叫被映像成为内部函式FS_Activa teDeviceEx(device.c),它将直接触发StartOneDriver。在整个初始化的过程中,StartOneDriver将根据注册表内的设定被呼叫多次来初始化不同的装置驱动程序。这个函式将引起一个特定的驱动程序加载过程,这些内容将在1 2 . 2中详细说明。
先看一下这个过程的基本步骤:
1) 从注册表中读取特定的值,准备并校验参数。
2) 设置Active键下的若干内容。

233

3) 根据是否有前缀来分别处理。
4) 呼叫装置管理器根据前面准备的参数注册驱动程序(呼叫RegisterDeviceEx函式)。
5) 设置Active下的相关键的值。
6) 对实作档案数据流接口的驱动做额外的初始化工作。
7) 将系统接口变化通知到相关的行程。
其中第4步的RegisterDeviceEx比较重要,它负责将驱动程序实体加载到系统中,这已经在6 . 2 . 1中做过说明。

12.2 StartOneDriver

StartOneDriver是个很长的函式(L0581—L1000),虽然这样,它的可读性还是比较好的,因为其中有比较多的整齐的语句用来存取注册表并检验参数的合理性。
下面将从三个方面考察这个函式实作的功能。

12.2.1 装置信息的获取和维护

装置信息均从注册表获取,信息可以分成两大类:一类为装置描述信息,包括装置前置(prefix)、驱动位置(DLL)、装置旗标(Flags)、索引(Index)以及装置周遭环境(Context),这部分内容是来自用户自定的注册表键值。图1 2 - 1显示了这些注册信息的组织结构。另一类是
装置活动信息,主要包括Active键下的各种装置动态信息。
小实验
使用Platform Builder导引工具建立一个平台,可以使用预设组态中的一个,例如P D A,尽量使功能简单以减少建立时间,平台基于x86 Emulator即可。编译并将建立的操作系统的debug版本映像下载到目标Emulator上。等系统引导成功后,使用远程注册表编辑工具(Tools选单下的Remote Registry Editor工具)连接目标机器,可以查看注册表的项目。
查看/ HKEY_LOCAL_MACHINE/Drivers/BuiltIn下面各个子键的值,注意系统输出的除错信息,寻找「Loading module device.exe at address」一行,可以看到如下的信息(根据组态的不同会不太一样):
4294767574 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module device.exe address 0x08010000-0x0801D000
4294767577 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!InitDevices: Root Key is
Drivers / BuiltIn .
4294767578 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!ActivateDeviceEx(Drivers
/BuiltIn) entered
4294767579 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module regenum.dll at
address 0x03F60000-0x03F65000 (RW data at 0x01FFD000-0x01FFD49C)
4294767582 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!ActivateDeviceEx(Drivers
/BuiltIn/PM) entered
234
第12章 驱动程序加载


图12-1 注册表项目
4294767586 PID:83fcd802 TID:83fd4a36 0x83fd1800: PNP interface class {A32942B7-
920C-486b-B0E6-92A702A99B35} (PWR0:) ATTACH
4294767589 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!ActivateDeviceEx(Drivers
/BuiltIn/Serial) entered
4294767591 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module ceddk.dll at
address 0x03DE0000-0x03DE4000 (RW data at 0x01FE3000-0x01FE3034)
Loaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/CEDDK.DLL'
4294767592 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module com16550.dll at
address 0x03920000-0x03933000 (RW data at 0x01F86000-0x01F86548)
Loaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/COM16550.DLL'
Loaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/PCMCIA.DLL'
Unloaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/PCMCIA.DLL'
4294767594 PID:83fcd802 TID:83fd4a36 0x83fd1800: SerInit - Devindex 0, SysIntr 19,
IOB 2F8, IOLen 8
4294767595 PID:83fcd802 TID:83fd4a36 0x83fd1800: Ser_InternalMapRegisterAddresses :
HalTranslateBusAddress - OK
4294767595 PID:83fcd802 TID:83fd4a36 0x83fd1800: Ser_InternalMapRegisterAddresses : IO Space
4294767595 PID:83fcd802 TID:83fd4a36 0x83fd1800: SerInit - SYSINTR 19
4294767596 PID:83fcd802 TID:83fd4a36 0x83fd1800: PNP interface class {f8a6ba98-
087a-43ac-a9d8-b7f13c5bae31} (COM1:) ATTACH
4294767597 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!ActivateDeviceEx(Drivers
/BuiltIn/PCMCIA) entered
4294767599 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module pcmcia.dll address 0x03950000-0x0396E000 (RW data at 0x01F8A000-0x01F8AA94)
4294767600 PID:83fcd802 TID:83fd4a36 0x83fd1800: PCMCIA.DLL DLL_PROCESS_ATTACH
4294767750 PID:83fcd802 TID:83fd4a36 0x83fd1800: PnP ISA InitBusInfo : 0 card (s) found
4294767750 PID:83fcd802 TID:83fd4a36 0x83fd1800: PCMCIA:IsValidPCICSig Invalid
CHIP_REVISION = 0xff at 0x3e0!!!
4294767750 PID:83fcd802 TID:83fd4a36 0x83fd1800: PCMCIA:IsValidPCICSig Invalid
CHIP_REVISION = 0xff at 0x3e2!!!
4294767751 PID:83fcd802 TID:83fd4a360x83fd1800 : PCMCIA:InitCardSvc PDCardInitServices returned 1
4294767751 PID:83fcd802 TID:83fd4a36 0x83fd1800: PCMCIA.DLL DLL_PROCESS_DETACH
4294767751 PID:83fcd802 TID:83fd4a36 0x83fd1800: <<< Unloading module pcmcia.dll at
address 0x03950000-0x0396E000 (RW data at 0x01F8A000-0x01F8AA94)
4294767754 PID:83fcd802 TID:83fd4a36 0x83fd1800: DEVICE!ActivateDeviceEx(Drivers/BuiltIn/NDIS) entered
4294767756 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module ndis.dll at
address 0x03CD0000-0x03D1D000 (RW data at 0x01FD4000-0x01FD4A9C)
Loaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/NDIS.DLL'
4294767757 PID:83fcd802 TID:83fd4a36 0x83fd1800: NDIS: RebindAdaptersOnResume = 0
4294767762 PID:83fcd802 TID:83fd4a36 0x83fd1800: >>> Loading module vmini.dll at
address 0x03C90000-0x03C99000 (RW data at 0x01FCC000-0x01FCC680)
Loaded symbols for 'U:/CE/T2/RELDIR/CEPC__X86DEBUG/VMINI.DLL'
. . . . . . . . . . . . . . . . . . . . . . . .
这是实际的驱动程序模块加载过程中输出的信息,对比注册表和这些信息,看看哪些因素影响这些模块的加载顺序。透过观察可以发现,RegEnum.dll是BuildIn键的DLL值,它也是最早加载的模块。
注册表列举器(RegEnum.dll)汇出了Init和Deinit函式。它透过读取注册表项发现新装置。因为当前一次呼叫未完成时,它能被再次呼叫,所以可支持阶层化的存取方法。当它被卸载时,任何它直接加载的资源会被同时卸载。注册列举器检查注册表传给它的根键,用来搜寻描述加载装置的子键。在这些键中,有的子键包括内建和本地装置、ISA装置、PCI总线,以及诸如ND IS的虚拟装置,诸如SNMP 选择性的协议元素或点对点的协议,还有其它的非列举O S组件。

RegEnum.dll按照顺序检查传给它键下面的第一层键。在每个找到的子键上呼叫ActivateDeviceEx。每一个子键可能有被ActivateDeviceEx或最终呼叫的驱动程序直译成任何值。另外,每个子键有一个从0到255的序列值。最小的序列值最先被载入。如果没有序列值,驱动程序将在最后加载。这个顺序可以透过前面的小实验来检查。
实作了档案数据流接口的驱动程序使用前缀的值寻找驱动程序起始点,例如当前缀为COM时,起始符号为COM_Init。若没有前缀则使用Init作为预设的初始点。
系统初始化时,RegEnum.dll一个接一个扫瞄HKEY_LOCAL_MACHINE/Drivers/BuiltIn子键或HKEY_LOCAL_MACHINE/Drivers/RootKey的值指向的地方并且为每个项目初始化装置。它用DLL的值加载DLL,然后在HKEY_LOCAL_MACHINE/Drivers/Active下为驱动程序建立子键。接着呼叫Init函式或驱动程序表项的指标把包含子键的字符串传递过去。XXX_Init接口函式能用得到的字符串呼叫RegOpenKeyEx得到此键的识别号,并呼叫RegQueryValueEx寻找键值,键值包含响应注册键的串行,注册键则是HKEY_LOCAL_MACHINE/Drivers/Buil tIn下对应的键。通常装置驱动的注册键中还包含其它的值,这些值指定类似I/O端口地址、IRQ设置、DMA设置,或其它附加的信息。

12.2.2 存取核心装置管理数据表

存取系统装置管理数据串行主要是为了使用分配Index的值,这种情况只发生在注册表中包含了前缀值而没有包含Index值的情况,这种情况多见于实作了数据流接口的驱动程序中,系统允许多个这种驱动程序的实体和同一个装置对应,它们的Index可以由系统自动分配,只可能为0 ~ 9,而要允许多个非数据流式的驱动程序的实体则必须指定Index值。
有两条数据串行会被存取: g_DevChain和g_CandidateDevs(参考L0821—L0934)。
StartOneDriver并不修改g_DevChain,它存取这个数据结构主要是为了检查当前加载的装置
驱动程序和已有的是否冲突,方法就是沿着串行逐项对比。g_CandidateDevs则是一条辅助串行,
它的作用主要是处理多个StartOneDriver并行的情况。
这一段原始码目标是寻找一个可用的Index的值,搜寻方案是首先在g_DevChain上搜寻,若没有可用的,则继续在g_CandidateDevs上找,若仍没有(若串链为空当然不会找到),则呼叫CreateCandidateDev产生一个,这样可用的Index一定可以找到。

12.2.3 系统事件的传递

StartOneDriver透过PnpProcessInterfaces将装置驱动接口更新事件发布到注册通知的行程中。PnpProcessInterfaces函式透过NotifyAll实作这一点。关于NotifyAll的详细情况请参看第6章。

12.3 主要来源程序清单以及情景注释

以下原始码摘自[CEROOT] /Private/Winceos/Coreos/Device/Lib/Devl oad.c,为节省篇幅,已经省略了若干行除错信息,和本章内容没有直接关联的原始码也省略,另一方面为了便于阅读,保持内容的完整性,主要原始码的注释保持和原始文件相同的格式,只是在必要的地方增加了一些中文说明。行号和原始文件的行号保持一致,因为原始码有删节,所以行号有时会不连续,如果需要,读者可以自己查阅原始原始码。
0001 - 0008 //版权信息
0009 - 0013 //文件内容说明
0014 - 0038 // includes 以及其它说明
0039
0040 extern CRITICAL_SECTION g_devcs; // device.c
0041 extern LIST_ENTRY g_DevChain; // device.c
0042 extern LIST_ENTRY g_CandidateDevs; // device.c
0043 extern DWORD g_BootPhase; // device.c
因为要直接存取装置信息,所以声明一下定义在device.c中的两条装置相关的串行、临界区识别号以及跟踪启动阶段(为了不同处理)的标志
0050 const TCHAR s_DllName_ValName[] = DEVLOAD_DLLNAME_VALNAME; //"DLL"
0051 const TCHAR s_ActiveKey[] = DEVLOAD_ACTIVE_KEY; //"Drivers/Active"
0052 const TCHAR s_BuiltInKey[] = DEVLOAD_BUILT_IN_KEY; //"Driver/BuiltIn"
这里是一组常数定义,它们的值已经注释在后面了,这三个字符串为TCHAR类型,TCHAR宏是Windows平台为支持国际化定义的类型映射,一般情况下它可以被映像为8位字符(char或者unsigned char)或者1 6位的字符(WCHAR,short或者unsigned short )。在Windows CE中WCHAR一般为1 6位字符。三个变量定义了几个注册键名:DLL键记录驱动程序所在的dll文件,Active键跟踪装置活动信息,BuiltIn跟踪装置基本信息。
0505
0506 // create a structure to keep track of device prefix/index combinations while we install
0507 // the device and add it to the list of candidate devices. Make sure to hold g_devcs
0508 // when this routine is called. Return a pointer to the structure if successful or NULL
0509 // if there's a problem.
0510 fscandidatedev_t *
0511 CreateCandidateDev(WCHAR *pszPrefix, DWORD dwPrefixLen, DWORD dwIndex)
0512 {
0513 fscandidatedev_t *pdc = NULL;
0514
0515 // sanity check parameters
0516 if(dwPrefixLen <= sizeof(pdc->szPrefix)) {
0517 // allocate a structure
0518 pdc = LocalAlloc(LPTR, sizeof(*pdc));
0519 if(pdc != NULL) {
0520 // init structure members
0521 memset(pdc, 0, sizeof(*pdc));
0522 memcpy(pdc->szPrefix, pszPrefix, dwPrefixLen);
0523 pdc->dwPrefixLen = dwPrefixLen;
0524 pdc->dwIndex = dwIndex;
0525
0526 // add this structure to the list
0527 InsertTailList(&g_CandidateDevs, (PLIST_ENTRY) pdc)
0528 }
0529 }
0530
0531 return pdc;
0532 }
0533
这个函式构造fscandidatedev_t串行来跟踪正在分配的Prefix/Index对,构造的结果作为指标回传。因为串行基本类型fscandidatedev_t的开头为LIST_ENTRY结构,在实际的使用中可以将前者的实体作为后者实体使用,实际上,这种形式在C++编译器的类别继承机制中经常被使用,于是,操作LIST_ENTRY类型的函式可能被用于fscandidatedev_t类型的串行。
函式需要校验参数值的合法性,做法是比较dwPrefixLen和为szPrefix所分配空间大小,后者是一个WCHAR类型的字符串,长度为3个字符,则实际上参数要求dwPrefixLen小于3,原始码并没有在这里引入常数是出于扩展性的考虑,因为以后这个字符串的长度可能改变,这样,当fscandidatedev_t中的定义改变之后,这里不用做任何变化,也可以正常使用。
之后,函式为局部指针变量分配空间,并且将参数的值指派给相对应的结构变量。类似的原始码有两种典型的错误,第一是字符串的指派值并没有拷贝内容而直接用指针指派值,二是没有考虑分配空间可能失败。前者通常会导致外部存取返回的结构是失败的指针存取,发生错误的距离可能很远,甚至很少出错,但这是很危险的;后者一般情况下不会导致失败,只有在系统内存严重不足的时候才会构成威胁,但是恶意的程序会导致这种失败,使系统不稳定。在操作系统中,类似的检测和容错原始码比较多,这是系统稳固性的基本保障手段之一。
0534 // Look for a member of the candidate device list that matches this one. Return a pointer
0535 // to the matching entry or NULL if no match is found . Makesure g_devcs when
0536 // calling this routine.
0537 fscandidatedev_t *
0538 FindCandidateDev(WCHAR *pszPrefix, DWORD dwPrefixLen, DWORD dwIndex)
0539 {
0540 fscandidatedev_t *pdc;
0541
0542 // look for a match
0543 for(pdc = (fscandidatedev_t *) g_CandidateDevs.Flink;
0544 pdc != (fscandidatedev_t *) &g_CandidateDevs;
0545 pdc = (fscandidatedev_t *) pdc->list.Flink) {
0546 if(pdc->dwPrefixLen == dwPrefixLen
0547 && pdc->dwIndex == dwIndex
0548 && memcmp(pdc->szPrefix, pszPrefix, dwPrefixLen) == 0) {
0549 // found a match, get out
0550 break;
0551 }
0552 }
0553 // did we find a match?
0554 if(pdc == (fscandidatedev_t *) &g_CandidateDevs) {
0555 pdc = NULL; // no, return NULL
0556 }
0557
0558 return pdc;
0560 }
0561
FindCandidateDev是一个典型的搜索函式,因为在操作系统中有大量的串行型数据结构,所以类似的根据一定信息搜寻节点搜索函式比较多,各个主要模块都会有。这里的搜索比较简单,就是在单向串行上逐项比较,需要注意的是字符串比较时要对内容进行比较(这里是储存区内容比较),而不是指标。
请注意,这个函式存取了全域变数,然而它并没有对数据的存取进行保护,因而这里存在潜在的危险,当多个行程同时呼叫这个函式时,可能出问题。不过只要对这个函式的使用稍加限制即可消除前面的问题,即这个函式的呼叫必须在保护这个数据的临界区内, StartOneDriver正是这么做的,所以,这个函式是一个为StartOneDriver写的辅助函式,它不可以轻易被无保护的呼叫。另一方面,即便是保护也需要注意不能引起死结。
0562 // remove an entry from the list of candidate device structures and free its associated
0563 // memory. Make sure to hold g_devcs when calling this function.
0564 void
0565 DeleteCandidateDev(fscandidatedev_t * pdc)
0566 {
0567 // sanity check
0568 if(pdc != NULL) {
0569 RemoveEntryList((PLIST_ENTRY) pdc);
0570 LocalFree(pdc);
0571 }
0572 }
删除之前需要检验被释放的指标是否为空,虽然这不能完全避免释放无效指标,但是这里的判断杜绝了释放空指标的可能。从呼叫情况看,这个函式的参数内容会涉及到全域变量,因而有类似FindCandidateDev的问题,此处不再赘述。
0574 //
0575 // Function to RegisterDevice a device driver and add it to the active device list
0576 // in HLM/Drivers/Active and then s i g n a l t h e s y s t e m t h a t a new device is available .
0577 //
0578 // Return the HANDLE from RegisterDevice.
0579 //
这个函式很长,但是读懂它并不困难,原因有两个:其一,这个函式功能比较单纯,有很多存取注册表数据的部分均有相同的模式,很容易举一反三;其二,由于函式呼叫时参数的书写风格(每行一个参数),虽然使原始码量看上去比较大而冗长,这因为参数的个数比较多,这样做反而增加了原始码的可读性。
0580 HANDLE
0581 StartOneDriver(
0582 LPCTSTR RegPath,
0583 DWORD LoadOrder,
0584 LPCREGINI lpReg,
0585 DWORD cReg,
0586 LPVOID lpvParam
0587 )
这里简要地对参数做些说明。RegPath指明了起始的根键位置,LoadOrder指明序号,后面三个和驱动程序自己定义的周遭环境变量有关。
0588 {
0589 BOOL bUseContext; //是否使用已有驱动程序的周遭环境
0590 BOOL bFoundIndex; //是否找到了可用的Index值
0591 BOOL bHasPrefix; //是否有Prefix
0592 HKEY ActiveKey; // Active键
0593 HKEY DevKey; // Devices键
0594 DWORD Context; //以下为同名键键值
0595 DWORD Disposition; //
0596 DWORD Handle; //
0597 DWORD Index; //
0598 DWORD Flags; //
0599 DWORD status; //
0600 DWORD ValLen; //
0601 DWORD ValType; //
0602 DWORD ActiveId; //
0603 LPTSTR str; //临时字符串变量
0604 TCHAR ActivePath[REG_PATH_LEN]; // Active键的路径
0605 TCHAR DevDll[DEVDLL_LEN]; // DLL指定的dll名称
0606 TCHAR DevName[DEVNAME_LEN]; //装置名
0607 TCHAR Prefix[DEVPREFIX_LEN]; //前置名
0608 fsdev_t * lpdev; //指向装置串行的临时指针
0609 fscandidatedev_t * lpcandidatedev = NULL;; //指向被选装置串行的临时指针
0610
0614 //
0615 // Get the required (dll) and optional (prefix, index, flags, and context) values.
0616 //
下面的若干行透过RegOpenKeyEx函式获取prefix、index、Flags以及context的值。status变量接受回传值,并用于错误检查,若关键值不存在,则回传异常。通常,这些判断分支内都是输出除错信息的好地方,未删原始码正是这么处理的。
0617 status = RegOpenKeyEx( // 打开注册表相关的键,识别码为DevKey
0618 HKEY_LOCAL_MACHINE,
0619 RegPath,
0620 0,
0621 0,
0622 &DevKey);
0623 if (status) {
0627 return NULL;
0628 }
0629
0630 // Read Flags value first (if it exists). If DEVFLAGS_NOLOAD or not correct boot phase,
0631 // return w/o checking other parameters.
0632 ValLen = sizeof(Flags);
0633 status = RegQueryValueEx(
0634 DevKey,
0635 DEVLOAD_FLAGS_VALNAME,
0636 NULL,
0637 &ValType,
0638 (PUCHAR)&Flags,
0639 &ValLen);
0640 if (status != ERROR_SUCCESS) {
0644 Flags = DEVFLAGS_NONE; // default is no flags set
0645 }
0646 if (Flags & DEVFLAGS_NOLOAD) {
0650 RegCloseKey(DevKey);
0651 return NULL; // Really success, but we cannot distinguish success at
0652 // deliberately not loading from a failure to load.
0653 }
0654 if ((Flags & DEVFLAGS_BOOTPHASE_1)&&(g_BootPhase > 1)) {
0658 RegCloseKey(DevKey);
0659 return NULL; // Same caveat as above.
0660 }
0661
存取Flags键的内容。这个键可以没有,它的默认值为DEVFLAGS_NONE,之后根据这个值检查两件事: 1)若标记为DEVFLAGS_NOLOAD,则表示这个驱动程序不需要加载,直接返回; 2)驱动加载时机不对,也返回。
0663 // Read DLL name
0664 ValLen = sizeof(DevDll);
0665 status = RegQueryValueEx(
0666 DevKey,
0667 s_DllName_ValName,
0668 NULL,
0669 &ValType,
0670 (PUCHAR)DevDll,
0671 &ValLen);
0672 if (status != ERROR_SUCCESS) {
0676 RegCloseKey(DevKey);
0677 return NULL; // dll name is required
0678 }
0679
取DLL的值并存放在DEVDLL中。这个变量最多保存DEVDLL_LEN为64的字符,因而注册表中DLL名字不能超过64,否则会溢出。这个值是必需的,否则无法载入,并返回错误NU LL。
0680
0681 // Read prefix value, if it exists.
0682 bHasPrefix = TRUE;
0683 ValLen = sizeof(Prefix);
0684 status = RegQueryValueEx(
0685 DevKey,
0686 DEVLOAD_PREFIX_VALNAME,
0687 NULL,
0688 &ValType,
0689 (PUCHAR)Prefix,
0690 &ValLen);
0691 if (status != ERROR_SUCCESS) {
0695 bHasPrefix = FALSE;
0696 }
0697
取Prefix值并设置bHasPrefix标记。这个标记对后面的处理有很大影响,稍后可以看到。
0698 //
0699 // Read the optional index and context values
0700 //
0701 ValLen = sizeof(Index);0702 status = RegQueryValueEx(
0703 DevKey,
0704 DEVLOAD_INDEX_VALNAME,
0705 NULL,
0706 &ValType,
0707 (PUCHAR)&Index,
0708 &ValLen);
0709 if (status != ERROR_SUCCESS) {
0713 Index = (DWORD)-1; // devload will find an index to use
0714 }
取Index的值,若没有,设为- 1。这是一个标记,表示需要系统自动分配一个Index值。
0716 bUseContext = TRUE;
0717 ValLen = sizeof(Context);
0718 status = RegQueryValueEx(
0719 DevKey,
0720 DEVLOAD_CONTEXT_VALNAME,
0721 NULL,
0722 &ValType,
0723 (PUCHAR)&Context,
0724 &ValLen);
0725 if (status != ERROR_SUCCESS) {
0729 bUseContext = FALSE; // context is pointer to active reg path string
0730 }
0731
取Context的值并设置bUseContext标记。
0732 //
0733 // Format the key's registry path (HLM/Drivers/Active/nnnn)
0734 //
0735 ActiveId = InterlockedIncrement(&v_NextDeviceNum) - 1;
0736 wsprintf(ActivePath, TEXT("%s//%02d"), s_ActiveKey, ActiveId);
ActiveId是当前活动驱动的序号,由系统自动计数产生,ActivePath将由它产生,例如「Drivers/Active/04」是可能的ActivePath值。s_ActiveKey实际上是常数「Driver/Active」。
0741 //
0742 // Create a new key in the active list
0743 //
0744 status = RegCreateKeyEx(
0745 HKEY_LOCAL_MACHINE,
0746 ActivePath,
0747 0,
0748 NULL,
0749 REG_OPTION_NON_VOLATILE,
0750 0,
0751 NULL,
0752 &ActiveKey, // HKEY result
0753 &Disposition);
0754 if (status) {
0758 RegCloseKey(DevKey);
0759 return NULL;
0760 }
0761
根据构造的ActivePath打开注册键,识别码保存在ActiveKey中。
0762 //
0763 // Default context is registry path
0764 //
0765 if (bUseContext == FALSE) {
0766 Context = (DWORD)ActivePath;
0767 }
bUseContext本来已经完成,后面可以看见对它的重新利用,不过意义已经不同了。在Windows CE .NET注册表中没有定义Context不代表没有,预设的就是ActivePath。
0770 //
0771 // Install requested values in the device's active registry key.
0772 // (handle and device name are added after RegisterDevice())
0773 //
0774 lpReg = MapCallerPtr((LPVOID)lpReg, sizeof(REGINI) * cReg);
0775 if (cReg != 0 && lpReg == NULL) {
0778 cReg = 0; // cause all entries to be ignored
0779 }
0780 while (cReg--) {
0781 _try {
0782 status = RegSetValueEx(
0783 ActiveKey,
0784 ValidateString(lpReg[cReg].lpszVal),
0785 0,
0786 lpReg[cReg].dwType,
0787 MapCallerPtr(lpReg[cReg].pData, lpReg[cReg].dwLen),
0788 lpReg[cReg].dwLen);
0789 } __except (EXCEPTION_EXECUTE_HANDLER) {
0790 status = ERROR_INVALID_ACCESS;
0791 }
0797 }
0798
根据参数表分析结果将用户自定义的周遭环境变量设置到注册表的相应部分。
0799 //
0800 // Registry path of the device driver (from HLM/Drivers/BuiltIn or HLM / Drivers / PCMCIA )
0801 //
0802 if (RegPath != NULL) {
0803 status = RegSetValueEx(
0804 ActiveKey,
0805 DEVLOAD_DEVKEY_VALNAME,
0806 0,
0807 DEVLOAD_DEVKEY_VALTYPE,
0808 (PBYTE)RegPath,
0809 sizeof(TCHAR)*(_tcslen(RegPath) + sizeof(TCHAR)));
0815 }
0816
为对应的PCMCIA或者BuiltIn键下的驱动设置Key的值。
0817 // Non-stream drivers must specify the index explicitly in order to load
0818 // multiple instances of the same driver. This is to prevent accidents because
0819 // non-stream drivers typically represent interfaces that multiplex functionality
0820 // over many physical devices.
0821 if (!bHasPrefix && Index == -1) {
0822 Index = 0;
0823 }
上面这个判断语句保证在没有注册表Index而且没有前缀的情况下,分配Index值为零。否则使用注册表读出来的Index值。当Index为- 1时,进行分配。
0825 if (Index == -1) {
这是需要系统分配Index的情况,分配按1234567890的顺序进行。
0826 //
0827 // Find the first available index for this device prefix.
0828 //
0829 bFoundIndex = FALSE;
0830 Index = 1; // device index (run it through 1-9 and then 0)
0831 EnterCriticalSection(&g_devcs);
因为有可能有多个StartOneDriver在运行,这里需要存取装置串行,应该被保护。
0832 while (!bFoundIndex) {
0833 bUseContext = FALSE; // reuse this flag for this loop.
这个地方重用了bUseContext变量,虽然节省了空间,不过不好阅读。
0835 // look for a conflict in the list of existing devices
0836 for (lpdev = (fsdev_t *)g_DevChain.Flink;
0837 lpdev != (fsdev_t *)&g_DevChain;
0838 lpdev = (fsdev_t *)lpdev->list.Flink) {
0839 if (!memcmp(Prefix, lpdev->type, sizeof(lpdev->type))) {
0840 if (lpdev->index == Index) {
0841 bUseContext = TRUE;
0842 break;
0843 }
0844 }
0845 }
0846
在g_DevChain串行上搜寻可用的Index。这个循环透过三种方式结束: 1)有和前缀标记类型相同的装置Index和当前检验的值冲突; 2)没有和当前检验值相同的Index值;3)没有Pr efix标记类型的装置或者没有装置。第一种情况下bUseContext为真,表示已经发现冲突,则不再检验当前Index值,将Index增加1继续检验;后两种情况还需要继续检验。
0847 // does this entry seem to be available?
0848 if(!bUseContext) {
0849 // look for a conflict in the list of candidate devices (those
0850 // currently being created by another thread)
0851 if (FindCandidateDev(Prefix, sizeof(lpdev->type), Index) != NULL) {
0852 // this device name may be used in the near future, don't use it now
0853 bUseContext = TRUE;
0854 }
0855 }
检验备选的串链中有无冲突情况发生,因为FindCandidateDev被呼叫时处于临界区,所以这里没问题,检验的时候不会有其它行程修改这个备选串链的数据。若检验到冲突则将Index值增加1进入下一轮检验,否则,构造一个新的备选装置接点插入备选装置列表。
0856
0857 // if this device name/index combination is in use, bUseContext
0858 // will be TRUE
0859 if (!bUseContext) {
0860 //
0861 // No other devices of this prefix are using this index.
0862 //
0863 bFoundIndex = TRUE;
0867
0868 // make sure nobody else uses this index while we are installing this device
0869 lpcandidatedev = CreateCandidateDev(Prefix, sizeof(lpdev->type), Index);
0870 if(lpcandidatedev == NULL) {
0873 bFoundIndex = FALSE;
0874 }
这里又是一个基于系统稳健性的考虑,在呼叫中需要分配新的内存,这是可能失败的。
0875 break; // 成功分配到I n d e x的值
0876 }
0877 if (Index == 0) { // There are no more indexes to try after 0
0878 break;
0879 }
分配顺序为1 2 3 4 5 6 7 8 9 0,0是最后一个,不能再多了。
0880
0881 Index++;
0882 if (Index == 10) {
0883 Index = 0; // Try 0 as the last index
0884 }
0885 } // while (trying device indexes)
0886 LeaveCriticalSection(&g_devcs);
这里离开了临界区, Index就是分配到的值,bFoundIndex表示这个最后检测值是否有效,之后可以进行实际的装置注册了。
0887 } else {
0888 bFoundIndex = TRUE;
这是使用注册表I n d e x值以及分配为0的情况。
0889 }
0890
0891 if (bFoundIndex) {
下面可以正式注册装置了。
0892 if (bHasPrefix) {
对于有前置的驱动项目要建构装置名,并写到注册表的特定位置。例如,「COM 2 : 0」就是一个可能的名字。
0893 //
0894 // Format device name from prefix and index and write it to registry
0895 //
0896 _tcscpy(DevName, Prefix);
0897 str = DevName + _tcslen(DevName);
0898 str[0] = (TCHAR)Index + (TCHAR)'0';
0899 str[1] = (TCHAR)':';
0900 str[2] = (TCHAR)0;
0901 status = RegSetValueEx(ActiveKey,
0902 DEVLOAD_DEVNAME_VALNAME,
0903 0,
0904 DEVLOAD_DEVNAME_VALTYPE,
0905 (PBYTE)DevName,
0906 sizeof(TCHAR)*(_tcslen(DevName) +size of ( TCHAR ) ) ) ;
0912 }
0913
0914 Handle = (DWORD)RegisterDeviceEx(
0915 bHasPrefix ? Prefix : NULL,
0916 Index,
0917 DevDll,
0918 Context,
0919 LoadOrder,
0920 Flags,
0921 ActiveId,
0922 lpvParam
0923 );
呼叫装置管理器装置注册函式注册装置,在这个函式中将完成原始码的加载、函式接口的加载、注册等。
0924 } else {
0925 Handle = 0;
这是分配I n d e x失败的情况。
0926 }
0927
0928 // if we've allocated a candidate device structure we should free it now.
0929 // The device has either been instantiated or it hasn't.
0930 if(lpcandidatedev != NULL) {
0931 EnterCriticalSection(&g_devcs);
0932 DeleteCandidateDev(lpcandidatedev);
0933 LeaveCriticalSection(&g_devcs);
0934 }
正式注册的装置应该从备选装置列表中删除,同样存取这些数据需要保护。
0935
0936 if (Handle == 0) {
0937 //
0938 // RegisterDevice failed
0939 //
0943 RegCloseKey(DevKey);
0944 RegCloseKey(ActiveKey);
0945 RegDeleteKey(HKEY_LOCAL_MACHINE, ActivePath);
0946 return NULL;
0947 }
0948
0949 //
0950 // Add handle from RegisterDevice()
0951 //
0952 status = RegSetValueEx(
0953 ActiveKey,
0954 DEVLOAD_HANDLE_VALNAME,
0955 0,
0956 DEVLOAD_HANDLE_VALTYPE,
0957 (PBYTE)&Handle,
0958 sizeof(Handle));
0964
将新的Handle值写到Active键下的Hnd值中。
0965 //
0966 // Only stream interfaces can use TAPI services or receive ioctls.
0967 //
0968 if (bHasPrefix) {
0970
对于具有前缀的驱动程序来说,Ioctrl键值表明是否需要做后期初始化。
0971 //
0972 // Determine whether this device wants a post init ioctl
0973 //
0974 ValLen = sizeof(Context);
0975 status = RegQueryValueEx(
0976 DevKey,
0977 DEVLOAD_INITCODE_VALNAME,
0978 NULL,
0979 &ValType,
0980 (PUCHAR)&Context,
0981 &ValLen);
0982 if (status != ERROR_SUCCESS) {
Ioctl键不存在,不需要做后期初始化,此处原有若干除错信息输出原始码。
0986 } else {
0987 //
0988 // Call the new device's IOCTL_DEVICE_INITIALIZED
0989 //
0990 DevicePostInit(DevName, Context, Handle, DevKey);
呼叫新的驱动程序的特定方法进行后期初始化。
0991 }
0992 }
0993
0994 PnpProcessInterfaces(ActivePath, TRUE); // defined in pnp.c
通知界面变更。
0995
0996 RegCloseKey(DevKey);
0997 RegCloseKey(ActiveKey);
0998
0999 return (HANDLE)Handle;
1000 } // StartOneDriver
1001
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: