WINCE下的USB驱动开发

301 篇文章 0 订阅
120 篇文章 3 订阅

作者:zhongnanjun_3

转自:http://blog.csdn.net/zhongnanjun_3/article/details/3555738


首先USB加载式流接口驱动要点分析

为了支持不同类型的外围设备,WinCE平台提供了具有定制接口的流接口驱动程序模型。因为大部分USB外围设备由于功能性更适合流接口驱动的结构,所以一般都采用加载式流接口驱动程序模型来开发USB设备驱动程序。

(1)USB系统结构分析
WinCE下USB系统软件由两层组成:较高USB设备驱动程序层和较低的USB函数层。较低的USB函数层本身又由两部分组成:较高的通用串行总线驱动程序(USBD)模块和较低的主控制器驱动程序(HCD)模块。通过HCD模块功能和USBD模块实现高层的USBD接口函数,USB设备驱动程序就能与外围设备进行通讯。

在数据传输的过程中,操作流程通常按下列的次序进行:①USB设备驱动程序进行数据传输的初始化,即通过USBD接口函数给USBD模块发送数据传输的请求。②USBD模块将该请求分成一些单独的事务。③HCD模块排出事务次序。④主控制器硬件执行事务。这里需要提醒的是,所有的事务都是从主机发出的,外围设备完全是被动接受型的。

(2)USB设备驱动程序入口点函数
从结构分析我们可知,所有的USB设备驱动程序必须在它们的DLL库设置一定的入口点与USBD模块进行适当的交互。设置入口点函数有两个作用:一是使得 USBD 模块能与外部设备交互;二是使得驱动程序能创建和管理任何可能需要的注册键。

下面简要介绍相关函数的作用:USBDeviceAttach是当 USB 设备连接到主计算机时运行,USBD模块会调用这个函数初始化USB设备,取得USB设备信息和配置USB设备,并且申请必需的资源。 USBInstallDrive是在第一次加载USB设备驱动程序时首先被调用,它使得驱动程序能创建需要的注册键,用于将一个驱动程序所需的注册表信息写入到HKEY_LOCAL_MACHINE/Drivers/USB/ClientDrivers目录下,例如设备名称等。需要注意的是,USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()、RegisterClientSettings()函数来注册相应的设备信息。

USBUninstallDriver是在用户删除USB设备驱动程序时调用,负责删除注册键并释放其它相关资源。它通过调用 UnRegisterClientSettings()和UnRegisterClientDriverID()函数来删除由驱动程序的 USBInstallDriver()函数创建的所有注册键。因此,我们在驱动程序中就需要严格按照这三个函数的原型来实现,否则就不能为设备管理器所识别。

 

3.USB设备流接口驱动的实现步骤

从WinCE USB设备驱动模型及结构分析中,我们可以清晰的看到主机和外设之间的实现方式。在主机端,通过USBD模块和HCD模块使用默认的PIPE访问一个通用的逻辑设备,实际上就是说USBD和HCD是一组访问所有USB设备的逻辑接口,它们负责管理所有USB设备的连接、加载、移除、数据传输和通用配置。其中HCD是主机控制驱动,是为USBD提供底层的功能访问服务,USBD是USB总线驱动,位于HCD的上层,利用HCD的服务提供较高层次的功能。因此,实现USB加载流驱动程序大致需要完成以下步骤:

(1)选择代表设备的文件名前缀。前缀非常重要,设备管理器在注册表中通过前缀来识别设备。同时,在流接口命名时也将这个前缀作为入口点函数的前缀,如果设备前缀为XXX,那么流接口对应为XXX_Close,XXX_Init等。

(2)设置驱动的各个入口点函数。所谓入口点是指提供给设备管理器的标准文件I/O接口。在生成一个DLL后,就用设备文件名前缀替换名字中的XXX。因此,每个加载式流接口驱动程序必须实现XXX_Init()、XXX_IOControl()以及XXX_PowerUp()等一组标准的函数,用来完成标准的文件I/O函数和电源管理等。

