詳解WinCE下USB Host驅動開發

來源:互聯網
上載者:User

 

WinCE下所有的驅動都是以DLL的形式,被device.exe進程載入的,所以每個驅動程式中都要實現DllEntry函數。

    在註冊表的HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\鍵下儲存了USB Host的驅動程式資訊。當我們第一次插入USB裝置時。因為不存在這樣的資訊,所以系統會彈出一個“
未能識別的USB裝置”的對話方塊,要求使用者輸入驅動程式的名稱。該名稱就是USB Host驅動DLL的檔案名稱。在輸入了名稱後,系統會自動調用該DLL的USBInstallDriver函數。該函數
負責向註冊表添加USB Host驅動的資訊,以便再次插入裝置時,能夠識別該USB裝置。其原型如下:

    BOOL USBInstallDriver(LPCWSTR szDriverLibFile);
    其中szDriverLibFile就是輸入的DLL檔案名稱。返回TRUE表示註冊成功。
    在向註冊表註冊USB Host資訊時,不能使用普通的註冊表函數,只能使用USBD提供的註冊函數。
    BOOL RegisterClientDriverID(LPCWSTR szUniqueDriverId);
    BOOL RegisterClientSettings(LPCWSTR szDriverLibFile, LPCWSTR             szUniqueDriverId, LPCWSTR erved, LPCUSB_DRIVER_SETTINGS lpDriverSettings);
    這兩個函數在USBD.DLL中,可以通過動態方式調用,也可以通過靜態方式調用。

動態方式如下:
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");
if(hInst) {
LPREGISTER_CLIENT_DRIVER_ID lpRegisterClientId =
  (LPREGISTER_CLIENT_DRIVER_ID)GetProcAddress(
   hInst,
   L"RegisterClientDriverID");
if(!lpRegisterClientId)
  return FALSE;
LPREGISTER_CLIENT_SETTINGS lpRegisterClientSetting =
  (LPREGISTER_CLIENT_SETTINGS)GetProcAddress(
   hInst,
   L"RegisterClientSettings");
if(!lpRegisterClientSetting)
  return FALSE;
else
return FALSE;
    此後,就可以通過lpRegisterClientId和lpRegisterClientSetting函數指標調用這些函數,最後記得要FreeLibrary。

靜態方式:
在.cpp源檔案中加入
#pragma   comment(lib,"usbd.lib")
並在source檔案的TARGETLIBS變數中加入$(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\usbd.lib
如此一來,就可以直接使用這兩個函數了。
1) BOOL RegisterClientDriverID(LPCWSTR szUniqueDriverId)
該函數註冊USB Host驅動程式的ID。
2) BOOL RegisterClientSettings(LPCWSTR szDriverLibFile, LPCWSTR szUniqueDriverId, LPCWSTR erved, LPCUSB_DRIVER_SETTINGS lpDriverSettings)
該函數負責註冊驅動程式的資訊。
szDriverLibFile 設定為USBInstallDriver函數傳入的DLL驅動程式名稱。
szUniqueDriverId 設定為調用RegisterClientDriverID註冊的驅動程式ID。
erved 設定為NULL
lpDriverSettings 該參數是一個USB_DRIVER_SETTINGS結構體。其聲明如下:
typedef struct {
  DWORD dwCount;
  DWORD dwVendorId;
  DWORD dwProductId;
  DWORD dwReleaseNumber;
  DWORD dwDeviceClass;
  DWORD dwDeviceSubClass;
  DWORD dwDeviceProtocol;
  DWORD dwInterfaceClass;
  DWORD dwInterfaceSubClass;
  DWORD dwInterfaceProtocol;
} USB_DRIVER_SETTINGS;
    Count為結構體大小,其他項對應USB描述符。
    其中除Count外的各欄位,如果不設定具體的值,可以設定為USB_NO_INFO。
