教程番外1——设备实例与设备类

1 简述

在教程1中,我们在INF文件中添加了一个自己的设备类,并为其分配了一个GUID,还定义了注册表项,先回顾一下INF文件中的相关部分:

; 版本域
[Version]
...
Class=HUSTSample  ; 我们的驱动不属于已有的设备类,定义一个新的设备类
ClassGuid={FDA3877E-5FF3-4c18-8235-7FEA16EE04E2}  ; 设备类的GUID
...

;**********************************************************************************
; 设备类部分
;**********************************************************************************

; 设备类安装域
[ClassInstall32]
Addreg=SampleClassReg  ; 写注册表之类,要写入的内容放在SampleClassReg子域中

; SampleClassReg子域
[SampleClassReg]
HKR,,,0,%ClassName%  ; 设备类的名称,由字符串域的ClassName决定
HKR,,Icon,,-5  ; 设备管理器中显示的图标,使用5号系统图标

;**********************************************************************************
; 字符串部分
;**********************************************************************************

; 字符串域
[Strings]
...
ClassName="HUST Sample Device"  ; 设备类的名称
...

在此INF文件中,我们定义了设备类HUSTSample,分配了一个GUID,然后在设备类相关的节中定义了一些注册表项,设备类的显示名称为“HUST Sample Device”,这也是我们在设备管理器中看到的名称,如图1.1。

图1.1

在教程2中,我们又提到了设备接口类和设备实例。关于设备类和设备实例,是驱动开发一开始就接触到的内容,因此,本节专门针对这个内容作相关介绍。

2 设备实例ID

2.1 硬件ID与兼容ID

硬件ID是由硬件厂商定义的一组标识字符串,Windows用来匹配设备和INF文件,即用来匹配设备和驱动包。硬件ID的格式如下:

<enumerator>\<enumerator-specific-device-ID>

如上所示,硬件ID分为两大部分,第一部分enumerator为设备枚举器。设备枚举器就是发现即插即用设备的组件,也就是我们插入一个鼠标、U盘时最先发现它们的组件。发现设备的过程叫做“设备枚举”,发现设备的组件叫做“设备枚举器(enumerator)”。设备枚举器一般为总线驱动,少数为总线过滤驱动。enumerator的表示符号一般为总线名称,例如常见的“PCI”、“USB”和“SCSI”等。第二部分<enumerator-specific-device-ID>厂商指定的信息,例如厂商标识、设备型号、修订版本等。为了具体说明,MSDN中给出了一个无线网络适配器(无线网卡)的硬件标识,如下:

USB\VID_1234&PID_5678&REV_0001

其中,插在USB端口的设备由USB集线器驱动枚举,所以设备枚举器为“USB”,后面的部分分别为:VID_1234为厂商标识,PID_5678为设备型号,REV_0001为修订版本号。不同总线对应的设备硬件ID是不相同的,MSDN中有相应的描述。需要注意的是,这只是一个示例,第二部分不一定要包含三部分,比如此硬件ID也可能为USB\VID_1234&PID_5678

兼容ID也是由硬件厂商定义的一组标识字符串,同样用来匹配INF文件。它的名字很好的说明了它存在的意义。兼容ID是硬件ID的补充,但比硬件ID更通用,可以提高驱动程序的适用范围,提高设备的兼容性。当设备的硬件ID与INF文件中定义的不匹配时,则使用兼容ID来匹配。兼容ID的格式与硬件ID相同,例如一个设备的硬件ID和兼容ID如下,读者可以自行比较。

硬件ID:
    USB\VID_8087&PID_0020&REV_0000
    USB\VID_8087&PID_0020

兼容ID:
    USB\Class_09&SubClass_00&Prot_00
    USB\Class_09&SubClass_00
    USB\Class_09

设备的硬件ID和兼容ID都可以定义一组,而不只一个。在教程1和教程2中我也定义了自己的硬件ID,例如教程2中定义的硬件ID为:root\IOSample,相应的INF文件节如下(分号为注释,为方便查看,有的注释分了行,看的时候注意):

; 厂商域
[Manufacturer]
%MfgName%=Standard,NT$ARCH$  ; 厂商名由字串串域MfgName决定,产品域有两个,
;一个为Standard,一个为Standard.NT“x86”(带版本信息)