(3)建立.DEF文件。当设备管理器初始化USB设备编译出来的流接口函数后,还必须建立一个.def文件。DEF文件定义了DLL要导出的接口集,而且加载式流驱动大多是以DLL形式存在的,所以应将DLL和DEF的文件名统一起来。DEF文件告诉链接程序需要输出什么样的函数,最后将驱动程序编译到内核中去,这样这个USB设备流接口驱动程序就可以被应用程序调用。

(4)在注册表中为驱动程序建立表项。在注册表中建立驱动程序入口点,这样设备管理器才能识别和管理这个驱动。此外,注册表中还能存储额外的信息,这些信息可以在驱动运行之后被使用到。

在这次USB驱动开发过程中,错走许多冤枉路使我叫苦连天。我感受最深的是由于WinCE提供了通用串行总线驱动程序(USBD)模块、USBD接口函数全集、样本主机控制器驱动程序(HCD)模块。所以,我们只需要根据USB设备硬件特性,利用USBD提供的不同函数,实现流接口函数与外围设备的交互。在没有特别的情况下,我最大的收获经验是把这些公用的源程序照搬过来,能极大的缩短开发周期,从而能更快速地进行嵌入式开发。


 

 

 

 

 

 

随着USB设备的普及,摆在开发人员面前的驱动开发任务也是越来越繁重了,特别是对于一些嵌入式开发厂商来讲,由于设备所采用的操作系统不同,相应的硬件接口也是不一样的,开发相关的USB 驱动程序更是难上加难。Windows CE.NET 是微软推出的功能强大的嵌入式操作系统,国内采用此操作系统的厂商已经很多了,本文就以windows ce.net为例,简单介绍一下如何开发windows ce.net下的USB驱动程序。 

首先要熟悉一些USB的基本概念,当然最好把USB 1.1的协议看一遍,(当然现在2。0的协议都已经有了)http://www.usb.org 
上可以下载,我记得好像有个中文版的,翻译的还可以,http://www.driverdevolep.com 
上有的,具体位置记不太清楚了,中文版的协议可以快速翻一边,了解一些基本的概念,但是设计到一些关键性的东西最好还是看英文版的心里比较清楚些。 

这里我就不介绍USB的基本协议了,假设用户已经熟悉了USB设备的一些基本的概念,并且对Winows CE.NET的开发有一定的了解。 

下面简略介绍一下Windows CE.NET中USB设备驱动开发的一些基础知识。 

Windows CE.NET 的USB系统软件分为两层: USB Client设备驱动程序和底层的Windows CE实现的函数层。USB设备驱动程序主要负责利用系统提供的底层接口配置设备,和设备进行通讯。底层的函数提本身又由两部分组成,通用串行总线驱动程序(USBD)模块和较低的主控制器驱动程序(HCD)模块。HCD负责最最底层的处理,USBD模块实现较高的USBD函数接口。USB设备驱动主要利用 USBD接口函数和他们的外围设备打交道。 

USB设备驱动程序主要和USBD打交道,所以我们必须详细的了解USBD提供的函数。 

主要的传输函数有: 
AbourtTransfer   IssueControlTransfer 
CloseTransfer    IssueInterrupTransfer 
GetIsochResult   IssueIsochTransfer 
GetTransferStatus  IstransferComplete 
IssueBulkTransfer  IssueVendorTransfer 

主要的用于打开和关闭USBD和USB设备之间的通信通道的函数有: 
AbortPipeTransfers  ClosePipe 
IsDefaultPipeHalted  IsPipeHalted 
OpenPipe       ResetDefaultPipe 
ResetPipe 

相应的打包函数接口有: 
GetFrameLength   GetFrameNumber   ReleaseFrameLengthControl 
SetFrameLength   TakeFrameLengthControl 

取得设置设备配置函数: 
ClearFeature   SetDescriptor 
GetDescriptor  SetFeature 
GetInterface   SetInterface 
GetStatus    SyncFrame 