這個結構體中的資訊講反應到註冊表的HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\鍵下,用於在USB裝置插入時,尋找USB驅動。下面以一個例子說明:
BOOL USBInstallDriver(LPCWSTR szDriverLibFile)
{
RETAILMSG(1,(TEXT("USBInstallDriver\r\n")));
RETAILMSG(1,(TEXT("USBInstallDriver:%s\r\n"), szDriverLibFile));
BOOL fRet = FALSE;
USB_DRIVER_SETTINGS DriverSettings;
DriverSettings.dwCount = sizeof(DriverSettings);
DriverSettings.dwVendorId = 0x10C4;
DriverSettings.dwProductId = 0x0003;
DriverSettings.dwReleaseNumber = USB_NO_INFO;

DriverSettings.dwDeviceClass = USB_NO_INFO;
DriverSettings.dwDeviceSubClass = USB_NO_INFO;
DriverSettings.dwDeviceProtocol = USB_NO_INFO;

DriverSettings.dwInterfaceClass = 0;
DriverSettings.dwInterfaceSubClass = 0;
DriverSettings.dwInterfaceProtocol = 0;

fRet = RegisterClientDriverID(L"USBTest");
if (fRet) {
  fRet = RegisterClientSettings(
   szDriverLibFile,
   L"USBTest",
   NULL,
   &DriverSettings);
  if(!fRet)
   RETAILMSG(1,(TEXT("RegisterClientSettings error\r\n")));
} else
  RETAILMSG(1,(TEXT("RegisterClientDriverID error\r\n")));
return fRet;
}

在WinCE中,將設定資訊分為了三組,每組3個值,
第一組:
dwVendorId、dwProductId、dwReleaseNumber
第二組:
dwDeviceClass、dwDeviceSubClass、dwDeviceProtocol
第三組:
dwInterfaceClass、dwInterfaceSubClass、dwInterfaceProtocol
    如果註冊成功,將會在HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\鍵下出現 “第一組\第二組\第三組\註冊ID\DLL”這樣的建,索引值為DLL驅動名稱。其中每組又是由三個值中間加底線組成。如果有一個值設定為USB_NO_INFO,則鍵名不包括該值。如果整個組中每個值都設定成USB_NO_INFO,則鍵名為Default。

據上面的例子,在我的系統下,將會產生如下鍵名:
    HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\4292_3\Default\0_0_0\USBTest\DLL = "MyUSBTest" (我的驅動程式為MyUSBTest.dll)
    當使用者插入USB裝置時,它會讀取USB裝置的描述符,根據描述符中的值在註冊表中尋找驅動程式名稱。

    現在假設,我們要WinCE只支援USB鍵盤,另外我們自己實現一個USB滑鼠驅動程式。如果不加註意,我們的USB滑鼠驅動程式將不能被調用。原因正是在於這個尋找USB裝置驅動的過程。WINCE提供的USBHID驅動程式的註冊表資訊是
    HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\3\Hid_Class\DLL = "USBHID.DLL"

    其中第三組資訊只使用了dwInterfaceClass,而USB鍵盤和USB滑鼠只有dwInterfaceProtocol不同。所以,一個3概括了所有的HID,當我們的USB滑鼠插入系統後,將會調用USBHID.DLL驅動程式處理,但是它只包括鍵盤的驅動,沒有滑鼠的驅動,所以滑鼠不能使用。要想使得自訂的USB滑鼠可以使用,則將第三組的值都設定上,如下:
    HKEY_LOCAL_MACHINE\Drivers\USB\LoadClients\Default\Default\3_1_1\Hid_Class\DLL = "USBHID.DLL"

    如此一來,當值為3_1_2的滑鼠插入後,因為找不到對應的索引值,將提示要求我們輸入USB滑鼠的驅動

當使用者需要卸載USB Host裝置驅動時,將會調用USBUnInstallDriver函數
BOOL USBUnInstallDriver();
    它與USBInstallDriver類似,不過是調用如下兩個函數
UnRegisterClientSettings
    BOOL UnRegisterClientSettings(LPCWSTR szUniqueDriverId, LPCWSTR szReserved, LPCUSB_DRIVER_SETTINGS lpDriverSettings);
    BOOL UnRegisterClientDriverID(LPCWSTR szUniqueDriverId);
    其中szUniqueDriverId是註冊時,使用的ID,szReserved保留,故設定為NULL,lpDriverSettings則是驅動程式設定資訊。

