USB HID 類的編寫

USB HID類的編寫

   使用CH372 USB芯片進行USB數據通信時,CH372默認有2種模式,一種是內置固件模式,另外一種是外置固件模式。當設置CH372爲外置固件模式時,上位機界面可以脫離調用該USB芯片公司提供的dll,使用微軟提供的DDK文件提供的函數實現。

    該類庫有兩個文件,分別是CHIDUSB.cpp和CHIDUSB.h。

CHIDUSB.h

 

複製代碼
1 /*************************************************** 2 *作 者:溫子祺 3 *聯繫方式:[email protected] 4 *說 明:CHIDUSB.h 5 不要忘記引用以下的代碼 6 #include "dbt.h" 7 8 extern "C" { 9 #include "hidsdi.h" 10 #include "setupapi.h" 11 } 12 #pragma comment(lib,"hid") 13 #pragma comment(lib,"setupapi") 14 ***************************************************/ 15 #ifndef __CHIDUSB_H__ 16  #define __CHIDUSB_H__ 17 18 #include "dbt.h" 19 20  extern "C" { 21 #include "hidsdi.h" 22 #include "setupapi.h" 23 } 24 25  #pragma comment(lib,"hid") 26  #pragma comment(lib,"setupapi") 27 28 29 30  class CHIDUSB 31 { 32  public: 33 CHIDUSB(); 34 virtual ~CHIDUSB(); 35 36 BOOL Close(void); 37 BOOL Open (CWnd *pPortOwner, 38 DWORD VID, 39 DWORD PID, 40 UINT unUSBRecvBufSize, 41 UINT unRecvMsg, 42 UINT unConnectMsg 43 ); 44 45 UINT Send(UCHAR *pSendBytes,UINT unSendLen); 46 UINT Recv(UCHAR *pRecvBytes); 47 void GetDevicePath(CString &str); 48 49 protected: 50 51 BOOL Ready(void)const; //USB是否已經打開 52 BOOL CreateThreadAndEvent(void); //創建線程和事件 53 static 54 DWORD RecvThread(LPVOID lpArg); //接收線程 55 56 void RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid); 57 58 private: 59 60 CWnd * m_pOwner; 61 62 BOOL m_bInit; 63 64 HIDP_CAPS m_Capabilities; 65 66 OVERLAPPED m_ovUSBRead; 67 OVERLAPPED m_ovUSBWrite; 68 69 HANDLE m_hUSBWrite; 70 HANDLE m_hUSBRead; 71 HANDLE m_hRecvExitEvent; 72 73 UCHAR *m_szUSBRecvBuf; 74 UINT m_unUSBRecvBufSize; 75 UINT m_unRecvLength; 76 77 UINT m_unRecvMsg; 78 UINT m_unConnectMsg; 79 CString m_strDevicePath; 80 81 }; 82 83 84 85 #endif
複製代碼

 

CHIDUSB.cpp

 

 

 