与USB进行交互的实现方法相关的多任务函数: 
FindInterface         RegisterClientDeviceId 
GetDeviceInfo         RegisterClientSettings 
GetUSBDVersion         RegisterNotificationRoutine 
LoadGenericInterfaceDriver   TranslateStringDescr 
OpenClientRegisterKey     UnRegisterNotificationRoutine 
常见的Windows CE.NET下USB的设备驱动程序的编写有以下几种方法: 

● 流式接口函数 
这种驱动程序主要呈现流式函数接口,主要输出XXX_Init,XXX_Deinit,XXX_Open,XXX_Close,XXX_Open,XXX_Close,XXX_Read,XXX_Write, 
XXX_Seek, XXX_IOControl,XXX_PowerUp,XXX_PowerDown等流式接口,注意上述的几个接口一定都要输出,另外XXX必须为三个字符,否则会出错。但是此类的驱动程序不是通过设备管理接口来加载的,所以必须手工的调用RegisterDevice()和 DeregisterDevice()函数来加载和卸载驱动程序。用户可以将此类的设备作为标准的文件来操作,只要调用相应的文件操作就可以和驱动程序打交道。 

● 使用现有的Window CE.NET的应用程序接口 
此类设备主要是利用Windows CE.NET中已经有了现成的函数接口,例如USB Mass Storage Disk,它主要利用现有的Windows CE.Net中已经有的可安装文件系统接口,呈现给系统可用的文件系统,对于用户来讲,它是透明的,用户仅仅感觉在操作一个文件夹。 

● 创建指定到特定的USBD的用户指定的API 
这种方法在USBD呈现设备时不需要任何限制,主要是特制的提供API给用户,一般不太常见。 


USB设备驱动程序必须输出的函数有: 
● USBDeviecAttach 
当USB设备连接到计算机上时,USBD模块就会调用此函数,这个函数主要用于初始化USB设备,取得USB设备信息,配置USB设备,并且申请必需的资源。 
● USBInstallDriver 
主要用于创建一个驱动程序加载所需的注册表信息,例如读写超时,设备名称等。 
● USBUninstallDriver 
主要用于释放驱动程序所占用的资源,以及删除USBInstallDriver函数创建的注册表等。 
上述的三个函数接口是所有的USB驱动程序必须提供的,缺一不可。 
另外比较重要的是USB设备驱动程序的注册表配置,一般的USB设备驱动程序的注册表配置在HKEY_LOCAL_MACHINE/Drivers/USB /LoadClients下,每个驱动程序的子键都有Group1_ID/Group2_ID/Group3_ID/DriverName格式,如果注册表信息与USB设备信息符合,USBD就会加载此驱动程序。否则设备的子键应该由供应商,设备类和协议信息通过下划线组成。 
具体的配置举个例子: 
例如你有个PDA设备,它具有一个USB接口,它的供应厂商ID假设为0x0888,设备ID为0x0999,没有使用特殊的协议,那么它的加载注册表应该写为: 
[HKEY_LOCAL_MACHINE/Drivers/USB/LoadClients/2184_2457/Default/Default/PDA] "DLL"="pdausb.dll" 
需要注意的是注册表构成都是十进制数值来标识的,注意一下十进制和十六进制的转换。 
再举个USB鼠标的例子,USB鼠标是标准的HID设备,它的协议为:InterfaceClassCode为3(HID类), InterfaceSubclassCode为1(引导接口类),InterfaceProtocolCode为2(鼠标协议类),所以它的注册如下: 
[HKEY_LOCAL_MACHINE/Drivers/USB/LoadClients/Default/Default/3_1_2/USBMouse] "DLL"="usbmouse.dll" 

