Wince下usb驱动详细总结(史无前例的详细)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chyuanzheng/article/details/7903214

0,前言:

        1, 本篇文章只讲wince下的usb host驱动,并深入解析HID驱动。本博客的目的并不是只是为了讲怎么写驱动, 更重要的是:

                   1,了解wince驱动的架构。

                   2,学习微软的写作方法,如何去架构一个非常复杂的程序(USB驱动的确是有点复杂,但是微软的代码写的非常的具有条理,是不可多得的学习资料微笑)。

        2,学习wince usb驱动,必须对usb的协议有一定了解,可以看看《USB2.0 硬件设计(第2版)》

1,USB驱动的总体结构

        这个方面的资料很多,其实也很简单,其实从总体来看,不管细节,USB驱动就那么三层架构而已。如下图:

         由于我们只专注于usb host端的驱动,所以只要看左边就可以了,最底层是硬件,硬件上面的第一层是HCD驱动,第二层是USBD,再上面就对应不同的usb设备来,print对应usb打印机,Mass Storage对应U盘,HID对应USB鼠标,USB键盘和其他符合HID设备,HID并不是一个驱动,而是一个类别,所以USB鼠标,USB键盘都要单独的驱动。

         下面我们从最底层往上来学习USB驱动。


2,USB Host Controller Hardware (HC 硬件)

       就如同串口一样,USB需要有硬件来支持,串口有RS232,RS485……,USB host controller 有OHCI,UHCI……,这个没什么好说的,到底不同那个的硬件有什么不同的特性,这里就不讨论了。赶紧看 下一小节。


3,USB  Host Controller Driver(HCD驱动)

       前面说了USB接口需要硬件,那么不同的硬件驱动方式肯定有所以不同,这点不像串口。如果你用的是OHCI,那么你必须有针对OHCI的驱动。
       我们以OHCI为例,来看看这一层的驱动如何架构。

       1.打开目录WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB\HCD,可以看到:

打开common中的source文件,可以看到里面的程序编译成hcdlib,打开OHC中的cource文件,可以看到里面的程序编译成ohci_lib,打开OHCD2中的source文件,可以看到里面的程序编译成ohcdmdd2。
        
       2.在D:\WINCE600\public\common\CESYSGEN\makefile中可以看到:



       从这里已经很明白了,上面的三个文件夹,再加上ceddk就能够编译出ohci2,这个就是OHCI对应的HCD层的驱动了。

       3.到这里已经讲完了HCD层,当然里面代码也是值得我们学习的,但是对我们写驱动却没有太多的帮助。我们来看看HCD层提供一些什么样的接口函数吧!
           1) 打开 D:\WINCE600\PUBLIC\COMMON\OAK\INC\hcdi.h 可以看到下面的函数。这几个函数是USBD层必须要实现的,你会在hcd层发现系统加载USBD.dll,然后调用上面这些函数,具体得代码,这里就不照抄了,参考D:\WINCE600\public\common\oak\drivers\usb\hcd\common\cdevice.cpp。

       BOOL HcdAttach(LPVOID lpvHcd, LPCHCD_FUNCS lpHcdFuncs, LPLPVOID lppvContext);
       BOOL HcdDetach(LPVOID lpvContext);
       BOOL HcdDeviceAttached(LPVOID lpvContext, UINT iDevice, UINT iEndpointZero,
       LPCUSB_DEVICE lpDeviceInfo, LPLPVOID lppvDeviceDetach);
       BOOL HcdDeviceDetached(LPVOID lpvDeviceDetach);

      
           2)hcdi.h 还可以看到下面的数据结构体。  这个数据结构里面都是一些函数,并通过HcdAttach函数传递给USBD层,这样USBD层就可以使用HCD层的服务了。
      struct _HCD_FUNCS {
    DWORD                       dwCount;
    LPHCD_GET_FRAME_NUMBER      lpGetFrameNumber;
    LPHCD_GET_FRAME_LENGTH      lpGetFrameLength;
    LPHCD_SET_FRAME_LENGTH      lpSetFrameLength;
    LPHCD_STOP_ADJUSTING_FRAME  lpStopAdjustingFrame;
    LPHCD_OPEN_PIPE             lpOpenPipe;
    LPHCD_CLOSE_PIPE            lpClosePipe;
    LPHCD_RESET_PIPE            lpResetPipe;
    LPHCD_IS_PIPE_HALTED        lpIsPipeHalted;
    LPHCD_ISSUE_TRANSFER        lpIssueTransfer;
    LPHCD_ABORT_TRANSFER        lpAbortTransfer;
    LPHCD_DISABLE_DEVICE        lpDisableDevice;
    LPHCD_SUSPEND_RESUME        lpSuspendResume;
};
    
    思考:这样写程序是不是很清晰呢,不过是几个函数,一个结构体,使得HCD层和USBD层相互连接起来了!