複製代碼
1 /*************************************************** 2 *作 者:溫子祺 3 *聯繫方式:[email protected] 4 *說 明:CHIDUSB.cpp 5 ***************************************************/ 6 #include "StdAfx.h" 7 #include "CHIDUSB.h" 8 #include <assert.h> //使用斷言 9 10 CHIDUSB::CHIDUSB() 11 { 12 m_pOwner=NULL; 13 m_hUSBWrite=NULL; 14 m_hUSBWrite=NULL; 15 m_hUSBRead=NULL; 16 m_hRecvExitEvent=NULL; 17 m_unRecvLength=0; 18 m_bInit=FALSE; 19 m_szUSBRecvBuf=NULL; 20 } 21 22 CHIDUSB::~CHIDUSB() 23 { 24 m_pOwner=NULL; 25 m_hUSBWrite=NULL; 26 m_hUSBWrite=NULL; 27 m_hUSBRead=NULL; 28 m_hRecvExitEvent=NULL; 29 m_szUSBRecvBuf=NULL; 30 } 31 32 BOOL CHIDUSB::Close(void) 33 { 34 m_bInit=FALSE; 35 36 if (m_hUSBRead) 37 { 38 CloseHandle(m_hUSBRead); 39 m_hUSBRead=NULL; 40 } 41 42 if (m_hUSBWrite) 43 { 44 CloseHandle(m_hUSBWrite); 45 m_hUSBWrite=NULL; 46 } 47 48 if (m_ovUSBRead.hEvent) 49 { 50 CloseHandle(m_ovUSBRead.hEvent); 51 m_ovUSBRead.hEvent=NULL; 52 } 53 54 if (m_ovUSBWrite.hEvent) 55 { 56 CloseHandle(m_ovUSBWrite.hEvent); 57 m_ovUSBWrite.hEvent=NULL; 58 } 59 60 if (m_hRecvExitEvent) 61 { 62 SetEvent(m_hRecvExitEvent); //退出線程 63 CloseHandle(m_hRecvExitEvent); 64 m_hRecvExitEvent=NULL; 65 } 66 67 if (m_pOwner) 68 { 69 m_pOwner=NULL; 70 } 71 72 if (m_szUSBRecvBuf) 73 { 74 delete []m_szUSBRecvBuf; 75 m_szUSBRecvBuf=NULL; 76 } 77 78 79 return TRUE; 80 } 81 82 BOOL CHIDUSB::Ready(void)const 83 { 84 85 return m_bInit; 86 } 87 88 BOOL CHIDUSB::CreateThreadAndEvent(void) 89 { 90 m_ovUSBRead.Offset=0; 91 m_ovUSBRead.OffsetHigh=0; 92 m_ovUSBRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); 93 94 if (NULL==m_ovUSBRead.hEvent) 95 { 96 return FALSE; 97 } 98 else 99 { 100 ResetEvent(m_ovUSBRead.hEvent); 101 } 102 103 104 m_ovUSBWrite.Offset=0; 105 m_ovUSBWrite.OffsetHigh=0; 106 m_ovUSBWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); 107 108 if (NULL==m_ovUSBWrite.hEvent) 109 { 110 111 return FALSE; 112 } 113 else 114 { 115 ResetEvent(m_ovUSBWrite.hEvent); 116 } 117 118 119 m_hRecvExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* 創建接收線程退出事件*/ 120 121 if (NULL==m_hRecvExitEvent) 122 { 123 return FALSE; 124 } 125 126 ResetEvent(m_hRecvExitEvent); //設置線程沒有退出 127 128 HANDLE hThread=NULL; 129 DWORD dwThreadID =0; 130 131 // 創建接收線程 132 hThread=CreateThread( 0, 133 0, 134 (LPTHREAD_START_ROUTINE)RecvThread, 135 this, 136 0, 137 &dwThreadID); 138 139 if (NULL==hThread) 140 { 141 return FALSE; 142 } 143 144 CloseHandle(hThread); 145 hThread=NULL; 146 147 return TRUE; 148 } 149 150 BOOL CHIDUSB::Open(CWnd *pPortOwner, 151 DWORD VID, 152 DWORD PID, 153 UINT unUSBRecvBufSize, 154 UINT unRecvMsg, 155 UINT unConnectMsg) 156 { 157 assert(NULL != pPortOwner); 158 159 m_pOwner = pPortOwner; 160 161 if (Ready()) 162 { 163 Close(); 164 } 165 166 if (!m_szUSBRecvBuf) 167 { 168 m_szUSBRecvBuf=new UCHAR[unUSBRecvBufSize]; 169 m_unUSBRecvBufSize=unUSBRecvBufSize; 170 } 171 172 173 ULONG Required; 174 //定義strUsbPath 設備路徑 175 //CString strUsbPath; 176 //定義一個GUID的結構體HidGuid來保存HID設備的接口類GUID。 177 GUID HidGuid; 178 //定義一個DEVINFO的句柄hDevInfoSet來保存獲取到的設備信息集合句柄。 179 HDEVINFO hDevInfoSet; 180 //定義MemberIndex,表示當前搜索到第幾個設備,0表示第一個設備。 181 DWORD MemberIndex; 182 //DevInfoData,用來保存設備的驅動接口信息 183 SP_DEVICE_INTERFACE_DATA DevInfoData; 184 //定義一個BOOL變量,保存函數調用是否返回成功 185 BOOL Result; 186 //定義一個RequiredSize的變量,用來接收需要保存詳細信息的緩衝長度。 187 DWORD RequiredSize; 188 //定義一個指向設備詳細信息的結構體指針。 189 PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetailData; 190 //定義一個用來保存打開設備的句柄。 191 HANDLE DevHandle; 192 //定義一個HIDD_ATTRIBUTES的結構體變量,保存設備的屬性。 193 HIDD_ATTRIBUTES DevAttributes; 194 195 // Request to receive messages when a device is attached or removed. 196 // Also see WM_DEVICECHANGE in BEGIN_MESSAGE_MAP(CUsbhidiocDlg, CDialog). 197 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface; 198 199 //對DevInfoData結構體的cbSize初始化爲結構體大小 200 DevInfoData.cbSize=sizeof(DevInfoData); 201 //對DevAttributes結構體的Size初始化爲結構體大小 202 DevAttributes.Size=sizeof(DevAttributes); 203 //根據HidGuid來獲取設備信息集合。其中Flags參數設置爲 204 //DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,前者表示使用的GUID爲 205 //接口類GUID,後者表示只列舉正在使用的設備,因爲我們這裏只 206 //查找已經連接上的設備。返回的句柄保存在hDevinfo中。注意設備 207 //信息集合在使用完畢後,要使用函數SetupDiDestroyDeviceInfoList 208 //銷燬,不然會造成內存泄漏。 209 210 //調用HidD_GetHidGuid函數獲取HID設備的GUID,並保存在HidGuid中。 211 HidD_GetHidGuid(&HidGuid); 212 213 hDevInfoSet=SetupDiGetClassDevs(&HidGuid, 214 NULL, 215 NULL, 216 DIGCF_DEVICEINTERFACE|DIGCF_PRESENT); 217 //然後對設備集合中每個設備進行列舉,檢查是否是我們要找的設備 218 //當找到我們指定的設備,或者設備已經查找完畢時,就退出查找。 219 //首先指向第一個設備,即將MemberIndex置爲0。 220 MemberIndex=0; 221 222 223 while(1) 224 { 225 //調用SetupDiEnumDeviceInterfaces在設備信息集合中獲取編號爲 226 //MemberIndex的設備信息。 227 Result=SetupDiEnumDeviceInterfaces(hDevInfoSet, 228 0, 229 &HidGuid, 230 MemberIndex, 231 &DevInfoData); 232 233 //如果獲取信息失敗,則說明設備已經查找完畢,退出循環。 234 if(Result==FALSE) break; 235 236 //將MemberIndex指向下一個設備 237 MemberIndex++; 238 239 //如果獲取信息成功,則繼續獲取該設備的詳細信息。在獲取設備 240 //詳細信息時,需要先知道保存詳細信息需要多大的緩衝區,這通過 241 //第一次調用函數SetupDiGetDeviceInterfaceDetail來獲取。這時 242 //提供緩衝區和長度都爲NULL的參數,並提供一個用來保存需要多大 243 //緩衝區的變量RequiredSize。 244 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet, 245 &DevInfoData, 246 NULL, 247 0, 248 &RequiredSize, 249 NULL); 250 251 //然後,分配一個大小爲RequiredSize緩衝區,用來保存設備詳細信息。 252 pDevDetailData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize); 253 254 if(pDevDetailData==NULL) //如果內存不足,則直接返回。 255 { 256 TRACE("內存不足!"); 257 SetupDiDestroyDeviceInfoList(hDevInfoSet); 258 return FALSE; 259 } 260 261 //並設置pDevDetailData的cbSize爲結構體的大小(注意只是結構體大小, 262 //不包括後面緩衝區)。 263 pDevDetailData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 264 265 //然後再次調用SetupDiGetDeviceInterfaceDetail函數來獲取設備的 266 //詳細信息。這次調用設置使用的緩衝區以及緩衝區大小。 267 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet, 268 &DevInfoData, 269 pDevDetailData, 270 RequiredSize, 271 &Required, 272 NULL); 273 274 //將設備路徑複製出來,然後銷燬剛剛申請的內存。 275 m_strDevicePath=pDevDetailData->DevicePath; 276 free(pDevDetailData); 277 278 //如果調用失敗,則查找下一個設備。 279 if(Result==FALSE) continue; 280 281 //如果調用成功,則使用不帶讀寫訪問的CreateFile函數 282 //來獲取設備的屬性,包括VID、PID、版本號等。 283 //對於一些獨佔設備(例如USB鍵盤),使用讀訪問方式是無法打開的, 284 //而使用不帶讀寫訪問的格式纔可以打開這些設備,從而獲取設備的屬性。 285 DevHandle=CreateFile(m_strDevicePath, 286 0, 287 FILE_SHARE_READ|FILE_SHARE_WRITE, 288 (LPSECURITY_ATTRIBUTES)NULL, 289 OPEN_EXISTING, 290 0, 291 NULL); 292 293 //如果打開成功,則獲取設備屬性。 294 if(DevHandle!=INVALID_HANDLE_VALUE) 295 { 296 //獲取設備的屬性並保存在DevAttributes結構體中 297 Result=HidD_GetAttributes(DevHandle, 298 &DevAttributes); 299 300 //獲取失敗,查找下一個 301 if(Result==FALSE) 302 { 303 //關閉剛剛打開的設備 304 CloseHandle(DevHandle); 305 DevHandle=NULL; 306 continue; 307 } 308 //如果獲取成功,則將屬性中的VID、PID以及設備版本號與我們需要的 309 //進行比較,如果都一致的話,則說明它就是我們要找的設備。 310 if(DevAttributes.VendorID==VID) //如果VID相等 311 if(DevAttributes.ProductID==PID) //並且PID相等 312 //if(DevAttributes.VersionNumber==StUsbID.m_dwPVN) //並且設備版本號相等 313 { 314 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface); 315 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 316 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid; 317 //獲取設備屬性結構體 318 319 PHIDP_PREPARSED_DATA PreparsedData; 320 321 HidD_GetPreparsedData(DevHandle, 322 &PreparsedData 323 ); 324 325 HidP_GetCaps (PreparsedData, 326 &m_Capabilities 327 ); 328 //釋放資源 329 HidD_FreePreparsedData(PreparsedData); 330 331 //那麼就是我們要找的設備,分別使用讀寫方式打開之,並保存其句柄 332 //並且選擇爲異步訪問方式。 333 //讀方式打開設備 334 335 m_hUSBRead=CreateFile(m_strDevicePath, 336 GENERIC_READ, 337 FILE_SHARE_READ|FILE_SHARE_WRITE, 338 (LPSECURITY_ATTRIBUTES)NULL, 339 OPEN_EXISTING, 340 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 341 NULL); 342 343 if (INVALID_HANDLE_VALUE==m_hUSBRead) 344 { 345 TRACE("讀訪問打開HidUsb設備失敗......!"); 346 } 347 else 348 { 349 TRACE("讀訪問打開HidUsb設備成功......!"); 350 351 } 352 353 //寫方式打開設備 354 m_hUSBWrite=CreateFile(m_strDevicePath, 355 GENERIC_WRITE, 356 FILE_SHARE_READ|FILE_SHARE_WRITE, 357 (LPSECURITY_ATTRIBUTES)NULL, 358 OPEN_EXISTING, 359 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 360 NULL); 361 362 if (INVALID_HANDLE_VALUE==m_hUSBWrite) 363 { 364 TRACE("寫訪問打開HidUsb設備失敗......!"); 365 } 366 else 367 { 368 TRACE("寫訪問打開HidUsb設備成功......!"); 369 370 } 371 372 if (m_hUSBRead == INVALID_HANDLE_VALUE 373 &&m_hUSBWrite == INVALID_HANDLE_VALUE) 374 { 375 return FALSE; 376 } 377 378 if (!CreateThreadAndEvent()) 379 { 380 return FALSE; 381 } 382 383 m_bInit=TRUE; 384 385 m_unRecvMsg=unRecvMsg; 386 m_unConnectMsg=unConnectMsg; 387 388 RegisterHIDDevice(pPortOwner,HidGuid); 389 390 return TRUE; 391 } 392 } 393 //如果打開失敗,則查找下一個設備 394 else continue; 395 } 396 397 398 //調用SetupDiDestroyDeviceInfoList函數銷燬設備信息集合 399 SetupDiDestroyDeviceInfoList(hDevInfoSet); 400 401 return FALSE; 402 403 } 404 UINT CHIDUSB::Send(UCHAR *pSendBytes,UINT unSendLen) 405 { 406 if (NULL == pSendBytes ||0==unSendLen) 407 { 408 return 0; 409 } 410 411 if (m_hUSBWrite==INVALID_HANDLE_VALUE \ 412 ||m_hUSBWrite==NULL) 413 { 414 return 0; 415 } 416 417 UCHAR szSendBuf[65] ={0}; 418 DWORD dwSendBytes=0; 419 INT rt=0; 420 421 //HID發送報告第一個字節必須爲0 422 //所以發送總長度爲0x41=65 423 424 memcpy(&szSendBuf[1],pSendBytes,64); 425 426 rt=WriteFile(m_hUSBWrite, 427 szSendBuf, 428 m_Capabilities.OutputReportByteLength, 429 NULL, 430 &m_ovUSBWrite); 431 432 WaitForSingleObject(m_ovUSBWrite.hEvent,3000); 433 ResetEvent (m_ovUSBWrite.hEvent); 434 435 GetOverlappedResult(m_hUSBWrite,&m_ovUSBWrite,&dwSendBytes,TRUE); 436 437 return (UINT)(dwSendBytes-1); 438 } 439 440 UINT CHIDUSB::Recv(UCHAR *pRecvBytes) 441 { 442 if (NULL == pRecvBytes || 0==m_unRecvLength) 443 { 444 return 0; 445 } 446 447 if (m_hUSBRead==INVALID_HANDLE_VALUE \ 448 ||m_hUSBRead==NULL) 449 { 450 return 0; 451 } 452 453 UINT unRecvLength=m_unRecvLength; 454 455 m_unRecvLength=0; 456 457 memcpy(pRecvBytes,m_szUSBRecvBuf,64); 458 459 return unRecvLength; 460 } 461 462 DWORD CHIDUSB::RecvThread(LPVOID lpArg) 463 { 464 assert(NULL != lpArg); 465 466 CHIDUSB *pArg=(CHIDUSB *)lpArg; 467 468 assert(NULL != pArg); 469 470 UCHAR szRecvBuf[65]={0}; 471 DWORD dwRecvBytes=0; 472 473 while(1) 474 { 475 if (WaitForSingleObject(pArg->m_hRecvExitEvent,0)==WAIT_OBJECT_0) 476 { 477 break; //線程退出 478 } 479 480 if (pArg->Ready()) 481 { 482 memset(pArg->m_szUSBRecvBuf,0,sizeof(pArg->m_szUSBRecvBuf)); 483 memset(szRecvBuf,0,sizeof(szRecvBuf)); 484 485 ReadFile(pArg->m_hUSBRead, 486 szRecvBuf, 487 pArg->m_Capabilities.InputReportByteLength, 488 NULL, 489 &pArg->m_ovUSBRead 490 ); 491 492 WaitForSingleObject(pArg->m_ovUSBRead.hEvent,INFINITE); 493 ResetEvent(pArg->m_ovUSBRead.hEvent); 494 //通過GetOverlappedResult函數來獲取實際讀取到的字節數。 495 GetOverlappedResult(pArg->m_hUSBRead,&pArg->m_ovUSBRead,&dwRecvBytes,TRUE); 496 497 if (dwRecvBytes) 498 { 499 memcpy(pArg->m_szUSBRecvBuf,&szRecvBuf[1],64); 500 pArg->m_unRecvLength=(UINT)(dwRecvBytes-1);//默認返回65個字節,所以要減1 501 dwRecvBytes=0; 502 //完成這個消息才進行下個操作,因而不需要加上同步事件。 503 //如果使用PostMessage()就需要加上同步事件 504 ::SendMessage((pArg->m_pOwner)->m_hWnd, 505 pArg->m_unRecvMsg, 506 0, 507 0); 508 509 } 510 511 } 512 Sleep(1); 513 } 514 515 Sleep(10); 516 517 return 0; 518 } 519 520 void CHIDUSB::GetDevicePath(CString &str) 521 { 522 str=m_strDevicePath; 523 } 524 525 void CHIDUSB::RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid) 526 { 527 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface; 528 HDEVNOTIFY DeviceNotificationHandle; 529 530 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface); 531 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 532 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid; 533 534 DeviceNotificationHandle = 535 RegisterDeviceNotification(pPortOwner->m_hWnd, &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE); 536 } 537 538
複製代碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章