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