常式如下:
BOOL USBUnInstallDriver()
{
 RETAILMSG(1,(TEXT("USBUninstallDriver\r\n")));
 BOOL fRet = FALSE;
 USB_DRIVER_SETTINGS DriverSettings;
 DriverSettings.dwCount = sizeof(DriverSettings);
 DriverSettings.dwVendorId = 0x10C4;
 DriverSettings.dwProductId = 0x0003;
 DriverSettings.dwReleaseNumber = USB_NO_INFO;
 
 DriverSettings.dwDeviceClass = USB_NO_INFO;
 DriverSettings.dwDeviceSubClass = USB_NO_INFO;
 DriverSettings.dwDeviceProtocol = USB_NO_INFO;
 
 DriverSettings.dwInterfaceClass = 0;
 DriverSettings.dwInterfaceSubClass = 0;
 DriverSettings.dwInterfaceProtocol = 0;
 
 fRet = UnRegisterClientSettings(L"USBTest", NULL, &DriverSettings);
 if(fRet) {
  fRet = UnRegisterClientDriverID(L"USBTest");
  if(!fRet)
   RETAILMSG(1,(TEXT("UnRegisterClientDriverID error\r\n")));
 } else
  RETAILMSG(1,(TEXT("UnRegisterClientSettings error\r\n")));
 return fRet;
}
    其中DriverSettings必須與USBInstallDriver的DriverSettings一致。
    回到原來的流程,WinCE註冊表中已經包含了驅動資訊,WinCE系統自動尋找註冊表,在找到裝置對應索引值的DLL後,將會調用該DLL的USBDeviceAttach函數。
BOOL USBDeviceAttach(
 USB_HANDLE hDevice,
 LPCUSB_FUNCS lpUsbFuncs,
 LPCUSB_INTERFACE lpInterface,
 LPCWSTR szUniqueDriverId,
 LPBOOL fAcceptControl,
 DWORD dwUnused)
    hDevice 裝置控制代碼,操作USB裝置時,需要使用該控制代碼
    lpUsbFuncs 指向一個包含各種USB操作的函數指標
    lpInterface USB介面資訊,這裡需要注意的是,如果在DriverSettings裡dwInterfaceClass、dwInterfaceSubClass、dwInterfaceProtocol設定為USB_NO_INFO,則該指標為NULL

    szUniqueDriverId 註冊裝置ID
    fAcceptControl 該值被賦值為TRUE,表示該驅動能操作該裝置。如果不能操作該裝置,則“未能識別的USB裝置”對話方塊會再次出現,要求使用者輸入驅動程式名稱
    dwUnused 未使用

    在該函數內,主要是做一些檢查,判斷是否能驅動裝置,還有就是註冊USB事件通知回呼函數,以及啟用流驅動。對於檢查部分,這裡不再詳細說明。

    首先,介紹一下啟用流驅動。
    流驅動為應用程式提供了一個訪問裝置的介面,利用該介面可以像訪問檔案一樣訪問裝置。USB裝置同樣可以使用該介面來為應用程式提供支援。在註冊表的
HKEY_LOCAL_MACHINE\Drivers\BuiltIn鍵下,儲存了各種WinCE內建流驅動程式的入口。這些驅動通過device.exe在系統啟動時被啟用。像USB這樣的裝置,只有插入時,才存在流
驅動介面,所以我們需要手動啟用流驅動。啟用流驅動的函數是:

    HANDLE ActivateDevice(LPCWSTR lpszDevKey, DWORD dwClientInfo);
lpszDevKey 字串指明了流驅動所在註冊表的鍵。獲悉流驅動的人都知道,流驅動在註冊表中必須包含兩個鍵Prefix和Dll。

    流驅動中所有介面函數都有類似XXX_的首碼,而這個Prefix則指明XXX對應的字串,如Prefix為COM,則流驅動包含如COM_Open、COM_Close、COM_Write、COM_Read這樣介面函數。Dll則說明了這些函數所在的動態連結程式庫。

在我的例子中存在如下的註冊表鍵:
[HKEY_LOCAL_MACHINE\Drivers\USB\ClientDrivers\USBTest]
   "Prefix"="TST"
   "Dll"="MyUSBTest.dll"
    通過dwClientInfo,可以把參數間接傳給驅動的XXX_init。我們可以把hDevice、lpUsbFuncs、lpInterface這樣資訊放置在一個結構體中,通過該函數傳遞給流驅動使用。
USB通知回呼函數,可以用來判斷各種USB事件的發生,如USB拔出。當發生事件後,系統會根據註冊的回呼函數做相應的處理,在USB裝置拔出後,所要做的事情,就是卸載流驅動,並釋放佔用的各種資源。

    註冊回呼函數是一個包含在lpUsbFuncs中的函數指標:
