天天看点

如何编写Windows CE.net的usb驱动程序(2)

上述讲了堆理论,可能读者脑袋都已经大了,为此,我们举个简单的例子来详细说明一下驱动程序的开发过程。 

例如我们有个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的驱动的源代码了,好好研究一下,你一定回受益非浅的。 

参考资料: 

1. 微软出版社 <<Windows Ce Device Driver Kit>> 

2. <<Universal Serial Bus Specification 1.1>> 来自http:://www.usb.org 

继续阅读