如何使用 WinUSB 与 USB 设备 通信设备使用年限

2249人阅读
嵌入式开发(6)
&&&&&&& 现在正在做一个项目,需要做一个Linux USB gadget驱动,以实现模拟串口的功能,和windows进行简单的数据传递。
&&&&&&& Linux端是USB的device端,gadget驱动提供的一种串口设备。这个驱动在linux端提供一个tty设备/dev/ttyGS0,用于数据收发。由于使用的硬件限制,只能提供2个端点,所以不能使用cdc acm这种标准的,windows已经支持的串口设备。在这个项目中,使用generic serial(drivers/usb/f_serial.c)作为device端的gadget驱动。LInux端gadget驱动的编译和使用网上有很多教程,这里就不多介绍了。
&&&&&&& 这里说一下,当Linux的gadget使用generic serila作为串口驱动时,windows端的驱动如何配置,以及windows如何操作才能与device进行通信。
&&&&&&& 在windows侧,需要安装WinUSB驱动,这个驱动一个USB通用驱动,只能提供一些基本的USB通信接口,而不能实现任何功能性的驱动。简单来说,WinUSB提供一套用户态的接口,用于实现对USB设备的配置、管理、读写等操作,也就是说在用户态实现内核态的驱动功能,但有许多限制。这里有一个微软对WinUSB的介绍:
&&&&&&& /zh-cn/library/windows/hardware/ff540196.aspx
中文版的,翻译一般般,有一些专有名词翻的太烂了,比如终结点就是endpoint,一般译作端点。
&&&&&&& WinUSB使用一般需要提供一个inf文件,使windows将插入的USB设备驱动设置为WinUSB。这个文档详细介绍了WinUSB的安装及如何编程使用WinUSB提供的API接口:
&&&&&&& /download/9/C/5/9C5BBAE-9FDE-D599BAC8184A/WinUsb_HowTo.docx
这里附一个测试过的INF文件备查,不要从上面的网页链接查看如何安装winusb,链接里的inf文件在XP下有问题,不能直接用。
; WinUSB Devices Driver
; Copyright (c) 2009, XXX company.
Signature = &$Windows NT$&
Class = USB
ClassGuid={36fc9e60-c465-11cf-40000}
Provider = %ProviderName%
DriverPackageDisplayName = %PackageDisplayName%
;CatalogFile=wudf.cat
CatalogFile.NTx86
= senx86.cat
CatalogFile.NTIA64 = senia64.cat
CatalogFile.NTAMD64 = senamd64.cat
DriverVer=09/01/.0.0
; ================== Class section ==================
[ClassInstall32]
Addreg=SecureStorageDeviceClassReg
[SecureStorageDeviceClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-1
; ========== Manufacturer/Models sections ===========
[Manufacturer]
%ProviderName% = MyDevice_WinUSB,NTx86,NTamd64,NTia64
[MyDevice_WinUSB.NTx86]
%USB\MyDevice.DeviceDesc% =USB_Install, USB\VID_1d6b&PID_0104&MI_02
[MyDevice_WinUSB.NTamd64]
%USB\MyDevice.DeviceDesc% =USB_Install, USB\VID_1d6b&PID_0104&MI_02
[MyDevice_WinUSB.NTia64]
%USB\MyDevice.DeviceDesc% =USB_Install, USB\VID_1d6b&PID_0104&MI_02
; =================== Installation ===================
[USB_Install]
Include=winusb.inf
Needs=WINUSB.NT
[USB_Install.Services]
Include=winusb.inf
AddService=WinUSB,0x,WinUSB_ServiceInstall
[WinUSB_ServiceInstall]
DisplayName
= %WinUSB_SvcDesc%
ServiceType
ErrorControl
ServiceBinary
= %12%\WinUSB.sys
[USB_Install.Wdf]
KmdfService=WINUSB, WinUsb_Install
[WinUSB_Install]
KmdfLibraryVersion=1.9
[USB_Install.HW]
AddReg=Dev_AddReg
[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,&{9f543223-cede-4fa3-b376-a25ce9a30e74}&
;HKR,,&SystemWakeEnabled&,0x
HKR,,&DeviceIdleEnabled&,0x
HKR,,&DeviceIdleIgnoreWakeEnable&,0x
HKR,,&UserSetDeviceIdleEnabled&,0x
HKR,,&DefaultIdleState&,0xx1
HKR,,&DefaultIdleTimeout&,0x
[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles
[CoInstallers_AddReg]
HKR,,CoInstallers32,0x,&WinUSBCoInstaller2.dll&,&WdfCoInstaller01009.dll,WdfCoInstaller&
[CoInstallers_CopyFiles]
WdfCoInstaller01009.dll
WinUSBCoInstaller2.dll
[DestinationDirs]
CoInstallers_CopyFiles=11
; ================= Source Media Section =====================
[SourceDisksNames]
1 = %DISK_NAME%,,,\x86
2 = %DISK_NAME%,,,\amd64
3 = %DISK_NAME%,,,\ia64
[SourceDisksFiles.x86]
WdfCoInstaller01009.dll=1
WinUSBCoInstaller2.dll=1
[SourceDisksFiles.amd64]
WdfCoInstaller01009.dll=2
WinUSBCoInstaller2.dll=2
[SourceDisksFiles.ia64]
WdfCoInstaller01009.dll=3
WinUSBCoInstaller2.dll=3
; =================== Strings ===================
ProviderName=&MyDevice&
PackageDisplayName=&Device Driver&
USB\MyDevice.DeviceDesc=&Device&
WinUSB_SvcDesc=&USB Service&
DISK_NAME=&USB Device Install Disk&
ClassName=&USB Serial Devices&&&&&&&& inf文件中指定的两个辅助安装程序WinUSBCoInstaller2.dll和WdfCoInstaller01009.dll可以在WDK安装目录中找到。如果用的是WDK7.1版本,那么这两个文件在C:\WinDDK\.1\redist下对应目录中;如果是WDK8版本,则在C:\Program Files\Windows Kits\8.0\redist\wdf下。将上面的inf文件和三个不同cpu类型目录放在同一个目录下,目录名参照[SourceDisksNames]字段,分别是x86、amd64和ia64。每个目录下分别放WinUSBCoInstaller2.dll和WdfCoInstaller01009.dll这两个文件的不同版本。
&&&&&&& 在设备管理器中更新驱动程序,并指定驱动安装路径,就可以安装WinUSB驱动了。更细节的内容请参考上面的问题,英文不好的去看网页链接把。
&&&&&&& 这里有一篇MSDN的简单介绍:/zh-cn/library/windows/hardware/ff540174.aspx,更详细的还是要看文档。下面我把代码单独摘抄出来,并加一些注释
// SerialIOTest.cpp : 定义控制台应用程序的入口点。
#include &stdafx.h&
// Include Windows headers
#include &windows.h&
#include &stdio.h&
#include &tchar.h&
#include &strsafe.h&
// Include WinUSB headers
#include &winusb.h&
#include &Usb100.h&
#include &Setupapi.h&
#pragma comment (lib , &setupapi.lib& )
#pragma comment (lib , &winusb.lib& )
//{9f543223-cede-4fa3-b376-a25ce9a30e74},这个GUID是INF注册的,见[Dev_AddReg]字段,通过GUID来访问设备节点
static const GUID OSR_DEVICE_INTERFACE =
{ 0x9fxcede, 0x4fa3, { 0xb3, 0x76, 0xa2, 0x5c, 0xe9, 0xa3, 0x0e, 0x74 } };
//创建 USB 设备的文件句柄
BOOL GetDeviceHandle (GUID guidDeviceInterface, PHANDLE hDeviceHandle)
if (guidDeviceInterface==GUID_NULL)
return FALSE;
BOOL bResult = TRUE;
HDEVINFO hDeviceI
SP_DEVINFO_DATA DeviceInfoD
SP_DEVICE_INTERFACE_DATA deviceInterfaceD
PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL;
ULONG requiredLength=0;
LPTSTR lpDevicePath = NULL;
DWORD index = 0;
//获取指定GUID的所有设备的信息,可能会有多个设备,且都使用WinUSB驱动
hDeviceInfo = SetupDiGetClassDevs(
&guidDeviceInterface,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfo == INVALID_HANDLE_VALUE)
printf(&Error SetupDiGetClassDevs: %d.\n&, GetLastError());
//遍历所有设备的接口
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (index = 0; SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); index++)
//Reset for this iteration
if (lpDevicePath)
LocalFree(lpDevicePath);
if (pInterfaceDetailData)
LocalFree(pInterfaceDetailData);
deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
//获取设备接口信息
bResult = SetupDiEnumDeviceInterfaces(
hDeviceInfo,
&DeviceInfoData,
&guidDeviceInterface,
&deviceInterfaceData);
//检查设备遍历是否完成
if (GetLastError () == ERROR_NO_MORE_ITEMS)
//其他错误
if (!bResult)
printf(&Error SetupDiEnumDeviceInterfaces: %d.\n&, GetLastError());
//返回的接口数据在SP_DEVICE_INTERFACE_DETAIL_DATA中,但不知道它的长度,
//所以需要调用两次SetupDiGetDeviceInterfaceDetail()函数,第一次获取长度,
//第二次才是真正调用,填充结构体内容
bResult = SetupDiGetDeviceInterfaceDetail(
hDeviceInfo,
&deviceInterfaceData,
&requiredLength,
//这里一定会失败,因为SetupDiGetDeviceInterfaceDetail()的第三个参数为NULL
if (!bResult)
//如果是由于参数为NULL,且获得了正确的接口信息长度,那么申请内存。
if ((ERROR_INSUFFICIENT_BUFFER==GetLastError()) && (requiredLength&0))
//为pInterfaceDetailData申请内存,长度是上面返回的requiredLength
pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength);
if (!pInterfaceDetailData)
printf(&Error allocating memory for the device detail buffer.\n&);
printf(&Error SetupDiEnumDeviceInterfaces: %d.\n&, GetLastError());
//get the interface detailed data
pInterfaceDetailData-&cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//第二次调用,使用正确的长度,和alloc好的pInterfaceDetailData
bResult = SetupDiGetDeviceInterfaceDetail(
hDeviceInfo,
&deviceInterfaceData,
pInterfaceDetailData,
requiredLength,
&DeviceInfoData);
//Check for some other error
if (!bResult)
printf(&Error SetupDiGetDeviceInterfaceDetail: %d.\n&, GetLastError());
//获取设备路径
size_t nLength = wcslen (pInterfaceDetailData-&DevicePath) + 1;
lpDevicePath = (TCHAR *) LocalAlloc (LPTR, nLength * sizeof(TCHAR));
StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData-&DevicePath);
lpDevicePath[nLength-1] = 0;
printf(&Device path:
%s\n&, lpDevicePath);
if (!lpDevicePath)
printf(&Error %d.&, GetLastError());
//打开设备
*hDeviceHandle = CreateFile (
lpDevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
if (*hDeviceHandle == INVALID_HANDLE_VALUE)
printf(&Error %d.&, GetLastError());
LocalFree(lpDevicePath);
LocalFree(pInterfaceDetailData);
bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo);
//初始化WinUSB,并获取设备的 WinUSB 接口句柄
BOOL GetWinUSBHandle(HANDLE hDeviceHandle, PWINUSB_INTERFACE_HANDLE phWinUSBHandle)
if (hDeviceHandle == INVALID_HANDLE_VALUE)
return FALSE;
BOOL bResult = WinUsb_Initialize(hDeviceHandle, phWinUSBHandle);
if(!bResult)
printf(&WinUsb_Initialize Error %d.&, GetLastError());
return FALSE;
//查询USB设备描述符--这个函数仅获取speed
BOOL GetUSBDeviceSpeed(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pDeviceSpeed)
if (!pDeviceSpeed || hDeviceHandle==INVALID_HANDLE_VALUE)
return FALSE;
BOOL bResult = TRUE;
ULONG length = sizeof(UCHAR);
bResult = WinUsb_QueryDeviceInformation(hDeviceHandle, DEVICE_SPEED, &length, pDeviceSpeed);
if(!bResult)
printf(&Error getting device speed: %d.\n&, GetLastError());
if(*pDeviceSpeed == LowSpeed)
printf(&Device speed: %d (Low speed).\n&, *pDeviceSpeed);
if(*pDeviceSpeed == FullSpeed)
printf(&Device speed: %d (Full speed).\n&, *pDeviceSpeed);
if(*pDeviceSpeed == HighSpeed)
printf(&Device speed: %d (High speed).\n&, *pDeviceSpeed);
struct PIPE_ID
PipeOutId;
//查询接口和端点信息
BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE_ID* pipeid)
if (hDeviceHandle==INVALID_HANDLE_VALUE)
return FALSE;
BOOL bResult = TRUE;
USB_INTERFACE_DESCRIPTOR InterfaceD
ZeroMemory(&InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR));
WINUSB_PIPE_INFORMATION
ZeroMemory(&Pipe, sizeof(WINUSB_PIPE_INFORMATION));
//遍历设备接口
bResult = WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &InterfaceDescriptor);
if (bResult)
//获取该接口的端点数
for (int index = 0; index & InterfaceDescriptor.bNumE index++)
//查询该端点对应的PIPE信息,这个函数可以获取PIPE的类型、ID、最大包大小等
bResult = WinUsb_QueryPipe(hDeviceHandle, 0, index, &Pipe);
if (bResult)
if (Pipe.PipeType == UsbdPipeTypeControl)
printf(&Endpoint index: %d Pipe type: Control Pipe ID: %d.\n&, index, Pipe.PipeType, Pipe.PipeId);
if (Pipe.PipeType == UsbdPipeTypeIsochronous)
printf(&Endpoint index: %d Pipe type: Isochronous Pipe ID: %d.\n&, index, Pipe.PipeType, Pipe.PipeId);
if (Pipe.PipeType == UsbdPipeTypeBulk)
if (USB_ENDPOINT_DIRECTION_IN(Pipe.PipeId))
printf(&Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n&, index, Pipe.PipeType, Pipe.PipeId);
pipeid-&PipeInId = Pipe.PipeId;
if (USB_ENDPOINT_DIRECTION_OUT(Pipe.PipeId))
printf(&Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n&, index, Pipe.PipeType, Pipe.PipeId);
pipeid-&PipeOutId = Pipe.PipeId;
if (Pipe.PipeType == UsbdPipeTypeInterrupt)
printf(&Endpoint index: %d Pipe type: Interrupt Pipe ID: %d.\n&, index, Pipe.PipeType, Pipe.PipeId);
//发送写入请求
BOOL WriteToBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten)
if (hDeviceHandle==INVALID_HANDLE_VALUE || !pID || !pcbWritten)
return FALSE;
BOOL bResult = TRUE;
UCHAR szBuffer[] = &Hello World&;
ULONG cbSize = strlen((const char*)szBuffer);
ULONG cbSent = 0;
//真正的WinUSB PIPE写函数
bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0);
if(!bResult)
printf(&Wrote to pipe %d: %s \nActual data transferred: %d.\n&, *pID, szBuffer, cbSent);
*pcbWritten = cbS
//发送读取请求
BOOL ReadFromBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG cbSize)
if (hDeviceHandle==INVALID_HANDLE_VALUE)
return FALSE;
BOOL bResult = TRUE;
UCHAR* szBuffer = (UCHAR*)LocalAlloc(LPTR, sizeof(UCHAR)*cbSize);
ULONG cbRead = 0;
//真正的WinUSB PIPE读函数
bResult = WinUsb_ReadPipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbRead, 0);
if(!bResult)
printf(&Read from pipe %d: %s \nActual data read: %d.\n&, *pID, szBuffer, cbRead);
LocalFree(szBuffer);
int _tmain(int argc, _TCHAR* argv[])
GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; //in the INF file
BOOL bResult = TRUE;
PIPE_ID PipeID;
HANDLE hDeviceHandle = INVALID_HANDLE_VALUE;
WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE;
UCHAR DeviceS
ULONG cbSize = 0;
//获取guid指定的USB设备handle,其实是获取guid指定的所有设备中最后一个的handle
bResult = GetDeviceHandle(guidDeviceInterface, &hDeviceHandle);
if(!bResult)
//获取WinUSB句柄
bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle);
if(!bResult)
//获取USB设备速度
bResult = GetUSBDeviceSpeed(hWinUSBHandle, &DeviceSpeed);
if(!bResult)
//获取端点地址
bResult = QueryDeviceEndpoints(hWinUSBHandle, &PipeID);
if(!bResult)
//发送Hello World
bResult = WriteToBulkEndpoint(hWinUSBHandle, &PipeID.PipeOutId, &cbSize);
if(!bResult)
//接收数据,接收缓存区为1024大小
bResult = ReadFromBulkEndpoint(hWinUSBHandle, &PipeID.PipeInId, 1024);
if(!bResult)
system(&PAUSE&);
&CloseHandle(hDeviceHandle);
WinUsb_Free(hWinUSBHandle);
编译一下,执行。这个程序仅仅是个简单的示例,收发都在同一个线程里,而且是先发后收,收到任何一个包就会结束。所以,不用想这是一个类似聊天室的小应用了。
【待补充linux kernel模块编译及使用】
&&&&&&& Linux这边只要执行cat /dev/ttyGS0,即可看到windows发送过来的“Hello World”。发送数据到windows,可以执行echo && & /dev/ttyGS0。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:47009次
排名:千里之外
原创:18篇
评论:13条
(1)(1)(2)(1)(1)(2)(1)(1)(1)(1)(1)(1)(2)(3)

我要回帖

更多关于 通信设备使用年限 的文章

 

随机推荐