到此为止,我们可以看出,其实驱动开发无非做两件事情,一件是和硬件打交道,另外一件是和操作系统打交道。举个简单的例子,例如:我们需要开发一个USB鼠标驱动程序,我们就需要了解USB鼠标硬件上是怎么发送数据的?操作系统怎么才能得到鼠标的控制事件?其实USB鼠标是有一个中断PIPE的,用于传送鼠标产生的数据,Windwos CE.NET中有个接口函数叫做mouse_event(),专门用于产生鼠标事件,但是它是不关心具体什么硬件的,甚至我们自己在应用程序中调用这个函数都可以实现模拟鼠标,对应的有个keybd_event(),用于产生键盘事件,知道了这个就好办多了,只要将相应的数据转换一下,调用一下 mouse_event()即可

例如我们有个USB Mouse设备,设备信息描述如下: 
Device Descriptor: 
bcdUSB: 0x0100 
bDeviceClass: 0x00 
bDeviceSubClass: 0x00 
bDeviceProtocol: 0x00 
bMaxPacketSize0: 0x08 (8) 
idVendor: 0x05E3 (Genesys Logic Inc.) 
idProduct: 0x0001 
bcdDevice: 0x0101 
iManufacturer: 0x00 
iProduct: 0x01 
iSerialNumber: 0x00 
bNumConfigurations: 0x01 

ConnectionStatus: DeviceConnected 
Current Config Value: 0x01 
Device Bus Speed: Low 
Device Address: 0x02 
Open Pipes: 1 

Endpoint Descriptor: 
bEndpointAddress: 0x81 
Transfer Type: Interrupt 
wMaxPacketSize: 0x0003 (3) 
bInterval: 0x0A 

可以看出上述设备有一个中断PIPE,包的最大值为3。可能有人问上述的值怎么得到的,win2k 的DDK中有个usbview的例程,编译一下,将你的USB设备插到PC机的USB口中,运行usbview.exe即可看得相应的设备信息。 

有了这些基本信息,就可以编写USB设备了,首先声明一下,下面的代码取自微软的USB鼠标样本程序,版权归微软所有,此处仅仅借用来描述一下USB鼠标驱动的开发过程,读者如需要引用此代码,需要得到微软的同意。 

首先,必须输出USBD要求调用的三个函数,首先到设备插入到USB端口时,USBD会调用USBDeviceAttach()函数,相应的代码如下: 
extern "C" BOOL 
USBDeviceAttach( 
USB_HANDLE hDevice, // USB设备句柄 
LPCUSB_FUNCS lpUsbFuncs, // USBDI的函数集合 
LPCUSB_INTERFACE lpInterface, // 设备接口描述信息 
LPCWSTR szUniqueDriverId, // 设备ID描述字符串。 
LPBOOL fAcceptControl, // 返回TRUE,标识我们可以控制此设备, 反之表示不能控制 
DWORD dwUnused) 

*fAcceptControl = FALSE; 
// 我们的鼠标设备有特定的描述信息,要检测是否是我们的设备。 
if (lpInterface == NULL) 
return FALSE; 
// 打印相关的USB设备接口描述信息。 
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: DeviceAttach, IF %u, #EP:%u, Class:%u, Sub:%u,Prot:%u/r/n"), lpInterface->Descriptor.bInterfaceNumber,lpInterface->Descriptor.bNumEndpoints, lpInterface->Descriptor.bInterfaceClass,lpInterface->Descriptor.bInterfaceSubClass,lpInterface->Descriptor.bInterfaceProtocol)); 
// 初试数据USB鼠标类,产生一个接受USB鼠标数据的线程 
CMouse * pMouse = new CMouse(hDevice, lpUsbFuncs, lpInterface); 
if (pMouse == NULL) 
return FALSE; 

if (!pMouse->Initialize()) 

delete pMouse; 
return FALSE; 


// 注册一个监控USB设备事件的回调函数,用于监控USB设备是否已经拔掉。 
(*lpUsbFuncs->lpRegisterNotificationRoutine)(hDevice, 
USBDeviceNotifications, pMouse); 

*fAcceptControl = TRUE; 
return TRUE; 


