文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>wince下USB设备驱动程序导读

wince下USB设备驱动程序导读

时间:2010-12-25  来源:GT_Andy

正如所料,接下来我们就进入到DRIVERS\USB\CLASS\STORAGE\CLASS文件夹下,接触USB设备驱动程序。
        我们先来了解两个头文件,分别是STORAGE\INC\usbmsc.h和STORAGE\CLASS\usbmscp.h,其中前者是USB存储设备公用的头文件,后者是需要按照自己的设备更改的头文件。我们先来看前者。
        在usbmsc.h这个头文件中,前边定义了很多常量,包括子类和协议的常量,这是从哪里来的呢?前文我们已经提到过,这些量值是依据USB设备规范得来的,在规范上都作了定义,所以此处的值必须与USB规范中的相一致。再向下的命令块结构体和数据块结构体是用来与USB设备通讯用的,可以通过这两个结构体的实例与USB设备传输数据。 下面的函数原型就不说了,前文提到过,在这里只记得有这几个函数就行了。
        再来看usbmscp.h这个头文件,这个头文件是要按照自己的需要和USB设备来进行修改的,比如DRIVER_NAME_SZ是驱动程序的名字,RESET_TIMEOUT 是一个超时的默认值。还有很重要的一个就是USBMSC_DRIVER_SETTINGS的设置,这个设置是与USBDI.H中的USB_DRIVER_SETTINGS结构体一一对应的,为了符合我自己的设备,通常要把dwVendorId和dwProductId等设置成设备的对应值,比如我的U盘的VendorID是0x058F,ProductID是0x9321,那我就会把这两个值对应的写在相应的位置上。同时在系统注册表中也会利用这两个值修改注册表的键以便设备管理器可以顺利的找到我的设备驱动。
        下面还有一个_USBMSC_DEVICE结构体,它是用来描述你自己的USB存储设备的,是封装了USBD函数表指针、磁盘设备指针、管道和配置项的最重要的数据结构,在驱动程序实现上此数据结构就是重点的参数,鉴于样例程序对每一个结构体元素都作了明确的注释,此处我就不一一描述了,它就像C++中的类一样,是最后把一些小类组合起来的可以最终使用的结构。
        好了,对这两个头文件有所了解以后,我们就进入最关键的部分,源程序。我们接下来来看usbmsc.c这个文件。为什么要先看这个文件而不是同一文件夹下的其他几个文件呢?我来解释一下。在这个文件夹中有一个usbmsc.def的文件,大家都知道它是定义了导出函数的,通常与它同名的程序文件都会含有DllEntry的入口,既然入口在这,那我们自然就先来看这个文件了。如果用到了其他的文件,再看不迟。
        这可是一个有1000多行的源程序,但不要害怕,我们只看最主要的,别的函数的实现你可以自己去研究。首先看到文件的DllEntry入口之前有5个函数原型的定义,从函数名上就可以知道这个函数的功能了,很显然这几个函数是程序实现过程中被调用的,所以目前知道功能就行了,不用了解实现方法。忘了说一句,这个程序中包含了bot.h和cbit.h两个头文件,可见程序中是要用到它们的功能的,不过先不管它,继续往下看。
        DllEntry入口函数的下面,就是USBInstallDriver()这个函数了,它的作用是进行与USB设备相关的注册表操作,主要的语句是:
bRc = RegisterClientDriverID( wsUsbDeviceID );
bRc = RegisterClientSettings( szDriverLibFile, wsUsbDeviceID, NULL, &usbDriverSettings );
即先注册设备类别,然后是设备细节。 同样,USBUnInstallDriver()函数是以相反的顺序解除注册信息的。 这几个与注册有关的函数在前面我们提到过,是由USBD接口提供的,这里我们可以看到USBD对设备驱动程序的重要性。
在继续向下看,我们发现了USBDeviceAttach()函数,这可是最重要的地方了,当有USB设备插入插口以后,操作系统是如何识别它的呢,如何将其做为一个文件夹加以访问的呢?我们就来解开这里的谜团。
        为了我们方便说明,我将此程序简化如下:

        后面的程序将以此行号进行说明。
我们来看程序的第4行,这里有一个判断语句,它是在判断插入的设备是否是USBMSC_INTERFACE_CLASS类型的,这个常量是在usbmsc.h文件中定义的,也就是说如果设备不是USB存储设备,那么就结束这个函数,也就是此驱动只能处理USB存储设备。
        当发现设备符合此驱动程序的要求后,就通过函数ParseUsbDescriptors()来解析这个设备,这个函数在下面的程序中将被实现,我们可以看一下该函数的函数体,很显然,它是在为设备进行各种配置,这就不多说它了。
        再往下,分配内存,设置标志,从注册表中读取信息。注意,这里读取到的注册表信息是Drivers\\USB\\ClientDrivers\\Mass_Storage_Class和bInterfaceSubClass变量组合成的注册表键下的值,具体可参阅源程序,这个注册表键下放置的内容是