; Standard产品域
[Standard]
%IOSample.DeviceDesc%=IOSample_Device, root\IOSample  ; 设备描述(可以等同理解为设备名)
;由字符串域中的IOSample.DeviceDesc指定,设备安装域为IOSample_Device,
;硬件ID为root\IOSample

; Standard.NT$ARCH$产品域
[Standard.NT$ARCH$]
%IOSample.DeviceDesc%=IOSample_Device, root\IOSample  ; 同上

2.2 设备ID与实例ID

设备ID与硬件ID格式类似,但不要混淆。首先明确一定,一个设备有一组硬件ID,但只有一个设备ID。设备ID对设备进行分类,也就是说,具有不同的设备ID表明两个设备属于不同的设备类别。一组硬件ID中的某一个可能成为设备ID,这个硬件ID必须严格标识了设备的类型。例如,前面所说的两个硬件ID,USB\VID_8087&PID_0020&REV_0000可以成为设备ID,而USB\VID_8087&PID_0020不能(只是说明概念,示例不一定正确)。

理解了设备ID,实例ID就容易了。设备ID标识一个设备类型,而实例ID标识此设备类型下的每个设备。最简单的实例ID就可以理解为"0000","0001"等,分别表示该设备类型下第一个设备和第二个设备。当然,系统中实际的实例ID格式有自己的定义。

2.3 设备实例ID

设备实例ID即设备ID和实例ID的组合,格式如下:

<device-ID>\<instance-specific-ID>

作用想必读者已经很清楚,即唯一的标识系统中的一个设备。系统设备树中的每个节点都有一个设备实例ID。有的代码里面可能会写为设备实例路径(InstancePath),都是同一个东西。设备实例ID会在系统重启时保持不变。下面是一个PCI总线上的设备实例ID:

PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08

3 设备识别与驱动安装过程

(来自MSDN)

第一步:识别新设备

用一个实例来说明,当用户在USB集线器(USB Hub)一个端口插入一块无线网卡(无线网络适配器)时,将发生如下步骤:

  1. 设备被USB集线器驱动检测到。集线器驱动获取适配器的相关信息,为它创建一个硬件ID。
  2. USB集线器驱动通知即插即用管理器检测到了一个新的设备,即插即用管理器向USB集线器驱动查询设备的所有硬件ID。(集线器驱动可以为一个设备创建多个硬件ID)
  3. 即插即用管理器通知Windows需要安装一个新设备。同时,向Windows提供前面查询到的硬件ID列表。
  4. Windows查找与这些硬件ID(中的某一个)匹配的驱动包。如果没有匹配的,则查找如兼容ID匹配的驱动包。

Windows在某些位置查找驱动包,如驱动仓库(Driver Store),查找的顺序过程可以看MSDN。

第二步:选择驱动程序

如果Windows查找到了多个匹配的驱动包,需要选择一个最合适的。Windows会给多个驱动包中的每个分配一个权值,根据权值来排序。如果权值相同,则要进一步根据驱动是否签名,驱动日期版本等条件来选择。

第三步:安装驱动程序

找到驱动程序之后,Windows会保存设备和驱动的信息。设备也会在设备树中生成设备节点,也会生成设备实例。Windows将INF文件中指定的驱动文件拷贝到系统指定位置,然后将控制权交给即插即用管理器,即插即用管理器加载驱动并启动设备。

4 设备安装类

Windows中,设备分为设备安装类和设备接口类,而一个设备信息集(HDEVINFO)包含了某个设备安装类或者设备接口类下面的所有设备(设备信息元素SP_DEVINFO_DATA)。本小节讲解设备安装类。

设备安装类是为了简化设备的安装过程而设置的,具有相同安装配置过程的设备归为同一个设备安装类。每个设备安装类都有一个相对应的GUID。也就是说,划分设备安装类的意义很简单,就是方便设备的安装。

Windows预定义了很多设备安装类,例如(类名和GUID):

Class = Battery
ClassGuid = {72631e54-78a4-11d0-bcf7-00aa00b7b32a}

