WinUSB安装以及与Linux通讯
来源:互联网 发布:ps3安装游戏数据 编辑:程序博客网 时间:2024/06/07 10:09
现在正在做一个项目,需要做一个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 = USBClassGuid={36fc9e60-c465-11cf-8056-444553540000}Provider = %ProviderName%DriverPackageDisplayName = %PackageDisplayName%;CatalogFile=wudf.catCatalogFile.NTx86 = senx86.catCatalogFile.NTIA64 = senia64.catCatalogFile.NTAMD64 = senamd64.catDriverVer=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.infNeeds=WINUSB.NT;[2][USB_Install.Services]Include=winusb.infAddService=WinUSB,0x00000002,WinUSB_ServiceInstall;[3][WinUSB_ServiceInstall]DisplayName = %WinUSB_SvcDesc%ServiceType = 1StartType = 3ErrorControl = 1ServiceBinary = %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,1HKR,,"DeviceIdleEnabled",0x00010001,1HKR,,"DeviceIdleIgnoreWakeEnable",0x00010001,1HKR,,"UserSetDeviceIdleEnabled",0x00010001,1HKR,,"DefaultIdleState",0x00010001,0x1HKR,,"DefaultIdleTimeout",0x00010001,500 ;[6][USB_Install.CoInstallers]AddReg=CoInstallers_AddRegCopyFiles=CoInstallers_CopyFiles[CoInstallers_AddReg]HKR,,CoInstallers32,0x00010000,"WinUSBCoInstaller2.dll","WdfCoInstaller01009.dll,WdfCoInstaller"[CoInstallers_CopyFiles]WdfCoInstaller01009.dllWinUSBCoInstaller2.dll[DestinationDirs]CoInstallers_CopyFiles=11; ================= Source Media Section =====================;[7][SourceDisksNames]1 = %DISK_NAME%,,,\x862 = %DISK_NAME%,,,\amd643 = %DISK_NAME%,,,\ia64[SourceDisksFiles.x86]WdfCoInstaller01009.dll=1WinUSBCoInstaller2.dll=1[SourceDisksFiles.amd64]WdfCoInstaller01009.dll=2WinUSBCoInstaller2.dll=2[SourceDisksFiles.ia64]WdfCoInstaller01009.dll=3WinUSBCoInstaller2.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 iteration if (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()的第三个参数为NULL if (!bResult) {//如果是由于参数为NULL,且获得了正确的接口信息长度,那么申请内存。 if ((ERROR_INSUFFICIENT_BUFFER==GetLastError()) && (requiredLength>0)) { //为pInterfaceDetailData申请内存,长度是上面返回的requiredLength pInterfaceDetailData = (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 data pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //第二次调用,使用正确的长度,和alloc好的pInterfaceDetailData bResult = SetupDiGetDeviceInterfaceDetail( hDeviceInfo, &deviceInterfaceData, pInterfaceDetailData, requiredLength, NULL, &DeviceInfoData); //Check for some other error if (!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设备描述符--这个函数仅获取speedBOOL 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 file BOOL 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指定的所有设备中最后一个的handle bResult = 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 World bResult = 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 安装WinUSB
- libusb和libusbk以及winusb
- WinUSB
- WinUSB
- WinUSB
- WinUSB
- 如何在 Ubuntu 14.04 中安装 Winusb
- 在Windows中如何用SSH_Client_Shell与Linux实现通讯以及共享
- windows 与 Linux SOCKET通讯
- app登陆以及与后台通讯安全性
- Linux环境Nginx安装与调试以及PHP安装
- Linux环境Nginx安装与调试以及PHP安装
- Linux环境Nginx安装与调试以及PHP安装
- linux安装jdk以及eclipse,安装Anaconda与pyChram
- 在 Ubuntu 上用 WinUSB 创建 Win7 安装 U 盘
- Redis简介以及Linux环境下的安装与部署
- Linux中mysql以及phpmyadmin的安装与配置
- 编写灵活的.net代码
- Boost1.39.0-VS2008快速编译
- Tomcat访问日志配置
- MSP430Ware学习笔记 UART SMCLK 115200-8-N-1
- dx找不到TID_D3DRMFrame
- WinUSB安装以及与Linux通讯
- wms-hosts文件配置
- 链表操作程序(完整)C语言
- js定义数组
- 临时表
- 哪个家伙说“网站去.Net化”?
- LibLinear & LibSVM in MATLAB 编译安装 For Mac
- makefile中定义宏进行编译
- AspNetPager 使用Demo