隨著USB設(shè)備的普及,擺在開(kāi)發(fā)人員面前的驅(qū)動(dòng)開(kāi)發(fā)任務(wù)也是越來(lái)越繁重了,特別是對(duì)于一些嵌入式開(kāi)發(fā)廠商來(lái)講,由于設(shè)備所采用的操作系統(tǒng)不同,相應(yīng)的硬件接口也是不一樣的,開(kāi)發(fā)相關(guān)的USB驅(qū)動(dòng)程序更是難上加難。Windows CE.NET 是微軟推出的功能強(qiáng)大的嵌入式操作系統(tǒng),國(guó)內(nèi)采用此操作系統(tǒng)的廠商已經(jīng)很多了,本文就以windows ce.net為例,簡(jiǎn)單介紹一下如何開(kāi)發(fā)windows ce.net下的USB驅(qū)動(dòng)程序。
首先要熟悉一些USB的基本概念,當(dāng)然最好把USB 1.1的協(xié)議看一遍,(當(dāng)然現(xiàn)在2.0的協(xié)議都已經(jīng)有了)http://www.usb.org上可以下載,我記得好像有個(gè)中文版的,翻譯的還可以,http://www.driverdevolep.com上有的,具體位置記不太清楚了,中文版的協(xié)議可以快速翻一邊,了解一些基本的概念,但是設(shè)計(jì)到一些關(guān)鍵性的東西最好還是看英文版的心里比較清楚些。
這里我就不介紹USB的基本協(xié)議了,假設(shè)用戶已經(jīng)熟悉了USB設(shè)備的一些基本的概念,并且對(duì)Winows CE.NET的開(kāi)發(fā)有一定的了解。
下面簡(jiǎn)略介紹一下Windows CE.NET中USB設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的一些基礎(chǔ)知識(shí)。
Windows CE.NET 的USB系統(tǒng)軟件分為兩層: USB Client設(shè)備驅(qū)動(dòng)程序和底層的Windows CE實(shí)現(xiàn)的函數(shù)層。USB設(shè)備驅(qū)動(dòng)程序主要負(fù)責(zé)利用系統(tǒng)提供的底層接口配置設(shè)備,和設(shè)備進(jìn)行通訊。底層的函數(shù)提本身又由兩部分組成,通用串行總線驅(qū)動(dòng)程序(USBD)模塊和較低的主控制器驅(qū)動(dòng)程序(HCD)模塊。HCD負(fù)責(zé)最最底層的處理,USBD模塊實(shí)現(xiàn)較高的USBD函數(shù)接口。USB設(shè)備驅(qū)動(dòng)主要利用USBD接口函數(shù)和他們的外圍設(shè)備打交道。
USB設(shè)備驅(qū)動(dòng)程序主要和USBD打交道,所以我們必須詳細(xì)的了解USBD提供的函數(shù)。
主要的傳輸函數(shù)有:
AbourtTransferI(yíng)ssueControlTransfer
CloseTransfer I(yíng)ssueInterrupTransfer
GetIsochResultI(yíng)ssueIsochTransfer
GetTransferStatus IstransferComplete
IssueBulkTransfer IssueVendorTransfer
主要的用于打開(kāi)和關(guān)閉USBD和USB設(shè)備之間的通信通道的函數(shù)有:
AbortPipeTransfersClosePipe
IsDefaultPipeHalted I(yíng)sPipeHalted
OpenPipeResetDefaultPipe
ResetPipe
相應(yīng)的打包函數(shù)接口有:
GetFrameLengthGetFrameNumberReleaseFrameLengthControl
SetFrameLengthTakeFrameLengthControl
取得設(shè)置設(shè)備配置函數(shù):
ClearFeature SetDescriptor
GetDescriptorSetFeature
GetInterface SetInterface
GetStatusSyncFrame
與USB進(jìn)行交互的實(shí)現(xiàn)方法相關(guān)的多任務(wù)函數(shù):
FindInterfaceRegisterClientDeviceId
GetDeviceInfoRegisterClientSettings
GetUSBDVersion RegisterNotificationRoutine
LoadGenericInterfaceDriver TranslateStringDescr
OpenClientRegisterKeyUnRegisterNotificationRoutine
常見(jiàn)的Windows CE.NET下USB的設(shè)備驅(qū)動(dòng)程序的編寫(xiě)有以下幾種方法:
● 流式接口函數(shù)
這種驅(qū)動(dòng)程序主要呈現(xiàn)流式函數(shù)接口,主要輸出XXX_Init,XXX_Deinit,XXX_Open,XXX_Close,XXX_Open,XXX_Close,XXX_Read,XXX_Write,
XXX_Seek,XXX_IOControl,XXX_PowerUp,XXX_PowerDown等流式接口,注意上述的幾個(gè)接口一定都要輸出,另外XXX必須為三個(gè)字符,否則會(huì)出錯(cuò)。但是此類(lèi)的驅(qū)動(dòng)程序不是通過(guò)設(shè)備管理接口來(lái)加載的,所以必須手工的調(diào)用RegisterDevice()和DeregisterDevice()函數(shù)來(lái)加載和卸載驅(qū)動(dòng)程序。用戶可以將此類(lèi)的設(shè)備作為標(biāo)準(zhǔn)的文件來(lái)操作,只要調(diào)用相應(yīng)的文件操作就可以和驅(qū)動(dòng)程序打交道。
● 使用現(xiàn)有的Window CE.NET的應(yīng)用程序接口
此類(lèi)設(shè)備主要是利用Windows CE.NET中已經(jīng)有了現(xiàn)成的函數(shù)接口,例如USB Mass Storage Disk,它主要利用現(xiàn)有的Windows CE.Net中已經(jīng)有的可安裝文件系統(tǒng)接口,呈現(xiàn)給系統(tǒng)可用的文件系統(tǒng),對(duì)于用戶來(lái)講,它是透明的,用戶僅僅感覺(jué)在操作一個(gè)文件夾。
● 創(chuàng)建指定到特定的USBD的用戶指定的API
這種方法在USBD呈現(xiàn)設(shè)備時(shí)不需要任何限制,主要是特制的提供API給用戶,一般不太常見(jiàn)。
USB設(shè)備驅(qū)動(dòng)程序必須輸出的函數(shù)有:
● USBDeviecAttach
當(dāng)USB設(shè)備連接到計(jì)算機(jī)上時(shí),USBD模塊就會(huì)調(diào)用此函數(shù),這個(gè)函數(shù)主要用于初始化USB設(shè)備,取得USB設(shè)備信息,配置USB設(shè)備,并且申請(qǐng)必需的資源。
● USBInstallDriver
主要用于創(chuàng)建一個(gè)驅(qū)動(dòng)程序加載所需的注冊(cè)表信息,例如讀寫(xiě)超時(shí),設(shè)備名稱等。
● USBUninstallDriver
主要用于釋放驅(qū)動(dòng)程序所占用的資源,以及刪除USBInstallDriver函數(shù)創(chuàng)建的注冊(cè)表等。
上述的三個(gè)函數(shù)接口是所有的USB驅(qū)動(dòng)程序必須提供的,缺一不可。
另外比較重要的是USB設(shè)備驅(qū)動(dòng)程序的注冊(cè)表配置,一般的USB設(shè)備驅(qū)動(dòng)程序的注冊(cè)表配置在HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients下,每個(gè)驅(qū)動(dòng)程序的子鍵都有Group1_ID\Group2_ID\Group3_ID\DriverName格式,如果注冊(cè)表信息與USB設(shè)備信息符合,USBD就會(huì)加載此驅(qū)動(dòng)程序。否則設(shè)備的子鍵應(yīng)該由供應(yīng)商,設(shè)備類(lèi)和協(xié)議信息通過(guò)下劃線組成。
具體的配置舉個(gè)例子:
例如你有個(gè)PDA設(shè)備,它具有一個(gè)USB接口,它的供應(yīng)廠商ID假設(shè)為0x0888,設(shè)備ID為0x0999,沒(méi)有使用特殊的協(xié)議,那么它的加載注冊(cè)表應(yīng)該寫(xiě)為:
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\2184_2457\Default\Default\PDA] "DLL"="pdausb.dll"
需要注意的是注冊(cè)表構(gòu)成都是十進(jìn)制數(shù)值來(lái)標(biāo)識(shí)的,注意一下十進(jìn)制和十六進(jìn)制的轉(zhuǎn)換。
再舉個(gè)USB鼠標(biāo)的例子,USB鼠標(biāo)是標(biāo)準(zhǔn)的HID設(shè)備,它的協(xié)議為:InterfaceClassCode為3(HID類(lèi)),InterfaceSubclassCode為1(引導(dǎo)接口類(lèi)),InterfaceProtocolCode為2(鼠標(biāo)協(xié)議類(lèi)),所以它的注冊(cè)如下:
[HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\3_1_2\USBMouse] "DLL"="usbmouse.dll"
到此為止,我們可以看出,其實(shí)驅(qū)動(dòng)開(kāi)發(fā)無(wú)非做兩件事情,一件是和硬件打交道,另外一件是和操作系統(tǒng)打交道。舉個(gè)簡(jiǎn)單的例子,例如:我們需要開(kāi)發(fā)一個(gè)USB鼠標(biāo)驅(qū)動(dòng)程序,我們就需要了解USB鼠標(biāo)硬件上是怎么發(fā)送數(shù)據(jù)的?操作系統(tǒng)怎么才能得到鼠標(biāo)的控制事件?其實(shí)USB鼠標(biāo)是有一個(gè)中斷PIPE的,用于傳送鼠標(biāo)產(chǎn)生的數(shù)據(jù),Windwos CE.NET中有個(gè)接口函數(shù)叫做mouse_event(),專門(mén)用于產(chǎn)生鼠標(biāo)事件,但是它是不關(guān)心具體什么硬件的,甚至我們自己在應(yīng)用程序中調(diào)用這個(gè)函數(shù)都可以實(shí)現(xiàn)模擬鼠標(biāo),對(duì)應(yīng)的有個(gè)keybd_event(),用于產(chǎn)生鍵盤(pán)事件,知道了這個(gè)就好辦多了,只要將相應(yīng)的數(shù)據(jù)轉(zhuǎn)換一下,調(diào)用一下mouse_event()即可,
上述講了堆理論,可能讀者腦袋都已經(jīng)大了,為此,我們舉個(gè)簡(jiǎn)單的例子來(lái)詳細(xì)說(shuō)明一下驅(qū)動(dòng)程序的開(kāi)發(fā)過(guò)程。
例如我們有個(gè)USB Mouse設(shè)備,設(shè)備信息描述如下:
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
可以看出上述設(shè)備有一個(gè)中斷PIPE,包的最大值為3。可能有人問(wèn)上述的值怎么得到的,win2k 的DDK中有個(gè)usbview的例程,編譯一下,將你的USB設(shè)備插到PC機(jī)的USB口中,運(yùn)行usbview.exe即可看得相應(yīng)的設(shè)備信息。
有了這些基本信息,就可以編寫(xiě)USB設(shè)備了,首先聲明一下,下面的代碼取自微軟的USB鼠標(biāo)樣本程序,版權(quán)歸微軟所有,此處僅僅借用來(lái)描述一下USB鼠標(biāo)驅(qū)動(dòng)的開(kāi)發(fā)過(guò)程,讀者如需要引用此代碼,需要得到微軟的同意。
首先,必須輸出USBD要求調(diào)用的三個(gè)函數(shù),首先到設(shè)備插入到USB端口時(shí),USBD會(huì)調(diào)用USBDeviceAttach()函數(shù),相應(yīng)的代碼如下:
extern "C" BOOL
USBDeviceAttach(
USB_HANDLE hDevice, // USB設(shè)備句柄
LPCUSB_FUNCS lpUsbFuncs, // USBDI的函數(shù)集合
LPCUSB_INTERFACE lpInterface, // 設(shè)備接口描述信息
LPCWSTR szUniqueDriverId, // 設(shè)備ID描述字符串。
LPBOOL fAcceptControl, // 返回TRUE,標(biāo)識(shí)我們可以控制此設(shè)備, 反之表示不能控制
DWORD dwUnused)
{
*fAcceptControl = FALSE;
// 我們的鼠標(biāo)設(shè)備有特定的描述信息,要檢測(cè)是否是我們的設(shè)備。
if (lpInterface == NULL)
return FALSE;
// 打印相關(guān)的USB設(shè)備接口描述信息。
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));
// 初試數(shù)據(jù)USB鼠標(biāo)類(lèi),產(chǎn)生一個(gè)接受USB鼠標(biāo)數(shù)據(jù)的線程
CMouse * pMouse = new CMouse(hDevice, lpUsbFuncs, lpInterface);
if (pMouse == NULL)
return FALSE;
if (!pMouse->Initialize())
{
delete pMouse;
return FALSE;
}
// 注冊(cè)一個(gè)監(jiān)控USB設(shè)備事件的回調(diào)函數(shù),用于監(jiān)控USB設(shè)備是否已經(jīng)拔掉。
(*lpUsbFuncs->lpRegisterNotificationRoutine)(hDevice,
USBDeviceNotifications, pMouse);
*fAcceptControl = TRUE;
return TRUE;
}
第二個(gè)函數(shù)是 USBInstallDriver()函數(shù),
一些基本定義如下:
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";
函數(shù)接口如下:
extern "C" BOOL
USBInstallDriver(
LPCWSTR szDriverLibFile) // @parm [IN] - Contains client driver DLL name
{
BOOL fRet = FALSE;
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");
// 注冊(cè)USB設(shè)備信息
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);
// 設(shè)置我們的特定的信息。
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;
}
上述代碼主要用于產(chǎn)生USB設(shè)備驅(qū)動(dòng)程序需要的注冊(cè)表信息,需要注意的是:USB設(shè)備驅(qū)動(dòng)程序不使用標(biāo)準(zhǔn)的注冊(cè)表函數(shù),而是使用RegisterClientDriverID()和RegisterClientSettings來(lái)注冊(cè)相應(yīng)的設(shè)備信息。
另外一個(gè)函數(shù)是USBUninstallDriver()函數(shù),具體代碼如下:
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);
// 必須填入與注冊(cè)時(shí)相同的信息。
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;
}
此函數(shù)主要用于刪除USBInstallDriver()時(shí)創(chuàng)建的注冊(cè)表信息,同樣的它使用自己的函數(shù)接口UnRegisterClientDriverID()和UnRegisterClientSettings()來(lái)做相應(yīng)的處理。
另外一個(gè)需要處理的注冊(cè)的監(jiān)控通知函數(shù)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:
//刪除相關(guān)的資源。
delete pMouse;
return TRUE;
}
return FALSE;
}
USB鼠標(biāo)的類(lèi)的定義如下:
class CMouse
{
public:
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface);
~CMouse();
BOOL Initialize();
private:
// 傳輸完畢調(diào)用的回調(diào)函數(shù)
static DWORD CALLBACK MouseTransferCompleteStub(LPVOID lpvNotifyParameter);
// 中斷處理函數(shù)
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;
// 數(shù)據(jù)接受緩沖區(qū)。
BYTE m_pbDataBuffer[8];
};
具體實(shí)現(xiàn)如下:
// 構(gòu)造函數(shù),初始化時(shí)調(diào)用
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));
}
// 析構(gòu)函數(shù),用于清除申請(qǐng)的資源。
CMouse::~CMouse()
{
// 通知系統(tǒng)去關(guān)閉相關(guān)的函數(shù)接口。
m_fClosing = TRUE;
// Wake up the connection thread again and give it time to die.
if (m_hEvent != NULL)
{
// 通知關(guān)閉數(shù)據(jù)接受線程。
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鼠標(biāo)驅(qū)動(dòng)程序
BOOL CMouse::Initialize()
{
LPCUSB_DEVICE lpDeviceInfo = (*m_lpUsbFuncs->lpGetDeviceInfo)(m_hDevice);
// 檢測(cè)配置:USB鼠標(biāo)應(yīng)該只有一個(gè)中斷管道
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);
}
// 創(chuàng)建數(shù)據(jù)接受線程
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鼠標(biāo)設(shè)備中讀出數(shù)據(jù),產(chǎn)生相應(yīng)的鼠標(biāo)事件。
BOOL CMouse::SubmitInterrupt()
{
if(m_hInterruptTransfer)
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer);
// 從USB鼠標(biāo)PIPE中讀數(shù)據(jù)
m_hInterruptTransfer = (*m_lpUsbFuncs->lpIssueInterruptTransfer)
(m_hInterruptPipe, MouseTransferCompleteStub, this,
USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK, // 表示讀數(shù)據(jù)
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;
}
// 處理鼠標(biāo)中斷傳輸?shù)臄?shù)據(jù)
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));
// 通知系統(tǒng)產(chǎn)生鼠標(biāo)事件
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());
}
// 數(shù)據(jù)傳輸完畢回調(diào)函數(shù)
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鼠標(biāo)線程
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);
}
看到了沒(méi)有,其實(shí)USB的驅(qū)動(dòng)程序編寫(xiě)就這么簡(jiǎn)單,類(lèi)似的其他設(shè)備,例如打印機(jī)設(shè)備,就有Bulk OUT PIPE,需要Bulk傳輸,那就需要了解一下IssueBulkTransfer()的應(yīng)用。當(dāng)然如果是開(kāi)發(fā)USB Mass Storage Disk的驅(qū)動(dòng),那就需要了解更多的協(xié)議,例如Bulk-Only Transport協(xié)議等。
微軟的Windows CE.NET的Platform Build中已經(jīng)帶有USB Printer和USB Mass Storage Disk的驅(qū)動(dòng)的源代碼了,好好研究一下,你一定回受益非淺的。
參考資料:
1. 微軟出版社 <<Windows Ce Device Driver Kit>>
2. <<Universal Serial Bus Specification 1.1>> 來(lái)自http:://www.usb.org