Windows CE下的USB裝置驅動程式開發執行個體

來源:互聯網
上載者:User

 

下面舉個簡單的例子來詳細說明一下驅動程式的開發過程。

例如我們有個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的Platform Build中已經帶有USB Printer和USB Mass Storage Disk的驅動的原始碼了,好好研究一下,你一定回受益非淺的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.