现在正在做一个项目,需要做一个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通讯相关推荐

  1. 安装图解:Linux Mint 4.0(Daryna)(或者说完美的桌面系统)

    http://www.pusuo.net/2009-08-04/110221670.html 安装图解:Linux Mint 4.0(Daryna)(或者说完美的桌面系统) 作者falko (Cont ...

  2. WinUsb实现USB免驱通讯技术总结

    随着电脑更新换代.USB接口逐渐替代传统接口(串口等),为了更加方便与PC机进行通讯,引入USB通讯. 如何才能实现产品与PC又快又稳定通讯 如何解决串口通讯中接受不定时帧间隔问题 如何实现即插即用的 ...

  3. VirtualBox下安装rhel5.5 linux系统

    以前也用过VMware server和VMware workstation虚拟机,现在使用了一段时间VirtualBox,感觉它比较轻巧,很适合我,在Win7系统下用起来很方便.下面详细介绍下在Vir ...

  4. virtualenv 安装与使用linux下(记录一下)

    virtualenv 安装与使用linux下(记录一下) 1.通过 pip install virtualenv 安装 pip install virtualenv 1 2.查看是否成功安装 virt ...

  5. 多个linux发行版本混合安装盘,使用 MultiBootUSB 安装多个 Linux 版本

    导读 我喜欢通过 U 盘尝试不同的 Linux 发行版.它让我可以在真实的硬件上测试操作系统,而不是虚拟化的环境中.此外,我可以将 USB 插入任何系统(比如 Windows 系统),做任何我想要的事 ...

  6. 如何在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 ...

  7. 在windows上的git bash中安装tree 和 linux tree命令使用

    在windows上的git bash中安装tree 和 linux tree命令使用 文章目录: 1 在windows上的git bash中安装tree 1.1 下载windows版本的tree 1. ...

  8. m1mac安装linux,M1 Mac 能安装 Ubuntu 和 Linux 了 ??

    原标题:M1 Mac 能安装 Ubuntu 和 Linux 了 ?? 作者:IT 之家.cnBeta 一.安装 Windows 10成功后:M1 Mac 运行 Ubuntu 也实现了 借助模拟器工具Q ...

  9. ubuntu amd 64bit 安装 QQ for linux教程(附 不能使用中文的解决办法)

    ubuntu amd 64bit 安装 QQ for linux教程(附 不能使用中文的解决办法) Ubuntu 64bit 安装32bit软件 64bit系统构架的先进不由多讲,只是应用程序相对少了 ...

最新文章

  1. 密码技术--椭圆曲线算法EDCSA数字签名及Go语言应用
  2. 4.边缘光照的描边shader
  3. 8口网管型工业以太网交换机产品性能介绍
  4. 计算机网络流量图阅读与理解,计算机网络流量监控的设计与实现
  5. linux命令行如何上翻,Linux学习教程-获取可用命令行参数 or 文件上下翻转和左右翻转...
  6. 2019年网络规划设计师上午真题及答案解析
  7. python 发包爬取中国移动充值页面---可判断手机号是否异常
  8. php xml 实例教程,php解析xml方法实例详解,解析xml实例详解_PHP教程
  9. 一次PHP中SQL的Where子句无效问题的解决之旅
  10. 阿里面试回答的认真总结
  11. 英特尔核显驱动hd630_英特尔发新处理器,换新 Logo,还把 AMD 吊打了一轮
  12. 结构体、文件操作、指针
  13. atitit.web ui 结构建模工具总结
  14. Ubuntu18.04配置视觉SLAM十四讲代码运行环境
  15. 速腾聚创三维激光雷达 实现cartographer 建图复现工作(代完成)
  16. 机器学习:什么是预测模型性能评估
  17. ForkJoinPool 详解
  18. CTO创新思维与能力
  19. 大促中为什么需要可视化监控大屏?
  20. 【往届会议已EI检索】第六届管理工程、软件工程与服务科学国际会议

热门文章

  1. 函数空间(巴纳赫空间、欧几里得空间、希尔伯特空间)
  2. 2021高教杯数学建模A
  3. 有什么高效的Windows笔记软件?3款优秀的笔记软件推荐!
  4. 区块链从入门到精通 - 区块链培训
  5. 7年了,终于拉开窗帘,看窗外,世界依然美好,这个世界,我还在
  6. 数据挖掘实战:二手车交易价格预测
  7. 深度:戴尔中国十年之变
  8. 2020高考倒计时html,2020高考倒计时的励志说说
  9. 重拾C语言——结构体和共用体
  10. 分形之城--没过,吃宵夜去了,生蚝真好吃