第二个函数是 USBInstallDriver()函数, 
一些基本定义如下: 
const WCHAR gcszRegisterClientDriverId[] = L"RegisterClientDriverID"; 
const WCHAR gcszRegisterClientSettings[] = L"RegisterClientSettings"; 
const WCHAR gcszUnRegisterClientDriverId[] = L"UnRegisterClientDriverID"; 
const WCHAR gcszUnRegisterClientSettings[] = L"UnRegisterClientSettings"; 
const WCHAR gcszMouseDriverId[] = L"Generic_Sample_Mouse_Driver"; 

函数接口如下: 
extern "C" BOOL 
USBInstallDriver( 
LPCWSTR szDriverLibFile) // @parm [IN] - Contains client driver DLL name 

BOOL fRet = FALSE; 
HINSTANCE hInst = LoadLibrary(L"USBD.DLL"); 

// 注册USB设备信息 
if(hInst) 

LPREGISTER_CLIENT_DRIVER_ID pRegisterId = (LPREGISTER_CLIENT_DRIVER_ID) 
GetProcAddress(hInst, gcszRegisterClientDriverId); 

LPREGISTER_CLIENT_SETTINGS pRegisterSettings = 
(LPREGISTER_CLIENT_SETTINGS) GetProcAddress(hInst, 
gcszRegisterClientSettings); 

if(pRegisterId && pRegisterSettings) 

USB_DRIVER_SETTINGS DriverSettings; 

DriverSettings.dwCount = sizeof(DriverSettings); 

// 设置我们的特定的信息。 
DriverSettings.dwVendorId = USB_NO_INFO; 
DriverSettings.dwProductId = USB_NO_INFO; 
DriverSettings.dwReleaseNumber = USB_NO_INFO; 

DriverSettings.dwDeviceClass = USB_NO_INFO; 
DriverSettings.dwDeviceSubClass = USB_NO_INFO; 
DriverSettings.dwDeviceProtocol = USB_NO_INFO; 

DriverSettings.dwInterfaceClass = 0x03; // HID 
DriverSettings.dwInterfaceSubClass = 0x01; // boot device 
DriverSettings.dwInterfaceProtocol = 0x02; // mouse 

fRet = (*pRegisterId)(gcszMouseDriverId); 

if(fRet) 

fRet = (*pRegisterSettings)(szDriverLibFile, 
gcszMouseDriverId, NULL, &DriverSettings); 

if(!fRet) 

//BUGBUG unregister the Client Driver’s ID 



else 

RETAILMSG(1,(TEXT("!USBMouse: Error getting USBD function pointers/r/n"))); 

FreeLibrary(hInst); 

return fRet; 

上述代码主要用于产生USB设备驱动程序需要的注册表信息,需要注意的是:USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()和RegisterClientSettings来注册相应的设备信息。 

另外一个函数是USBUninstallDriver()函数,具体代码如下: 
extern "C" BOOL 
USBUnInstallDriver() 

BOOL fRet = FALSE; 
HINSTANCE hInst = LoadLibrary(L"USBD.DLL"); 

if(hInst) 

LPUN_REGISTER_CLIENT_DRIVER_ID pUnRegisterId = 
(LPUN_REGISTER_CLIENT_DRIVER_ID) 
GetProcAddress(hInst, gcszUnRegisterClientDriverId); 

LPUN_REGISTER_CLIENT_SETTINGS pUnRegisterSettings = 
(LPUN_REGISTER_CLIENT_SETTINGS) GetProcAddress(hInst, 
gcszUnRegisterClientSettings); 

if(pUnRegisterSettings) 

USB_DRIVER_SETTINGS DriverSettings; 

DriverSettings.dwCount = sizeof(DriverSettings); 
// 必须填入与注册时相同的信息。 
DriverSettings.dwVendorId = USB_NO_INFO; 
DriverSettings.dwProductId = USB_NO_INFO; 
DriverSettings.dwReleaseNumber = USB_NO_INFO; 