电源设备:此设备安装类包含电源设备和UPS设备。

Class = Bluetooth
ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}

蓝牙设备:此设备安装类包含所有蓝牙设备。(Windows XP SP1 及以后版本)

类安装器(class installer )

为一个DLL,完成某个设备安装类下的设备安装操作。不是所有的设备安装类都需要类安装器(我们的教程中定义的设备安装类就没有提供类安装器),需要完成一些操作的设备安装类才需要,例如,Ports类安装器负责给端口安装类下的设备分配一个COM端口名。

类辅助安装器(class co-installer)

如果系统提供的类安装器或者自己编写的类安装器完成的功能不够,则可以为该设备安装类编写一个类辅助安装器,类辅助安装器对该设备安装类下面的所有设备都有效。

设备辅助安装器(device co-installer)

设备辅助安装器针对于某个具体的设备,是对类辅助安装器的进一步补充。

本文“简述”中所述的INF文件(教程)定义的就是设备安装类,类名和GUID如下:

Class=HUSTSample 
ClassGuid={FDA3877E-5FF3-4c18-8235-7FEA16EE04E2}  

5 设备接口类

设备接口类,顾名思议,是设备对外的接口,用于设备的管理和访问操作。通过设备接口,系统对设备的状态进行跟踪。

每个设备向某个设备接口类注册时,会生成该设备接口类的一个实例,这个实例标识了该设备。设备接口类也可以在INF文件中定义,但我们一般在程序中调用WdfDeviceCreateDeviceInterface函数来创建设备接口类和它的一个实例,并在头文件中发布在接口(GUID),这样,就可以在应用程序中访问该设备了。

Setup系列函数中常常将设备安装类和设备接口类混合在一起使用,参数ClassGuid即可以表示设备安装类的GUID,也可以表示设备接口类的GUID,使用时要注意区分。

6 注册表键

设备枚举键

我将HKLM\SYSTEM\CurrentControlSet\Enum称为设备枚举键,该注册表键包含了系统中的设备信息,该子键下的设备按Enumerator(设备枚举器)划分,设备枚举器下又按deviceID(设备ID)划分类型,最后在下面创建instanceID(实例ID),即具体的设备,保存了设备信息,如设备描述,硬件ID,兼容ID,资源需求等。Setup系列函数的枚举过程一般就从该注册表键开始,设备管理器中设备排列与该键类似。

设备安装类键

注册表键HKLM\SYSTEM\CurrentControlSet\Control\Class保存设备安装类的信息,每个设备安装类GUID占一个子键。保存设备安装类的信息,如类安装器。示例如下:

其中Class项为类名,Installer32项为类安装器。

设备接口类键

注册表键HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses保存设备接口类的信息,每个设备接口类GUID占一个子键,下面每个设备接口类的实例占一个子键。示例如下:

此设备接口类下有两个实例,DeviceInstance项表示设备实例路径。实例键下还有个引用键,这个键一般为“#”,有时候也有引用字符串,这个键有一个重要的项SymbolicLink,这就是我们在教程2中讲述的通过设备接口要查询到的符号链接路径,通过这个路径可以在应用程序中访问设备对象,进而操作设备。

与设备枚举键的联系

设备接口键与设备枚举键密切联系,按照DeviceInstance项的值,即可在Enum键下找到该设备实例,所以设备接口键下的实例不过是设备实例的一个重新的名称而已,通过遍历设备接口键,可以在Enum找到该实例,进而查询设备的信息。

硬件键

硬件键和软件键作介绍,因为驱动开发中常常会看到这两个名称。

注册表键HKLM\SYSTEM\CurrentControlSet\Enum\Enumerator\deviceID\实例ID叫做硬件键,也叫设备键,保存设备的信息。位于枚举键下。

软件键

注册表键HKLM\SYSTEM\CurrentControlSet\Control\Class\GUID\实例ID(十进制4位数)叫做软件键,也叫驱动键,保存驱动程序的信息。位于安装类键下。

7 再看教程2的GetDevicePath函数