4,USB  Core Driver(USBD驱动)

         1,学习了HCD层的驱动,这一层就相对简单了。还是先看目录。哦,简单了,只有一个目录,USBD!在D:\WINCE600\public\common\oak\drivers\usb\usbd!

        2,打开makefile看看USBD是如何编译的,在 D:\WINCE600\public\common\CESYSGEN\makefile 你会发下,USBD最后简单编译为动态库,USBD.dll。至于为什么会编译动态库,可以参考:http://blog.csdn.net/chyuanzheng/article/details/7858782          这里就不多说了。

        3,同样的,我们来看USBD为上层提供了什么样的服务。打开:D:\WINCE600\PUBLIC\COMMON\DDK\INC\usbdi.h。

1) 可以看到下面的函数,这几个函数是上层必须实现的。无论是print驱动,还是Mass Storage驱动,等等,都要实现。
BOOL USBDeviceAttach(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
                     LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
                     LPBOOL fAcceptControl,
                     LPCUSB_DRIVER_SETTINGS lpDriverSettings, DWORD dwUnused);
BOOL USBInstallDriver(LPCWSTR szDriverLibFile);
BOOL USBUnInstallDriver();
 
    2)还有一个结构体_USB_FUNCS, 这个数据结构里面都是一些函数,并通过USBDeviceAttach函数传递给上层,这样上层就可以使用USBD层的服务了。_USB_FUNCS这个结构有点长,我就没写在这里来,可以参考HCD层的架构。


思考1:发现了没,USBD提供的接口层和HCD提供的接口层在结构上是完全一致的,当一种方法反复使用的时候,说明我们也可以拿来用在我们的程序中。
思考2:还有一个发现,HCD层使用了hcdi.h(注意名字)提供接口函数,而USBD使用usbdi.h。所以你如果发现后面带i的.h文件,那么详细看,这个是不是某个库提供的接口呢!

5,Mass Storage驱动

          这个是我最早学习USB驱动看的一个例子,文章链接在http://blog.csdn.net/linarm/article/details/1451002,感谢这篇文章的作者!这一层这篇文章已经说的足够的清楚,我就不多说了。在这篇文章里,你可以看出Mass驱动是如何调用USBD层的。其实和USBD层调用HCD差不多。


6,HID驱动

          HID驱动是非常复杂的一层,但是它是建立在USBD层之上,也就是说HID驱动必须按照usbdi.h中的接口规范来写。打开目录:D:\WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB\CLASS\HID\HIDCLASS

          1,可以看到目录结构如下。所以可以很明显的看出来,HID分成了两层,一个是MDD层,一个是USBPDD层。因为USBD层调用的USBPDD层,所以我们来看看USBPDD这个文件夹。