DriverSettings.dwDeviceClass = USB_NO_INFO; 
DriverSettings.dwDeviceSubClass = USB_NO_INFO; 
DriverSettings.dwDeviceProtocol = USB_NO_INFO; 

DriverSettings.dwInterfaceClass = 0x03; // HID 
DriverSettings.dwInterfaceSubClass = 0x01; // boot device 
DriverSettings.dwInterfaceProtocol = 0x02; // mouse 

fRet = (*pUnRegisterSettings)(gcszMouseDriverId, NULL, 
&DriverSettings); 


if(pUnRegisterId) 

BOOL fRetTemp = (*pUnRegisterId)(gcszMouseDriverId); 
fRet = fRet ? fRetTemp : fRet; 

FreeLibrary(hInst); 

return fRet; 

此函数主要用于删除USBInstallDriver()时创建的注册表信息,同样的它使用自己的函数接口UnRegisterClientDriverID()和UnRegisterClientSettings()来做相应的处理。 

另外一个需要处理的注册的监控通知函数USBDeviceNotifications(): 
extern "C" BOOL USBDeviceNotifications(LPVOID lpvNotifyParameter, DWORD dwCode, 
LPDWORD * dwInfo1, LPDWORD * dwInfo2, LPDWORD * dwInfo3, 
LPDWORD * dwInfo4) 

CMouse * pMouse = (CMouse *)lpvNotifyParameter; 

switch(dwCode) 

case USB_CLOSE_DEVICE: 
//删除相关的资源。 
delete pMouse; 
return TRUE; 

return FALSE; 



USB鼠标的类的定义如下: 
class CMouse 

public: 
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs, 
LPCUSB_INTERFACE lpInterface); 
~CMouse(); 

BOOL Initialize(); 
private: 
// 传输完毕调用的回调函数 
static DWORD CALLBACK MouseTransferCompleteStub(LPVOID lpvNotifyParameter); 
// 中断处理函数 
static ULONG CALLBACK CMouse::MouseThreadStub(PVOID context); 
DWORD MouseTransferComplete(); 
DWORD MouseThread(); 

BOOL SubmitInterrupt(); 
BOOL HandleInterrupt(); 

BOOL m_fClosing; 
BOOL m_fReadyForMouseEvents; 

HANDLE m_hEvent; 
HANDLE m_hThread; 

USB_HANDLE m_hDevice; 
USB_PIPE m_hInterruptPipe; 
USB_TRANSFER m_hInterruptTransfer; 

LPCUSB_FUNCS m_lpUsbFuncs; 
LPCUSB_INTERFACE m_pInterface; 

BOOL m_fPrevButton1; 
BOOL m_fPrevButton2; 
BOOL m_fPrevButton3; 

// 数据接受缓冲区。 
BYTE m_pbDataBuffer[8]; 
}; 

具体实现如下: 

// 构造函数,初始化时调用 
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs, 
LPCUSB_INTERFACE lpInterface) 

m_fClosing = FALSE; 
m_fReadyForMouseEvents = FALSE; 
m_hEvent = NULL; 
m_hThread = NULL; 

m_hDevice = hDevice; 
m_hInterruptPipe = NULL; 
m_hInterruptTransfer = NULL; 

m_lpUsbFuncs = lpUsbFuncs; 
m_pInterface = lpInterface; 

m_fPrevButton1 = FALSE; 
m_fPrevButton2 = FALSE; 
m_fPrevButton3 = FALSE; 

memset(m_pbDataBuffer, 0, sizeof(m_pbDataBuffer)); 


// 析构函数,用于清除申请的资源。 
CMouse::~CMouse() 

// 通知系统去关闭相关的函数接口。 
m_fClosing = TRUE; 

// Wake up the connection thread again and give it time to die. 
if (m_hEvent != NULL) 

// 通知关闭数据接受线程。 
SetEvent(m_hEvent); 

if (m_hThread != NULL) 

DWORD dwWaitReturn; 

dwWaitReturn = WaitForSingleObject(m_hThread, 1000); 
if (dwWaitReturn != WAIT_OBJECT_0) 