讲述了前面一些概念之后,现在我们再来看一下教程2中编写的GetDevicePath函数,教程2中我们说到,如果一个设备接口类下面包含了多个实例,那么GetDevicePath只获取了第一个。如何获取一个指定的实例(设备)?重点在SetupDiGetClassDevsExEnumerator参数。教程2中该参数设为NULL,没有讲述该参数的使用,现在我们就来看看。

SetupDiGetClassDevsEx函数中,如果Flags参数指定了DIGCF_DEVICEINTERFACE,表示枚举指定的设备接口类,此时Enumerator可以指定一个设备实例ID(注意是设备实例ID) 。如果没有指定该标记,则枚举设备(Enum键),这个时候Enumerator也可以指定,但是此时指定是设备枚举器(注意是设备枚举器,不是设备实例ID)。所以我们的GetDevicePath要改一改,把Enumerator参数留出来,函数原型如下:

PCHAR GetDevicePath( IN  LPGUID InterfaceGuid, const CHAR* InstancePath )

多加了一个InstancePath参数,并把SetupDiGetClassDevsEx函数的Enumerator参数由NULL改为InstancePath。这样,我们就可以指定设备实例ID,从而唯一地确定一个设备了。

为了进行实验,我们还要稍微改一改教程1中的HelloWorld,因为目前我们只有教程2注册了一个设备接口类IOSample_DEVINTERFACE_GUID,此设备接口类只有一个实例,所以我们稍稍修改HelloWorld程序,在里面也注册IOSample_DEVINTERFACE_GUID,创建第二个实例,在利用GetDevicePath来枚举。具体的过程就不说了,大家参照教程2的代码写就行了。

安装好新的HelloWorld之后,现在,IOSample_DEVINTERFACE_GUID{95cc5f28-95d3-44fd-8dcd-264d4ce5fecc}下面就有两个实例了,如图:

GetDevicePath第二个参数设为NULL:GetDevicePath(&IOSample_DEVINTERFACE_GUID, NULL),调用得到两个路径,如图:

GetDevicePath第二个参数设为HelloWorld的实例路径,这个实例路径可以在设备管理器中得到,选择设备,右键“属性”,下拉框中选择“设备范例Id”即可,如下图:

调用GetDevicePath(&IOSample_DEVINTERFACE_GUID, "ROOT\\HUSTSAMPLE\\0000"),调用得到一个路径,如图:

这样,我们就唯一定位了一个设备。

8 总结

本节中注意介绍一些设备驱动开发中遇到的基础知识概念,这些概念在驱动开发中很重要,读者要自己实验理解。

本节源码地址:https://github.com/hustd10/Windows-Driver.git,文件名为FanWai_1。

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
EDA(Electronic Design Automation)技术是电子系统设计的核心,涉及到电子设备设计的各个方面。在EDA技术中,仿真和版图是不可分割的两个重要环节。本文结合实例,介绍了模拟集成电路EDA技术中的仿真和版图设计。 对于EDA技术的仿真部分,常用的软件有PSpice、LTSpice、Cadence等。下面以PSpice为例,介绍仿真的设计过程。 首先,需要将电路图绘制好,设定好电路参数,比如电阻值、电容值和电压值等。然后在工具菜单中选择仿真,设置仿真选项,比如仿真型、仿真时间和仿真步长等,点击开始仿真即可生成仿真结果。仿真结果可以用波形图和数值描述来表现,通过观察波形图和数值,可以分析电路的各项性能指标,比如幅频特性、相频特性和时域响应等,为后续的设计提供依据。 接下来,介绍EDA技术中的版图设计。版图设计是针对芯片的物理尺寸和布局进行细致的设计。通常情况下,版图的设计包括针对芯片结构的逻辑布图设计和芯片的物理布局设计两个环节。在逻辑布图设计中,需要将电路的逻辑模型转换为板图中的网格模型,并将元器件适当地布置在不同的坐标位置上;在物理布局设计中,需要遵循电路的物理特性及产生的电磁干扰等问题,适当地调整元器件之间的距离和相对位置。 综上所述,EDA技术中的仿真和版图设计是电子系统设计的重要环节,可以提高设计效率、降低设计成本、提高电路的可靠性和性能指标。随着电子工艺的发展,EDA技术的应用范围会越来越广,也会带来越多的技术革新和工程挑战。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值