| 上述讲了堆理论,可能读者脑袋都已经大了,为此,我们举个简单的例子来详细说明一下驱动程序的开发过程。 |
| 例如我们有个USB Mouse设备,设备信息描述如下: |
| bMaxPacketSize0: 0x08 (8) |
| idVendor: 0x05E3 (Genesys Logic Inc.) |
| ConnectionStatus: DeviceConnected |
| Current Config Value: 0x01 |
| wMaxPacketSize: 0x0003 (3) |
| 可以看出上述设备有一个中断PIPE,包的最大值为3。可能有人问上述的值怎么得到的,win2k 的DDK中有个usbview的例程,编译一下,将你的USB设备插到PC机的USB口中,运行usbview.exe即可看得相应的设备信息。 |
| 有了这些基本信息,就可以编写USB设备了,首先声明一下,下面的代码取自微软的USB鼠标样本程序,版权归微软所有,此处仅仅借用来描述一下USB鼠标驱动的开发过程,读者如需要引用此代码,需要得到微软的同意。 |
| 首先,必须输出USBD要求调用的三个函数,首先到设备插入到USB端口时,USBD会调用USBDeviceAttach()函数,相应的代码如下: |
| USB_HANDLE hDevice, // USB设备句柄 |
| LPCUSB_FUNCS lpUsbFuncs, // USBDI的函数集合 |
| LPCUSB_INTERFACE lpInterface, // 设备接口描述信息 |
| LPCWSTR szUniqueDriverId, // 设备ID描述字符串。 |
| LPBOOL fAcceptControl, // 返回TRUE,标识我们可以控制此设备, 反之表示不能控制 |
| // 我们的鼠标设备有特定的描述信息,要检测是否是我们的设备。 |
| 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->Initialize()) |
| // 注册一个监控USB设备事件的回调函数,用于监控USB设备是否已经拔掉。 |
| (*lpUsbFuncs->lpRegisterNotificationRoutine)(hDevice, |
| USBDeviceNotifications, pMouse); |
| 第二个函数是 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"; |
| LPCWSTR szDriverLibFile) // @parm [IN] - Contains client driver DLL name |
| HINSTANCE hInst = LoadLibrary(L"USBD.DLL"); |
| 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); |
| fRet = (*pRegisterSettings)(szDriverLibFile, |
| gcszMouseDriverId, NULL, &DriverSettings); |
| //BUGBUG unregister the Client Driver's ID |
| RETAILMSG(1,(TEXT("!USBMouse: Error getting USBD function pointers\r\n"))); |
| 上述代码主要用于产生USB设备驱动程序需要的注册表信息,需要注意的是:USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()和RegisterClientSettings来注册相应的设备信息。 |
| 另外一个函数是USBUninstallDriver()函数,具体代码如下: |
| HINSTANCE hInst = LoadLibrary(L"USBD.DLL"); |
| 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); |
| 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, |
| BOOL fRetTemp = (*pUnRegisterId)(gcszMouseDriverId); |
| fRet = fRet ? fRetTemp : fRet; |
| 此函数主要用于删除USBInstallDriver()时创建的注册表信息,同样的它使用自己的函数接口UnRegisterClientDriverID()和UnRegisterClientSettings()来做相应的处理。 |
| 另外一个需要处理的注册的监控通知函数USBDeviceNotifications(): |
| extern "C" BOOL USBDeviceNotifications(LPVOID lpvNotifyParameter, DWORD dwCode, |
| LPDWORD * dwInfo1, LPDWORD * dwInfo2, LPDWORD * dwInfo3, |
| CMouse * pMouse = (CMouse *)lpvNotifyParameter; |
| CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs, |
| LPCUSB_INTERFACE lpInterface); |
| static DWORD CALLBACK MouseTransferCompleteStub(LPVOID lpvNotifyParameter); |
| static ULONG CALLBACK CMouse::MouseThreadStub(PVOID context); |
| DWORD MouseTransferComplete(); |
| BOOL m_fReadyForMouseEvents; |
| USB_PIPE m_hInterruptPipe; |
| USB_TRANSFER m_hInterruptTransfer; |
| LPCUSB_FUNCS m_lpUsbFuncs; |
| LPCUSB_INTERFACE m_pInterface; |
| CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs, |
| LPCUSB_INTERFACE lpInterface) |
| m_fReadyForMouseEvents = FALSE; |
| m_hInterruptTransfer = NULL; |
| m_lpUsbFuncs = lpUsbFuncs; |
| m_pInterface = lpInterface; |
| memset(m_pbDataBuffer, 0, sizeof(m_pbDataBuffer)); |
| // Wake up the connection thread again and give it time to die. |
| dwWaitReturn = WaitForSingleObject(m_hThread, 1000); |
| if (dwWaitReturn != WAIT_OBJECT_0) |
| TerminateThread(m_hThread, DWORD(-1)); |
| (*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer); |
| (*m_lpUsbFuncs->lpClosePipe)(m_hInterruptPipe); |
| BOOL CMouse::Initialize() |
| LPCUSB_DEVICE lpDeviceInfo = (*m_lpUsbFuncs->lpGetDeviceInfo)(m_hDevice); |
| 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)); |
| 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"))); |
| m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| RETAILMSG(1,(TEXT("USBMouse: Error on CreateEvent for connect event\r\n"))); |
| m_hThread = CreateThread(0, 0, MouseThreadStub, this, 0, NULL); |
| RETAILMSG(1,(TEXT("USBMouse: Error on CreateThread\r\n"))); |
| // 从USB鼠标设备中读出数据,产生相应的鼠标事件。 |
| BOOL CMouse::SubmitInterrupt() |
| (*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer); |
| m_hInterruptTransfer = (*m_lpUsbFuncs->lpIssueInterruptTransfer) |
| (m_hInterruptPipe, MouseTransferCompleteStub, this, |
| USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK, // 表示读数据 |
| min(m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize, |
| if (m_hInterruptTransfer == NULL) |
| DEBUGMSG(ZONE_ERROR,(L "!USBMouse: Error in IssueInterruptTransfer\r\n")); |
| DEBUGMSG(ZONE_TRANSFER,(L"USBMouse::SubmitInterrupt,Transfer:0x%X\r\n", |
| BOOL CMouse::HandleInterrupt() |
| 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"), |
| DEBUGMSG(ZONE_TRANSFER,(TEXT("USBMouse::HandleInterrupt, hTransfer 0x%X complete (%u bytes, Error:%X)\r\n"), |
| m_hInterruptTransfer,dwBytes,dwError)); |
| if(dwError != USB_NO_ERROR) |
| DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error 0x%X in interrupt transfer\r\n"),dwError)); |
| DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Invalid byte cnt %u from interrupt transfer\r\n"),dwBytes)); |
| dwFlags |= MOUSEEVENTF_MOVE; |
| if(fButton1 != m_fPrevButton1) |
| dwFlags |= MOUSEEVENTF_LEFTDOWN; |
| dwFlags |= MOUSEEVENTF_LEFTUP; |
| if(fButton2 != m_fPrevButton2) |
| dwFlags |= MOUSEEVENTF_RIGHTDOWN; |
| dwFlags |= MOUSEEVENTF_RIGHTUP; |
| if(fButton3 != m_fPrevButton3) |
| dwFlags |= MOUSEEVENTF_MIDDLEDOWN; |
| dwFlags |= MOUSEEVENTF_MIDDLEUP; |
| m_fPrevButton1 = fButton1; |
| m_fPrevButton2 = fButton2; |
| m_fPrevButton3 = fButton3; |
| (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); |
| m_fReadyForMouseEvents = IsAPIReady(SH_WMGR); |
| DWORD CALLBACK CMouse::MouseTransferCompleteStub(LPVOID lpvNotifyParameter) |
| CMouse * pMouse = (CMouse *)lpvNotifyParameter; |
| return(pMouse->MouseTransferComplete()); |
| DWORD CMouse::MouseTransferComplete() |
| ULONG CALLBACK CMouse::MouseThreadStub(PVOID context) |
| CMouse * pMouse = (CMouse *)context; |
| return(pMouse->MouseThread()); |
| DWORD CMouse::MouseThread() |
| DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: Worker thread started\r\n"))); |
| SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); |
| WaitForSingleObject(m_hEvent, INFINITE); |
| if ((*m_lpUsbFuncs->lpIsTransferComplete)(m_hInterruptTransfer)) |
| 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)); |
| RETAILMSG(1,(TEXT("USBMouse: Worker thread exiting\r\n"))); |
| 看到了没有,其实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 |
|