-
Evaluate an ACPI Method in Display Miniport Driver in XP - [技术相关]2009-07-23
作者按:这是去年11月份在修bug时遇到的一个问题。由于各种各样的原因,导致问题不能通过一种常规的方式来解决,只好通过这种非常规的方式来实现。当时在网上找了很多资料,看到的都是再问如何解决,或有解答也未能完全解决。东查西找加实践,总算是搞定了此问题。
如果不是即将离开驱动领域,还真不知道什么时候会动手写它。以后也许不会再做驱动了,想想这两年,也就这点东西能对其他人有所帮助了。谨以此文献给我那惨淡的2年。所谓ACPI method就是在ACPI设备上调用的一个方法用以获得某种系统状态,如DockStation的状态是Docking还是UnDocking的,本本的盖子是开着的还是合上的或者其他的系统状态。ACPI Method本身实现于ACPI的命名空间内,存于System BIOS的ACPI table中。调用ACPI Method的设备必须处于ACPI的命名空间内。
对于显卡驱动而言,在Vista和XP下,获得某个系统状态的方式略有不同。
- Vista提供了一个Dxgknl的接口函数DxgkCbEvalAcpiMethod用于显卡驱动调用ACPI Method。显卡驱动所需要的做的只是根据各个ACPI Method的名字、输入输出参数的不同,设置相应的参数直接调用DxgkCbEvalAcpiMethod,OS会完成与System BIOS的交互并把结果返回给Driver。
- 而XP下,则没有类似的接口。如果显卡驱动要获得某个系统状态,正统方式则是调用Int10,给VideoBios发请求,然后由VideoBios与SystemBios交互,SystemBios调用相应的ACPI Method,从而取得相应的系统状态。该过程需要VideoBios和SystemBios事先定好相应的“通信协议”,否则SystemBios不予理会VideoBios的请求。
然而,由于需求设计阶段的考虑不周,在不能增加SystemBios与VideoBios之间“通信协议”的前提下,要求获取当前系统的某些状态。这个前提意味着无法通过常规方式去获得。由于SystemBios中已经存在相应的ACPI Method,Driver所需要做的就是找到一种方法不通过VideoBios而是直接把请求发送给SystemBios,以触发其调用该ACPI Method。对于MiniportDriver而言,系统中的DeviceStack如下所示:--------------
| miniport |
--------------
| ACPI |
--------------
| PCI |
--------------Miniport只要把请求发送给正确的ACPI DeviceObject,就可以通过该ACPI DeviceObject来完成ACPI Method的调用。请求可以通过发送IRP的方式来实现。Miniport在发送IRP之前,需要先把请求封装在一个IRP中,IRP是通过接口IoBuildDeviceIoControlRequest来封装的。封装好之后,再通过IoCallDriver把IRP请求发送给对应的ACPI Method。注意:一定要把请求发给显卡驱动对应的那个ACPI DeviceObject,它才会正确响应ACPI Method。对于接口IoBuildDeviceIoControlRequest而言,有2个参数至关重要:一个是IOCTL,另外一个就是ACPI DeviceObject。这里用到的IOCTL是IOCTL_ACPI_EVAL_METHOD,可以在acpiioct.h中找到。重要的是第二个参数,OS没有提供这样的一个接口可以直接获得ACPI DeviceObject。到这里,问题就归结为如何获得相应的ACPI DeviceObject。在此之前,需要先对OS的DeviceStack有所了解,这在DDK中有比较清楚的描述。这里只简单描述一下与驱动相关的部分。OS在扫描PCIE的时候,如果该插槽上有显卡存在,则会创建相应的ACPI DeviceObject,并挂在ACPI DeviceObject List上。每个DeviceObject都以链表形式链接在一起,并挂在相应的DriverObject下面。之后,OS会创建显卡的DriverObject和DeviceObject,并把这个DeviceObject挂到为该显卡创建的ACPI DeviceObject上。在OS中,DriverObject是唯一的,而DeviceObject则会存在多个。
对于每个DeviceObject,可以通过pDeviceObject->AttachedDevice来获得挂在该DeviceObject上的设备的DeviceObject。而通过pDeviceObject->DriverObject则可以获得对应的DriverObject。这两点是查找ACPI DeviceObject的关键。
每个DeviceObject都会保存它所对应的设备类型。显卡驱动的DeviceObject的类型是FILE_DEVICE_VIDEO,利用这点可以滤掉很多无关的ACPI DeviceObejct了。因为显卡驱动的DriverName是唯一的,可以通过比较DriverName来确定对应的ACPI DeviceObject。详细一点,就是通过查看挂在每个ACPI DeviceObject上的DeviceObject是不是显卡对应的DeviceObject,如果是,则从显卡的DeviceObject中取到显卡的DriverObject,然后比较该DriverObject的Name是不是显卡的DriverName(自家显卡的DriverName可以从DriverEntry中传入的第一个参数Context1中获得)。相同的话,则表明该ACPI DeviceObject是所要找的那个。
上面大概讲了一下DeviceStack相关的内容以及如何确定哪个ACPI DeviceObject才是显卡所对应的那个。现在就只剩下最后一个问题,即,如何获得ACPI DeviceObject List。
由DriverObject和DeviceObject的关系可知,只要能拿到DriverObject,自然就可以拿到DeviceObject List(通过pDriverObject->DeviceObject)。要获取DriverObject,则可以通过一个UnDocument的接口ObReferenceObjectByName来获取。ObReferenceObjectByName在网上可以找到很多资料,这里不再赘述。另外还有一点,ACPI的DriverName是L"\\Driver\\ACPI",它是作为第一个参数传给ObReferenceObjectByName的(注: 第一个参数是Unicode,需要用RtlInitUnicodeString来创建一个Unicode字符串)。拿到ACPI 的DeviceObject之后,就可以通过IRP直接给SystemBios发ACPI Method的请求了
Tags: 多收了三五斗



又见技术贴,赞~