TerminateThread(m_hThread, DWORD(-1)); 

CloseHandle(m_hThread); 
m_hThread = NULL; 

CloseHandle(m_hEvent); 
m_hEvent = NULL; 


if(m_hInterruptTransfer) 
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer); 

if(m_hInterruptPipe) 
(*m_lpUsbFuncs->lpClosePipe)(m_hInterruptPipe); 



// 初始化USB鼠标驱动程序 
BOOL CMouse::Initialize() 

LPCUSB_DEVICE lpDeviceInfo = (*m_lpUsbFuncs->lpGetDeviceInfo)(m_hDevice); 

// 检测配置:USB鼠标应该只有一个中断管道 
if ((m_pInterface->lpEndpoints[0].Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) != USB_ENDPOINT_TYPE_INTERRUPT) 

RETAILMSG(1,(TEXT("!USBMouse: EP 0 wrong type (%u)!/r/n"), 
m_pInterface->lpEndpoints[0].Descriptor.bmAttributes)); 
return FALSE; 

DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: EP 0:MaxPacket: %u, Interval: %u/r/n"), 
m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize, 
m_pInterface->lpEndpoints[0].Descriptor.bInterval)); 

m_hInterruptPipe = (*m_lpUsbFuncs->lpOpenPipe)(m_hDevice, 
&m_pInterface->lpEndpoints[0].Descriptor); 

if (m_hInterruptPipe == NULL) { 
RETAILMSG(1,(TEXT("Mouse: Error opening interrupt pipe/r/n"))); 
return (FALSE); 

m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
if (m_hEvent == NULL) 

RETAILMSG(1,(TEXT("USBMouse: Error on CreateEvent for connect event/r/n"))); 
return(FALSE); 

// 创建数据接受线程 
m_hThread = CreateThread(0, 0, MouseThreadStub, this, 0, NULL); 
if (m_hThread == NULL) 

RETAILMSG(1,(TEXT("USBMouse: Error on CreateThread/r/n"))); 
return(FALSE); 


return(TRUE); 


// 从USB鼠标设备中读出数据,产生相应的鼠标事件。 
BOOL CMouse::SubmitInterrupt() 

if(m_hInterruptTransfer) 
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer); 

// 从USB鼠标PIPE中读数据 
m_hInterruptTransfer = (*m_lpUsbFuncs->lpIssueInterruptTransfer) 
(m_hInterruptPipe, MouseTransferCompleteStub, this, 
USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK, // 表示读数据 
min(m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize, 
sizeof(m_pbDataBuffer)), 
m_pbDataBuffer, 
NULL); 

if (m_hInterruptTransfer == NULL) 

DEBUGMSG(ZONE_ERROR,(L "!USBMouse: Error in IssueInterruptTransfer/r/n")); 
return FALSE; 

else 

DEBUGMSG(ZONE_TRANSFER,(L"USBMouse::SubmitInterrupt,Transfer:0x%X/r/n", 
m_hInterruptTransfer)); 

return TRUE; 


// 处理鼠标中断传输的数据 
BOOL CMouse::HandleInterrupt() 

DWORD dwError; 
DWORD dwBytes; 

DWORD dwFlags = 0; 
INT dx = (signed char)m_pbDataBuffer[1]; 
INT dy = (signed char)m_pbDataBuffer[2]; 

BOOL fButton1 = m_pbDataBuffer[0] & 0x01 ? TRUE : FALSE; 
BOOL fButton2 = m_pbDataBuffer[0] & 0x02 ? TRUE : FALSE; 
BOOL fButton3 = m_pbDataBuffer[0] & 0x04 ? TRUE : FALSE; 

if (!(*m_lpUsbFuncs->lpGetTransferStatus)(m_hInterruptTransfer, &dwBytes,&dwError)) 

DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error in GetTransferStatus(0x%X)/r/n"), 
m_hInterruptTransfer)); 
return FALSE; 

else 

