WinUSB安装以及与Linux通讯
现在正在做一个项目,需要做一个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的介绍:
http://msdn.microsoft.com/zh-cn/library/windows/hardware/ff540196.aspx
中文版的,翻译一般般,有一些专有名词翻的太烂了,比如终结点就是endpoint,一般译作端点。
WinUSB使用一般需要提供一个inf文件,使windows将插入的USB设备驱动设置为WinUSB。这个文档详细介绍了WinUSB的安装及如何编程使用WinUSB提供的API接口:
http://download.microsoft.com/download/9/C/5/9C5B2167-8017-4BAE-9FDE-D599BAC8184A/WinUsb_HowTo.docx
这里附一个测试过的INF文件备查,不要从上面的网页链接查看如何安装winusb,链接里的inf文件在XP下有问题,不能直接用。
; WinUSB Devices Driver; Copyright (c) 2009, XXX company.[Version]
Signature = "$Windows NT$"
Class = USB
ClassGuid={36fc9e60-c465-11cf-8056-444553540000}
Provider = %ProviderName%
DriverPackageDisplayName = %PackageDisplayName%
;CatalogFile=wudf.cat
CatalogFile.NTx86 = senx86.cat
CatalogFile.NTIA64 = senia64.cat
CatalogFile.NTAMD64 = senamd64.cat
DriverVer=09/01/2009,1.0.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 ===================
;[1]
[USB_Install]
Include=winusb.inf
Needs=WINUSB.NT;[2]
[USB_Install.Services]
Include=winusb.inf
AddService=WinUSB,0x00000002,WinUSB_ServiceInstall;[3]
[WinUSB_ServiceInstall]
DisplayName = %WinUSB_SvcDesc%
ServiceType = 1
StartType = 3
ErrorControl = 1
ServiceBinary = %12%\WinUSB.sys;[4]
[USB_Install.Wdf]
KmdfService=WINUSB, WinUsb_Install[WinUSB_Install]
KmdfLibraryVersion=1.9;[5]
[USB_Install.HW]
AddReg=Dev_AddReg[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{9f543223-cede-4fa3-b376-a25ce9a30e74}"
;HKR,,"SystemWakeEnabled",0x00010001,1
HKR,,"DeviceIdleEnabled",0x00010001,1
HKR,,"DeviceIdleIgnoreWakeEnable",0x00010001,1
HKR,,"UserSetDeviceIdleEnabled",0x00010001,1
HKR,,"DefaultIdleState",0x00010001,0x1
HKR,,"DefaultIdleTimeout",0x00010001,500;[6]
[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WinUSBCoInstaller2.dll","WdfCoInstaller01009.dll,WdfCoInstaller"[CoInstallers_CopyFiles]
WdfCoInstaller01009.dll
WinUSBCoInstaller2.dll[DestinationDirs]
CoInstallers_CopyFiles=11; ================= Source Media Section =====================;[7]
[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 ===================[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\7600.16385.1\redist下对应目录中;如果是WDK8版本,则在C:\Program Files\Windows Kits\8.0\redist\wdf下。将上面的inf文件和三个不同cpu类型目录放在同一个目录下,目录名参照[SourceDisksNames]字段,分别是x86、amd64和ia64。每个目录下分别放WinUSBCoInstaller2.dll和WdfCoInstaller01009.dll这两个文件的不同版本。
在设备管理器中更新驱动程序,并指定驱动安装路径,就可以安装WinUSB驱动了。更细节的内容请参考上面的问题,英文不好的去看网页链接把。
这里有一篇MSDN的简单介绍:http://msdn.microsoft.com/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 =
{ 0x9f543223, 0xcede, 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 hDeviceInfo;SP_DEVINFO_DATA DeviceInfoData;SP_DEVICE_INTERFACE_DATA deviceInterfaceData;PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL;ULONG requiredLength=0;LPTSTR lpDevicePath = NULL;DWORD index = 0;//获取指定GUID的所有设备的信息,可能会有多个设备,且都使用WinUSB驱动hDeviceInfo = SetupDiGetClassDevs( &guidDeviceInterface,NULL, NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);if (hDeviceInfo == INVALID_HANDLE_VALUE) { // ERROR printf("Error SetupDiGetClassDevs: %d.\n", GetLastError());goto done;}//遍历所有设备的接口DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);for (index = 0; SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); index++){//Reset for this iterationif (lpDevicePath){LocalFree(lpDevicePath);}if (pInterfaceDetailData){LocalFree(pInterfaceDetailData);}deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);//获取设备接口信息bResult = SetupDiEnumDeviceInterfaces( hDeviceInfo,&DeviceInfoData,&guidDeviceInterface,0, &deviceInterfaceData);//检查设备遍历是否完成if (GetLastError () == ERROR_NO_MORE_ITEMS){break;}//其他错误if (!bResult) {printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError());goto done;}//返回的接口数据在SP_DEVICE_INTERFACE_DETAIL_DATA中,但不知道它的长度,//所以需要调用两次SetupDiGetDeviceInterfaceDetail()函数,第一次获取长度,//第二次才是真正调用,填充结构体内容bResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo,&deviceInterfaceData,NULL, 0,&requiredLength,NULL);//这里一定会失败,因为SetupDiGetDeviceInterfaceDetail()的第三个参数为NULLif (!bResult) {//如果是由于参数为NULL,且获得了正确的接口信息长度,那么申请内存。if ((ERROR_INSUFFICIENT_BUFFER==GetLastError()) && (requiredLength>0)){//为pInterfaceDetailData申请内存,长度是上面返回的requiredLengthpInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength);if (!pInterfaceDetailData) { // ERROR printf("Error allocating memory for the device detail buffer.\n");goto done;}}else{printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError());goto done;}}//get the interface detailed datapInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);//第二次调用,使用正确的长度,和alloc好的pInterfaceDetailDatabResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo,&deviceInterfaceData,pInterfaceDetailData,requiredLength,NULL,&DeviceInfoData);//Check for some other errorif (!bResult) {printf("Error SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError());goto done;}//获取设备路径 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){//Error.printf("Error %d.", GetLastError());goto done;}//打开设备*hDeviceHandle = CreateFile (lpDevicePath,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);if (*hDeviceHandle == INVALID_HANDLE_VALUE){//Error.printf("Error %d.", GetLastError());goto done;}done:LocalFree(lpDevicePath);LocalFree(pInterfaceDetailData); bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo);return bResult;
}//初始化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){//Error.printf("WinUsb_Initialize Error %d.", GetLastError());return FALSE;}return bResult;
}//查询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());goto done;}if(*pDeviceSpeed == LowSpeed){printf("Device speed: %d (Low speed).\n", *pDeviceSpeed);goto done;}if(*pDeviceSpeed == FullSpeed){printf("Device speed: %d (Full speed).\n", *pDeviceSpeed);goto done;}if(*pDeviceSpeed == HighSpeed){printf("Device speed: %d (High speed).\n", *pDeviceSpeed);goto done;}done:return bResult;
}struct PIPE_ID
{UCHAR PipeInId;UCHAR PipeOutId;
};//查询接口和端点信息
BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE_ID* pipeid)
{if (hDeviceHandle==INVALID_HANDLE_VALUE){return FALSE;}BOOL bResult = TRUE;USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;ZeroMemory(&InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR));WINUSB_PIPE_INFORMATION Pipe;ZeroMemory(&Pipe, sizeof(WINUSB_PIPE_INFORMATION));//遍历设备接口bResult = WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &InterfaceDescriptor);if (bResult){//获取该接口的端点数for (int index = 0; index < InterfaceDescriptor.bNumEndpoints; 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);}}else{continue;}}}done:return bResult;
}//发送写入请求
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){goto done;}printf("Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID, szBuffer, cbSent);*pcbWritten = cbSent;done:return bResult;}//发送读取请求
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){goto done;}printf("Read from pipe %d: %s \nActual data read: %d.\n", *pID, szBuffer, cbRead);done:LocalFree(szBuffer);return bResult;}int _tmain(int argc, _TCHAR* argv[])
{GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; //in the INF fileBOOL bResult = TRUE;PIPE_ID PipeID;HANDLE hDeviceHandle = INVALID_HANDLE_VALUE;WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE;UCHAR DeviceSpeed;ULONG cbSize = 0;//获取guid指定的USB设备handle,其实是获取guid指定的所有设备中最后一个的handlebResult = GetDeviceHandle(guidDeviceInterface, &hDeviceHandle);if(!bResult){goto done;}//获取WinUSB句柄bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle);if(!bResult){goto done;}//获取USB设备速度bResult = GetUSBDeviceSpeed(hWinUSBHandle, &DeviceSpeed);if(!bResult){goto done;}//获取端点地址bResult = QueryDeviceEndpoints(hWinUSBHandle, &PipeID);if(!bResult){goto done;}//发送Hello WorldbResult = WriteToBulkEndpoint(hWinUSBHandle, &PipeID.PipeOutId, &cbSize);if(!bResult){goto done;}//接收数据,接收缓存区为1024大小bResult = ReadFromBulkEndpoint(hWinUSBHandle, &PipeID.PipeInId, 1024);if(!bResult){goto done;}system("PAUSE");done://关闭CloseHandle(hDeviceHandle);WinUsb_Free(hWinUSBHandle);return 0;
}
编译一下,执行。这个程序仅仅是个简单的示例,收发都在同一个线程里,而且是先发后收,收到任何一个包就会结束。所以,不用想这是一个类似聊天室的小应用了。
【待补充linux kernel模块编译及使用】
Linux这边只要执行cat /dev/ttyGS0,即可看到windows发送过来的“Hello World”。发送数据到windows,可以执行echo "1234567890" > /dev/ttyGS0。
WinUSB安装以及与Linux通讯相关推荐
- 安装图解:Linux Mint 4.0(Daryna)(或者说完美的桌面系统)
http://www.pusuo.net/2009-08-04/110221670.html 安装图解:Linux Mint 4.0(Daryna)(或者说完美的桌面系统) 作者falko (Cont ...
- WinUsb实现USB免驱通讯技术总结
随着电脑更新换代.USB接口逐渐替代传统接口(串口等),为了更加方便与PC机进行通讯,引入USB通讯. 如何才能实现产品与PC又快又稳定通讯 如何解决串口通讯中接受不定时帧间隔问题 如何实现即插即用的 ...
- VirtualBox下安装rhel5.5 linux系统
以前也用过VMware server和VMware workstation虚拟机,现在使用了一段时间VirtualBox,感觉它比较轻巧,很适合我,在Win7系统下用起来很方便.下面详细介绍下在Vir ...
- virtualenv 安装与使用linux下(记录一下)
virtualenv 安装与使用linux下(记录一下) 1.通过 pip install virtualenv 安装 pip install virtualenv 1 2.查看是否成功安装 virt ...
- 多个linux发行版本混合安装盘,使用 MultiBootUSB 安装多个 Linux 版本
导读 我喜欢通过 U 盘尝试不同的 Linux 发行版.它让我可以在真实的硬件上测试操作系统,而不是虚拟化的环境中.此外,我可以将 USB 插入任何系统(比如 Windows 系统),做任何我想要的事 ...
- 如何在CentOS 5.x 中安装Windows Azure Linux Agent (WALA)
Qing Liu Tue, Mar 10 2015 3:06 AM 在今天的这一个章节中,我们主要讨论在CentOs 5.x 中如何安装Windows Azure Linux Agent 2.11 ...
- 在windows上的git bash中安装tree 和 linux tree命令使用
在windows上的git bash中安装tree 和 linux tree命令使用 文章目录: 1 在windows上的git bash中安装tree 1.1 下载windows版本的tree 1. ...
- m1mac安装linux,M1 Mac 能安装 Ubuntu 和 Linux 了 ??
原标题:M1 Mac 能安装 Ubuntu 和 Linux 了 ?? 作者:IT 之家.cnBeta 一.安装 Windows 10成功后:M1 Mac 运行 Ubuntu 也实现了 借助模拟器工具Q ...
- ubuntu amd 64bit 安装 QQ for linux教程(附 不能使用中文的解决办法)
ubuntu amd 64bit 安装 QQ for linux教程(附 不能使用中文的解决办法) Ubuntu 64bit 安装32bit软件 64bit系统构架的先进不由多讲,只是应用程序相对少了 ...
最新文章
- 密码技术--椭圆曲线算法EDCSA数字签名及Go语言应用
- 4.边缘光照的描边shader
- 8口网管型工业以太网交换机产品性能介绍
- 计算机网络流量图阅读与理解,计算机网络流量监控的设计与实现
- linux命令行如何上翻,Linux学习教程-获取可用命令行参数 or 文件上下翻转和左右翻转...
- 2019年网络规划设计师上午真题及答案解析
- python 发包爬取中国移动充值页面---可判断手机号是否异常
- php xml 实例教程,php解析xml方法实例详解,解析xml实例详解_PHP教程
- 一次PHP中SQL的Where子句无效问题的解决之旅
- 阿里面试回答的认真总结
- 英特尔核显驱动hd630_英特尔发新处理器,换新 Logo,还把 AMD 吊打了一轮
- 结构体、文件操作、指针
- atitit.web ui 结构建模工具总结
- Ubuntu18.04配置视觉SLAM十四讲代码运行环境
- 速腾聚创三维激光雷达 实现cartographer 建图复现工作(代完成)
- 机器学习:什么是预测模型性能评估
- ForkJoinPool 详解
- CTO创新思维与能力
- 大促中为什么需要可视化监控大屏?
- 【往届会议已EI检索】第六届管理工程、软件工程与服务科学国际会议