LPUN_REGISTER_NOTIFICATION_ROUTINE lpUnRegisterNotificationRoutine

該函數的聲明如下:
typedef BOOL (* LPREGISTER_NOTIFICATION_ROUTINE)(
  USB_HANDLE hDevice,
  LPDEVICE_NOTIFY_ROUTINE lpNotifyRoutine,
  LPVOID lpvNotifyParameter
);
hDevice 裝置控制代碼
lpNotifyRoutine 回呼函數
lpvNotifyParameter 傳遞給回呼函數的參數
在回呼函數中卸載流驅動使用
BOOL DeactivateDevice(HANDLE hDevice);
其中,hDevice 傳入ActivateDevice時返回的控制代碼。
下面是具體的樣本:
typedef struct {
 DWORD dwSize;
 USB_HANDLE hDevice,
 LPCUSB_FUNCS lpUsbFuncs,
 LPCUSB_INTERFACE lpInterface,
 HANDLE hStreamDevice;
} TESTUSBINFO, PTESTUSBINFO;
//回呼函數
extern "C" BOOL USBDeviceNotifications(
 LPVOID lpvNotifyParameter,
 DWORD dwCode,
 LPDWORD *dwInfo1,
 LPDWORD *dwInfo2,
 LPDWORD *dwInfo3,
 LPDWORD *dwInfo4)
{
 if (dwCode == USB_CLOSE_DEVICE) {
  PTESTUSBINFO pDrv = (PDRVCONTEXT) lpvNotifyParameter;
  DeactivateDevice(pDrv->hStreamDevice); //卸載流驅動
  LocalFree(pDrv); //釋放資源
 }
 RETAILMSG(1,(TEXT("Free Driver Resources!\r\n")));
 return TRUE;
}
BOOL USBDeviceAttach(
 USB_HANDLE hDevice,
 LPCUSB_FUNCS lpUsbFuncs,
 LPCUSB_INTERFACE lpInterface,
 LPCWSTR szUniqueDriverId,
 LPBOOL fAcceptControl,
 DWORD dwUnused)
{
 RETAILMSG(1,(TEXT("USBDeviceAttach\r\n")));
 *fAcceptControl = FALSE;
 //顯示USB裝置的一些資訊
 if(lpInterface != NULL) {
  RETAILMSG(1,(TEXT("usbserialhost: 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));
  RETAILMSG(1,(TEXT("Endpoint 1:%u\r\n"),
   lpInterface->lpEndpoints[0].Descriptor.bmAttributes));
  RETAILMSG(1,(TEXT("Endpoint 2:%u\r\n"),
   lpInterface->lpEndpoints[1].Descriptor.bmAttributes));
  RETAILMSG(1,(TEXT("Endpoint 3:%u\r\n"),
   lpInterface->lpEndpoints[2].Descriptor.bmAttributes));
 }
 LPCUSB_DEVICE lpUsbDev = (lpUsbFuncs->lpGetDeviceInfo)(hDevice);
 if(!lpUsbDev)
 {
  RETAILMSG(1,(TEXT("Unable to get USB device!\r\n")));
  return FALSE;
 }
 //儲存必要的資訊供驅動程式其他部分使用
 PTESTUSBINFO pDrv = (PTESTUSBINFO)LocalAlloc (LPTR, sizeof (PTESTUSBINFO));
 pDrv->dwSize = sizeof (DRVCONTEXT);
 pDrv->hDevice = hDevice;
 pDrv->lpUsbFuncs = lpUsbFuncs;
 pDrv->lpInterface = lpInterface;
 //啟用流驅動
 pDrv->hStreamDevice = ActivateDevice (L"Drivers\\USB\\ClientDrivers\\USBTest", (DWORD)pDrv);
 if (pDrv->hStreamDevice) {
  //註冊回呼函數
  (*lpUsbFuncs->lpRegisterNotificationRoutine)(
   hDevice,
   USBDeviceNotifications,
   pDrv);
 } else {
  RETAILMSG(1, (TEXT("Can't activate stream device! rc=%d\r\n"), GetLastError()));
  LocalFree(pDrv);
  return FALSE;
 }
 //驅動可以操作該裝置
 *fAcceptControl = TRUE;
 return TRUE;
}

    至此,USB Host端裝置驅動程式所必須實現的功能都已經實現。並且和流驅動相串連。應用程式已經可以使用流驅動的介面來操作USB裝置了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.