1)在USBPDD文件夹中,你会发现对usbdi.h中接口必须实现的函数,和对usbdi.h提供的服务的使用,这里就不多说了。
2)在source文件内发现USBPDD编译为USBHID_LIB,实际是编译为usbhid.dll,至于是如何编译的,看makefile,如下(就没截图了):
usbhid:: usbclient usbd hidparse hidmdd
 所以usbhid直接包含usbd。而usbclient,是所有USBD的上层一个共有的静态库。而hidparse提供对hid中的描述符进行解析的一个库,这个库没有提供源代码(我没有找到,难道在private文件夹中?),不过不影响我们对程序的理解。那么hidmdd是什么呢,hidmdd就是上面所说的MDD层。在它的同名文件夹中。

          2,MDD层。hidmdd只是个静态库而已,由USBPDD直接对它进行调用,接口是什么呢?其实就是 hid.h(我开始以为是hidpddi.h,后来发现不是)而已(这个后面没有加上i,成为hidi.h,可能是mdd pdd两层比较近)。打开D:\WINCE600\PUBLIC\COMMON\OAK\INC\hid.h,这个没什么好说的,USBPDD对MDD层的调用是直接的。不就一静态库,当然直接调用。

         3,那么这两层又提供了什么样接口给上层调用呢?答案是hiddi.h,打开E:\refer\usb_wince\COMMON_OAK_INC\hiddi.h。好了,我就不多说,和前面的几个接口头文件是一样的,读者可以自行分析。


7,HID上层驱动(以USB键盘为例)

         USB键盘驱动是建立HID驱动之上的,所以必须符合HID驱动的接口规范,接口规范定义在hiddi.h中。

 我想了想,还是不需要过于详细了,否则读者不会反感,我都会反感的,usb键盘驱动在目录 D:\WINCE600\PUBLIC\COMMON\OAK\DRIVERS\USB\CLASS\HID\CLIENTS\KBDHID,自行分析吧。

8,结尾

       其实看一个程序,当然要看懂它的功能,但是看懂它的功能不如看懂架构,然而看懂它的架构不如看懂它的接口。

         看懂了接口,你就可以使用它,利用它,控制它,你看懂了wondows的接口(win32)你就可以编写应用程序,而不需要搞懂整个windows系统!!!,你学会了飞机的接口(驾驶飞机),你就能操作飞机飞翔在天空,而不需要搞懂飞机的制作!
       USB驱动就写到这里,我并没有做到史无前例的详细,问题是,你看懂了么!

9,再次结尾

      接口如此重要,如何设计一个好的接口呢?你领悟了么。


10,再次再次结尾


      想想看,USB驱动又如何向应用程序提供接口呢?

11,再次再次再次结尾

      
      这次真的结束了……


参考:wince下的USB驱动要点总结 http://blog.csdn.net/chyuanzheng/article/details/7858782



补充,HID的启动与注册表在hidmdd.cpp中 见 OpenClientRegKey函数:

// Contains most of the logic to find and open the proper HID client key.
// The algorithm searches for the most specific vendor key of the form
// idVendor_idProduct_bcdDevice. Then its subkeys are searched for the most
// specific interface key of the form bInterface_bCollection. Then its
// subkeys are searched for the most specific top level collection key 
// of the form UsagePage_Usage.
//
// The order of vendor precedence is described below.
//
// idVendor_idProduct_bcdDevice
// idVendor_idProduct
// idVendor
// Default
//
// The order of interface precedence is described below.
//
// bInterface_bCollection
// bInterface
// Default
//
// The order of TLC precedence is 
//
// UsagePage_Usage
// UsagePage
// Default
//
// So the order of checking would be
// 1. idVendor_idProduct_bcdDevice\bInterface_bCollection\UsagePage_Usage
// 2. idVendor_idProduct_bcdDevice\bInterface_bCollection\UsagePage
// 3. idVendor_idProduct_bcdDevice\bInterface_bCollection\Default
// 4. idVendor_idProduct_bcdDevice\bInterface\UsagePage_Usage
// ...
// 7. idVendor_idProduct_bcdDevice\Default\UsagePage_Usage
// ...
// 10. idVendor_idProduct\bInterface_bCollection\UsagePage_Usage
// ...
// 42. Default\bInterface_bCollection\Default
// 43. Default\bInterface\UsagePage_Usage
// 44. Default\bInterface\UsagePage
// 45. Default\bInterface\Default
// 46. Default\Default\UsagePage_Usage
// 47. Default\Default\UsagePage
// 48. Default\Default\Default
//



展开阅读全文

没有更多推荐了,返回首页