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

 

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接口ActivateDeviceL10 59)。ActivateDevice是定义在winbase.h (标准windows标头档之一)中的一个宏,它透过WIN32_DEV_CALL宏映像装置管理部分注册的Win32系统函式(注册用数据结构请参考第6章程式清单6 - 6),从程序中知道它的序号为7,函式指标被映像为0,这是因为系统直接将它转化为了对应1 2号接口(ActivateDeviceEx)的呼叫。这个呼叫被映像成为内部函式FS_Activa teDeviceExdevice.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是个很长的函式(L0581L1000),虽然这样,它的可读性还是比较好的,因为其中有比较多的整齐的语句用来存取注册表并检验参数的合理性。

下面将从三个方面考察这个函式实作的功能。

 

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.dllBuildIn键的DLL值,它也是最早加载的模块。

注册表列举器(RegEnum.dll)汇出了InitDeinit函式。它透过读取注册表项发现新装置。因为当前一次呼叫未完成时,它能被再次呼叫,所以可支持阶层化的存取方法。当它被卸载时,任何它直接加载的资源会被同时卸载。注册列举器检查注册表传给它的根键,用来搜寻描述加载装置的子键。在这些键中,有的子键包括内建和本地装置、ISA装置、PCI总线,以及诸如ND IS的虚拟装置,诸如SNMP 选择性的协议元素或点对点的协议,还有其它的非列举O S组件。

 

RegEnum.dll按照顺序检查传给它键下面的第一层键。在每个找到的子键上呼叫ActivateDeviceEx。每一个子键可能有被ActivateDeviceEx或最终呼叫的驱动程序直译成任何值。另外,每个子键有一个从0255的序列值。最小的序列值最先被载入。如果没有序列值,驱动程序将在最后加载。这个顺序可以透过前面的小实验来检查。

实作了档案数据流接口的驱动程序使用前缀的值寻找驱动程序起始点,例如当前缀为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_DevChaing_CandidateDevs(参考L0821L0934)。

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位的字符(WCHARshort或者unsigned short )。在Windows CEWCHAR一般为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函式获取prefixindexFlags以及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_LEN64的字符,因而注册表中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 00是最后一个,不能再多了。

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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值