1 前言USB開發跟其他檔案裝置(如串口)開發一樣,痛點是找到該USB裝置的路徑,本文以DDK裡src/usb/bulkusb例子為參考,闡述一個非HID的USB調試器軟體的開發過程。
2 裝置GUID一般裝置會有兩個GUID, 一個為Class GUID, 在INF檔案中,另一個為Device GUID,在SYS檔案中。 CreateFile使用的是SYS中的GUID,想得到它有兩個辦法: 1 跟裝置的提供者索要。 2在註冊表裡找,一般在:HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Control/DeviceClasses/
3 裝置路徑根據裝置GUID,枚舉所有USB HOST,找匹配的USB裝置,然後擷取其路徑。
3.1 標頭檔 usbport.h#define WINVER 0x0500#include <windows.h>#include <setupapi.h>#include <basetyps.h>#include <usbdi.h>#include <initguid.h>#include <stdio.h> #pragma comment(lib,"setupapi.lib")#pragma comment(lib,"hid.lib")#pragma comment(lib,"comctl32.lib") #ifndef BULKUSBH_INC#define BULKUSBH_INC #define BULKUSB_IOCTL_INDEX 0x0000 #define IOCTL_BULKUSB_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN, / BULKUSB_IOCTL_INDEX,/ METHOD_BUFFERED, / FILE_ANY_ACCESS) #define IOCTL_BULKUSB_RESET_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, / BULKUSB_IOCTL_INDEX+1,/ METHOD_BUFFERED, / FILE_ANY_ACCESS) #define IOCTL_BULKUSB_RESET_PIPE CTL_CODE(FILE_DEVICE_UNKNOWN, / BULKUSB_IOCTL_INDEX+2,/ METHOD_BUFFERED, / FILE_ANY_ACCESS) extern HANDLE open_file(char *filename);extern int GetUsbPath(char *path);extern int WriteUsb(HANDLE hUsb,char *Outbuff, int len);extern int ReadUsb(HANDLE hUsb,BYTE inbuff[],DWORD &nBytesRead,int nToRead);
3.2 源檔案 usbport.cpp#include "usbport.h" //8a3bf75d-83c7-440e-8276-5ae3f3ea6e77DEFINE_GUID(GUID_CLASS_I82930_BULK,0x8a3bf75d, 0x83c7, 0x440e,0x82, 0x76, 0x5a, 0xe3, 0xf3, 0xea, 0x6e, 0x77); BOOL GetUsbDeviceFileName( LPGUID pGuid, char *outNameBuf);HANDLE OpenUsbDevice( LPGUID pGuid, char *outNameBuf);HANDLE OpenOneDevice (HDEVINFO HardwareDeviceInfo, PSP_INTERFACE_DEVICE_DATA DeviceInfoData,char *devName);int GetUsbPath(char *path);int WriteUsb(HANDLE hUsb,char *Outbuff, int len);int ReadUsb(HANDLE hUsb,BYTE inbuff[], DWORD &nBytesRead,int nToRead); /*名稱:open_file 功能:開啟USB裝置 參數:filename 定義為”PIPE00” pipe name for bulk input pipe on our test board ,”PIPE01” pipe name for bulk output pipe on our test board。PIPE00 和 PIPE01 是參考src/usb/bulkusb,我實際在用時這兩個效果一樣,為了避免USB異常引起的死機,我檔案開啟採用非阻塞模式。*/HANDLE open_file( char *filename){ int success = 1; HANDLE h; char completeDeviceName[256] = ""; //generated from the GUID registered by the driver itself if ( !GetUsbDeviceFileName((LPGUID) &GUID_CLASS_I82930_BULK,completeDeviceName) ) { //NOISY(("Failed to GetUsbDeviceFileName/n", GetLastError())); return INVALID_HANDLE_VALUE; } strcat (completeDeviceName, "//"); strcat (completeDeviceName, filename); //printf("completeDeviceName = (%s)/n", completeDeviceName); h = CreateFile(completeDeviceName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (h == INVALID_HANDLE_VALUE) { //NOISY(("Failed to open (%s) = %d", completeDeviceName, GetLastError())); success = 0; } else { //NOISY(("Opened successfully./n")); } return h;}/*名稱:GetUsbDeviceFileName 功能:擷取USB裝置路徑 參數:pGUID 返回:outNameBuf USB裝置路徑*/BOOL GetUsbDeviceFileName( LPGUID pGuid, char *outNameBuf){ HANDLE hDev = OpenUsbDevice( pGuid, outNameBuf ); if ( hDev != INVALID_HANDLE_VALUE ) { CloseHandle( hDev ); return TRUE; } return FALSE; }/*名稱:OpenUsbDevice 功能:擷取USB裝置路徑 參數:pGUID 裝置GUID 返回:outNameBuf USB裝置路徑*/HANDLE OpenUsbDevice( LPGUID pGuid, char *outNameBuf){ ULONG NumberDevices; HANDLE hOut = INVALID_HANDLE_VALUE; HDEVINFO hardwareDeviceInfo; SP_INTERFACE_DEVICE_DATA deviceInfoData; ULONG i; BOOLEAN done; PUSB_DEVICE_DESCRIPTOR usbDeviceInst; PUSB_DEVICE_DESCRIPTOR *UsbDevices = &usbDeviceInst; *UsbDevices = NULL; NumberDevices = 0; // // Open a handle to the plug and play dev node. // SetupDiGetClassDevs() returns a device information set that contains info on all // installed devices of a specified class. // hardwareDeviceInfo = SetupDiGetClassDevs ( pGuid, NULL, // Define no enumerator (global) NULL, // Define no (DIGCF_PRESENT | // Only Devices present DIGCF_INTERFACEDEVICE)); // Function class devices. // // Take a wild guess at the number of devices we have; // Be prepared to realloc and retry if there are more than we guessed // NumberDevices = 4; done = FALSE; deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA); i=0; while (!done) { NumberDevices *= 2; if (*UsbDevices) { *UsbDevices = (PUSB_DEVICE_DESCRIPTOR)realloc (*UsbDevices, (NumberDevices * sizeof (USB_DEVICE_DESCRIPTOR))); } else { *UsbDevices = (PUSB_DEVICE_DESCRIPTOR)calloc (NumberDevices, sizeof (USB_DEVICE_DESCRIPTOR)); } if (NULL == *UsbDevices) { // SetupDiDestroyDeviceInfoList destroys a device information set // and frees all associated memory. SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); return INVALID_HANDLE_VALUE; } usbDeviceInst = *UsbDevices + i; for (; i < NumberDevices; i++) { // SetupDiEnumDeviceInterfaces() returns information about device interfaces // exposed by one or more devices. Each call returns information about one interface; // the routine can be called repeatedly to get information about several interfaces // exposed by one or more devices. if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, 0, // We don't care about specific PDOs pGuid, i, &deviceInfoData)) { hOut = OpenOneDevice (hardwareDeviceInfo, &deviceInfoData, outNameBuf); if ( hOut != INVALID_HANDLE_VALUE ) { done = TRUE; break; } } else { if (ERROR_NO_MORE_ITEMS == GetLastError()) { done = TRUE; break; } } } } NumberDevices = i; // SetupDiDestroyDeviceInfoList() destroys a device information set // and frees all associated memory. SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); free ( *UsbDevices ); return hOut;}HANDLE OpenOneDevice ( IN HDEVINFO HardwareDeviceInfo, IN PSP_INTERFACE_DEVICE_DATA DeviceInfoData, IN char *devName ){ PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL; ULONG predictedLength = 0; ULONG requiredLength = 0; HANDLE hOut = INVALID_HANDLE_VALUE; // // allocate a function class device data structure to receive the // goods about this particular device. // SetupDiGetInterfaceDeviceDetail ( HardwareDeviceInfo, DeviceInfoData, NULL, // probing so no output buffer yet 0, // probing so output buffer length of zero &requiredLength, NULL); // not interested in the specific dev-node predictedLength = requiredLength; // sizeof (SP_FNCLASS_DEVICE_DATA) + 512; functionClassDeviceData = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc (predictedLength); functionClassDeviceData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA); // // Retrieve the information from Plug and Play. // if (! SetupDiGetInterfaceDeviceDetail ( HardwareDeviceInfo, DeviceInfoData, functionClassDeviceData, predictedLength, &requiredLength, NULL)) { free( functionClassDeviceData ); return INVALID_HANDLE_VALUE; } strcpy( devName,functionClassDeviceData->DevicePath) ; //printf( "Attempting to open %s/n", devName ); hOut = CreateFile ( functionClassDeviceData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, // no SECURITY_ATTRIBUTES structure OPEN_EXISTING, // No special create flags 0, // No special attributes NULL); // No template file if (INVALID_HANDLE_VALUE == hOut) { //printf( "FAILED to open %s/n", devName ); } free( functionClassDeviceData ); return hOut;}/*名稱:GetUsbPath 功能:返回USB裝置路徑 參數:pGUID 返回:path 路徑*/int GetUsbPath(char *path){ if ( !GetUsbDeviceFileName((LPGUID) &GUID_CLASS_I82930_BULK,path) ) { return 0; } return 1;}/*名稱:WriteUsb 功能:向USB寫資料 參數:hUsb USB控制代碼,Outbut 資料指標,len 資料長度*/int WriteUsb(HANDLE hUsb,char *Outbuff, int len){ DWORD nBytesWrite,endtime,lrc; static OVERLAPPED ol; DWORD dwErrorMask,dwError; COMSTAT comstat; if(hUsb==NULL) return 0; ol.Offset=0; //裝置使用指定0 ol.OffsetHigh=0; //裝置使用指定0 ol.hEvent=NULL; //標識事件為非訊號狀態,資料傳送完成時,它將被設為訊號狀態 ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!WriteFile(hUsb,Outbuff,len,&nBytesWrite,&ol)) { //出錯資訊處理-------------------------------- if((lrc=GetLastError())==ERROR_IO_PENDING) { endtime=GetTickCount()+1000; while(!GetOverlappedResult(hUsb,&ol,&nBytesWrite,FALSE)) { dwError=GetLastError(); if(GetTickCount()>endtime) { MessageBox(NULL,"寫串口時間過長,目前串口發送緩衝區中的資料數目為空白",NULL,NULL); break; } if(dwError=ERROR_IO_INCOMPLETE) continue; //未完全讀完時的正常返回結果 else { // 發生錯誤,嘗試恢複! break; } } } //-------------------------------------------------// } CloseHandle(ol.hEvent); FlushFileBuffers(hUsb); return 1;}/*名稱:ReadUsb 功能:讀取USB裝置發來的資料 參數:hUsb USB控制代碼,nToRead讀取的長度 返回:inbuff 讀到的資料,nBytesRead 讀到的長度*/int ReadUsb(HANDLE hUsb,BYTE inbuff[], DWORD &nBytesRead,int nToRead){ DWORD lrc; ///縱向冗餘校正 DWORD endtime; /////////jiesuo static OVERLAPPED ol; int ReadNumber=0; int numCount=0 ; //控制讀取的數目 DWORD dwErrorMask; DWORD dwEvtMask=0 ; DWORD ntoread; int ReadTime; ReadTime=2000; ntoread=nToRead; ol.Offset=0; ///相對檔案開始的位元組位移量 ol.OffsetHigh=0; ///開始傳送資料的位元組位移量的高位字,管道和通訊時調用進程可忽略。 ol.hEvent=NULL; ///標識事件,資料傳送完成時設為訊號狀態 ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!ReadFile(hUsb,inbuff,ntoread,&nBytesRead,&ol)) { if((lrc=GetLastError())==ERROR_IO_PENDING) { /////////////////// endtime=GetTickCount()+ReadTime;//GetTickCount()取回系統開始至此所用的時間(毫秒) while(!GetOverlappedResult(hUsb,&ol,&nBytesRead,FALSE))//該函數取回重疊操作的結果 { if(GetTickCount()>endtime) break; } } } CloseHandle(ol.hEvent); return 1; }
4 編譯前提開發USB程式,需要下載Microsotft的DDK(DRIVER DEVELOP KIT)庫,該庫的配置請參考http://blog.csdn.net/skdev/archive/2007/01/26/1494635.aspx
5 效果