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
复制代码

 

你可能感兴趣的:(USB HID 类的编写)