[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\Mass_Storage_Class\6]
"DLL"="USBDISK6.DLL"
"Prefix"="DSK"
"Folder"="USB Disk"
"IOCTL"=dword:4
"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"
由此可以看出,通过此处的注册表读取,驱动程序可以知道这个设备将通过哪种形式以及哪个DLL向操作系统提供接口。同时也为后续的操作进行了准备。
        最关键的部分就在接下来的LoadDriver()那句,加载了另一个驱动程序的DLL文件,就是上述注册表中的USBDISK6.DLL文件,计数器增一,取到该文件中UsbDiskAttach函数及UsbDiskDetach函数的地址,注册事件通知处理函数,然后调用了该DLL文件中的UsbDiskAttach函数。
        由此可见,USB设备驱动程序有两层功能,一方面是识别出指定的设备并进行配置,另一方面按照要求调用更高层的驱动程序来向操作系统提供接口。当调用了USBDISK6.DLL后,操作系统就会按该文件中的程序以一个磁盘的形式或文件夹的形式进行处理,通过文件系统的操作,就可以对其进行读写控制了。我们也可以看一下HID设备的这个函数,它也是通过这种方式让操作系统知道把USB设备识别成鼠标设备的。
        前文我们说过还有一个通知消息的回调函数,我们在刚才的程序体中已经发现通过:
UsbFuncs->lpRegisterNotificationRoutine( hDevice, UsbDeviceNotify, pUsbDevice );
语句已经对这个函数进行了设置。我们再向下来看一下这个函数的函数体。这个函数很简单,只要对USB_CLOSE_DEVICE消息进行处理,既然是要关闭USB设备,那么调用USBDISK6.DLL中的Detach函数是必须的,让上层的驱动程序进行释放,然后将引用计数减一,如果不再有设备引用此驱动程序,则FreeLibrary(),仅此而已。
        其余的函数可以再仔细研究一下,在此就不详细描述了,接下来我们要弄明白的就是到底操作系统是如何通过抽象的DISK读写具体的设备呢?
带着上次留下的疑问,我们继续来学习操作系统如何通过USBDISK读写USB设备的。我们先看USB\CLASS\STORAGE\DISK\SCSI2\usbdisk6.def文件。在这个文件中可以看到,该DLL一共导出了14个函数,其中两个是上次内容当中被设备驱动程序调用的UsbDiskAttach和UsbDiskDetach,余下的是一组以DSK开头的流驱动接口,易见,USBDISK是以流驱动的形式向操作系统提供服务的。
        为了清晰起见,以下大量的程序我们并不学习,而只关心设备读写,因此我们来看DISK.C这个程序文件。找到DSK_Read和DSK_Write两个函数,令我们大失所望,因为这两个函数都是形如
UNREFERENCED_PARAMETER(pDevice);
UNREFERENCED_PARAMETER(pBuffer);
UNREFERENCED_PARAMETER(BufferLength);
DEBUGMSG(ZONE_ERR,(TEXT("DSK_Read\n")));
SetLastError(ERROR_INVALID_FUNCTION);
return 0;
这样的实现,也就是说用户无法通过常规的ReadFile和WriteFile函数使用这个设备,那怎么办?是否意味着这个DISK无法读写呢?当然不是,我们应该马上想到DSK_IOControl()这个函数,当遇到某些设备无法用常规的文件操作函数操作时,我们有DeviceIoControl()用户函数可以使用,而这个函数就会调用到驱动程序中的DSK_IOControl函数。
        在这个函数中,我们找到了对IOCTL_DISK_READ等命令的处理程序,其中最关键的一句就是ScsiRWSG(pDevice, pSgReq, pDevice->Lun, bRead),即调用了一个ScsiRWSG的函数。
        在Scsi2.c这个程序中,我们找到了这个函数,其中SG指的是一种读写缓冲区的数据结构,实际上就是带有缓冲区及长度的一个结构体,是CE下磁盘设备通用的读写数据结构,可以在diskio.h中找到它的定义。在这个函数中我们发现它再次调用了ScsiReadWrite()这个函数进行读写操作,找到这个函数,里面有我们最重要的一行调用,即调用了UsbsDataTransfer()函数,还记得这个函数在哪见过吗?没错,就是在USB设备的驱动程序当中。
        通过这一过程我们发现,那些Scsi的函数都只是在准备一些缓冲区、数据结构等,并没有对硬件进行操作,真正要操作硬件设备的还是由驱动程序来完成的,可见,设备驱动程序是有着很强层次结构的,下层是专门针对物理设备的,上层是针对操作系统的抽象设备的,下层是U盘等物理实体,上层是文件夹,二者通过一定的通信或调用机制完成了设备在操作系统下的正常工作。
        回到usbmsc.c程序中来,找到UsbsDataTransfer函数,这个函数很简单,根据传输协议调用CBIT_DataTransfer()或BOT_DataTransfer() 即可。
相关阅读 更多 +
排行榜 更多 +
宝宝情商养成宝宝巴士

宝宝情商养成宝宝巴士

休闲益智 下载
燥热手机版

燥热手机版

飞行射击 下载
巨人狙击手安卓版

巨人狙击手安卓版

飞行射击 下载