DEBUGMSG(ZONE_TRANSFER,(TEXT("USBMouse::HandleInterrupt, hTransfer 0x%X complete (%u bytes, Error:%X)/r/n"), 
m_hInterruptTransfer,dwBytes,dwError)); 


if (!SubmitInterrupt()) 
return FALSE; 

if(dwError != USB_NO_ERROR) 

DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error 0x%X in interrupt transfer/r/n"),dwError)); 
return TRUE; 


if(dwBytes < 3) 

DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Invalid byte cnt %u from interrupt transfer/r/n"),dwBytes)); 
return TRUE; 


if(dx || dy) 
dwFlags |= MOUSEEVENTF_MOVE; 

if(fButton1 != m_fPrevButton1) 

if(fButton1) 
dwFlags |= MOUSEEVENTF_LEFTDOWN; 
else 
dwFlags |= MOUSEEVENTF_LEFTUP; 


if(fButton2 != m_fPrevButton2) 

if(fButton2) 
dwFlags |= MOUSEEVENTF_RIGHTDOWN; 
else 
dwFlags |= MOUSEEVENTF_RIGHTUP; 


if(fButton3 != m_fPrevButton3) 

if(fButton3) 
dwFlags |= MOUSEEVENTF_MIDDLEDOWN; 
else 
dwFlags |= MOUSEEVENTF_MIDDLEUP; 


m_fPrevButton1 = fButton1; 
m_fPrevButton2 = fButton2; 
m_fPrevButton3 = fButton3; 

DEBUGMSG(ZONE_EVENTS, 
(TEXT("USBMouse event: dx:%d, dy:%d, dwFlags:0x%X (B1:%u, B2:%u, B3:%u)/r/n"), 
dx,dy,dwFlags,fButton1,fButton2,fButton3)); 

// 通知系统产生鼠标事件 
if (m_fReadyForMouseEvents) 
mouse_event(dwFlags, dx, dy, 0, 0); 
else 
m_fReadyForMouseEvents = IsAPIReady(SH_WMGR); 

return TRUE; 



DWORD CALLBACK CMouse::MouseTransferCompleteStub(LPVOID lpvNotifyParameter) 

CMouse * pMouse = (CMouse *)lpvNotifyParameter; 
return(pMouse->MouseTransferComplete()); 


// 数据传输完毕回调函数 
DWORD CMouse::MouseTransferComplete() 

if (m_hEvent) 
SetEvent(m_hEvent); 
return 0; 



ULONG CALLBACK CMouse::MouseThreadStub(PVOID context) 

CMouse * pMouse = (CMouse *)context; 
return(pMouse->MouseThread()); 


// USB鼠标线程 
DWORD CMouse::MouseThread() 

DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: Worker thread started/r/n"))); 
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); 

if (SubmitInterrupt()) 

while (!m_fClosing) 

WaitForSingleObject(m_hEvent, INFINITE); 

if (m_fClosing) 
break; 

if ((*m_lpUsbFuncs->lpIsTransferComplete)(m_hInterruptTransfer)) 

if (!HandleInterrupt()) 
break; 

else 

RETAILMSG(1,(TEXT("!USBMouse: Event signalled, but transfer not complete/r/n"))); 
// The only time this should happen is if we get an error on the transfer 
ASSERT(m_fClosing || (m_hInterruptTransfer == NULL)); 
break; 



RETAILMSG(1,(TEXT("USBMouse: Worker thread exiting/r/n"))); 
return(0); 


看到了没有,其实USB的驱动程序编写就这么简单,类似的其他设备,例如打印机设备,就有Bulk OUT PIPE,需要Bulk传输,那就需要了解一下IssueBulkTransfer()的应用。当然如果是开发USB Mass Storage Disk的驱动,那就需要了解更多的协议,例如Bulk-Only Transport协议等。 

微软的Windows CE.NET的Platform Build中已经带有USB Printer和USB Mass Storage Disk的驱动的源代码了,好好研究一下,你一定回受益非浅的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值