随着计算机技术的发展,USB接口技术凭借其可热插拔、即插即用等特点得到了广泛应用。然而,USB不适合长距离数据传输的缺陷阻碍了它的进一步发展。USB Over IP技术结合了USB和IP网络的两大优点,打破了USB的距离限制,使远程USB设备无需透过另一台上网电脑就能够直接被本地计算机使用,极大地增强了USB设备的灵活性。本文在USB Over IP中应用了端口映射,充分利用USB的管道思想,从而实现了在网络上方便地共享USB外围设备。
1 USB管道简介
在USB系统的分层结构中,可以认为数据传输是在主机软件(USB系统软件或客户软件)和USB设备的各个端点之间直接进行的,它们之间的连接被称为“管道”,如图1所示。
管道是对主机和USB设备间通信流的抽象,它表示主机的数据缓冲区和USB设备的端点之间存在着逻辑数据传输,而实际的数据传输是由USB总线接口层来完成的。管道和USB设备中的端点是一一对应的。根据USB数据传输类型的不同,管道又分为流管道和消息管道。其中,流管道是单向的,在流管道中传输的数据不具有USB定义的结构,可用作块传输、同步传输和中断传输;而消息管道是双向的,在消息管道中传输的数据具有USB定义的结构,只用作控制传输。USB设备0号端点所实现的缺省控制管道就是一条消息管道。
USB通用端口映射充分采用了USB逻辑管道思想,将USB端点通过网络一对一地映射到本地计算机中,客户软件访问远程USB设备和访问本地USB设备没有任何区别。
图1 管道
2 端口映射总体设计
USB通用端口映射类似于C/S结构的软件,主要由3部分组成,即USB通用映射程序客户端、USB通用映射程序服务器端和虚拟总线。其中,USB通用映射程序客户端在嵌入式AT91RM9200板卡中实现,操作系统为嵌入式Linux平台,主要完成USB设备的检测和Linux系统中USB设备的驱动,任何USB设备插入AT91RM9200板卡都可以被该驱动初始化;服务器端是Windows操作系统平台的PC机,它需要远程访问USB设备。整体逻辑框图如图2所示。
图2 整体逻辑框图
实现该USB通用端口映射的工作原理主要有3步:
① 把用户在AT91RM9200板卡上需要使用的USB设备驱动,安装到基于Windows平台的PC机服务器上(只需安装其对应的Windows驱动),首先让Windows平台的PC机服务器上可以使用该USB设备。
② 用户在AT91RM9200板卡插入USB设备时,USB通用映射驱动程序客户端会自动把该设备的产商ID和产品ID,报告给Windows平台的PC机服务器上的USB通用映射程序服务器端;再由虚拟总线加在服务器上枚举出该设备的驱动并实现即插即用自动加载。
③ 通过这种USB通用映射程序,可以在Windows平台的PC机下使用远程的USB设备,而且该USB设备在AT91RM9200板卡上的Linux系统中都使用同一个驱动(USB通用映射程序客户端)。
3 端口映射的具体实现
3.1 在USB通用映射程序客户端的实现
通用的USB驱动,关键在于集中器驱动枚举USB设备时让集中器直接加载自己初始化的驱动链表中的驱动,而且不需要PID/VID验证。具体方法是修改Linux内核USB子系统的源代码,这里不再详述。对于通用的USB驱动程序,其端口映射方法大体实现框架如图3所示。
具体步骤如下:
① 在驱动probe探测函数中探测接口所有的端点,并为每个端点建立缓冲区。
② 建立线程1来发送获取IP的UDP请求,每秒发送1次。
③ 建立线程2接收获取IP的UDP应答,阻塞读取;之所以要用两个线程,而不用一个线程先发送后接收,是考虑到UDP协议是不可靠的,万一Windows主机那边没有接收到获取IP的UDP请求,那么Linux就永远阻塞在这里不得返回了。当接收到IP的UDP应答时,就会取消线程1,以免它一直发送占用网络带宽。线程2以当前收到的IP地址配置IP,然后继续返回阻塞读取,以满足Windows主机随时修改IP的要求。
④ IP确定以后,启动多个子线程,每个子线程对应一个USB接口端点,每个子线程调用connect函数申请服务器的处理网络套接字句柄,因而对USB通用映射程序服务器端来说,每个端点都有一个句柄,以便控制。可以将这个句柄存放在一个句柄数据结构中。
⑤ 重要的USB控制端点0缺省控制,因为是双向管道,需要建立两个句柄,即一个IN和一个OUT。
⑥ 批量传输、同步传输和中断传输都是单向的管道,只需建立一个句柄。如果是IN,则阻塞读取套接字的数据。如果有数据到来,则分析其协议,如果是定义的读请求、需读的字节数等,就发送读请求给USB设备,读取USB设备该端点的数据,然后返回,写给套接字。如果是OUT,则仍然阻塞读取套接字的数据。如果有数据到来,则分析其协议,若是定义的写请求、需写的字节数等,就读取套接字后面紧跟着的数据,返回后发送给USB设备。
图3 probe函数大体实现框架
整个USB通用映射都是在内核态完成的,包括网络客户端的实现。
3.2 在USB通用映射程序服务器端的实现
USB通用映射程序服务器端是将Linux客户端的USB端口映射过来,同时与Windows中的虚拟总线进行通信,起到中间桥梁和协议转换的作用。协议转换主要是进行数据的打包和解包,解释网络数据和虚拟总线驱动的数据包,实现网络与虚拟总线的通信。
如图4所示,在USB设备配置过程中建立的USB管道在网络中也保持着其特性。USB设备含有多少个端点,与主机通信时就可以使用多少条管道,在各个管道之间进行数据传输是相互独立的。
图4 端点映射在USB通用映射程序服务器端的实现
3.3 虚拟总线驱动的实现
3.3.1 虚拟总线与系统的关系
某种意义上PNP管理器是一个总线驱动程序,它实现一个称为root的虚拟总线。所有的设备都是连接在这个总线上的,它为每一个直接连接在它上面的设备建立一个PDO(物理设备对象),而将虚拟USB总线直接连接在系统根总线root上。root总线将为虚拟USB总线建立PDO,虚拟USB总线驱动则建立自己的FDO和粘附在其上的子设备的PDO。
虚拟总线由根总线驱动程序建立PDO;虚拟总线驱动建立自己的FDO,如果有USB设备需要枚举,将建立该USB设备的PDO;加载USB设备驱动,该驱动建立自己的FDO。整个设备对象放设备栈中,IRP将在该栈中得到传输。
该虚拟总线扮演了USB总线驱动程序接口(USBDI)的角色,负责处理驱动程序中的各种USB请求。它实际上是与硬件无关的,要在毫无电气连接的总线上枚举一系列USB设备,它无法知道有设备需要列举,因此其枚举总线上的设备是通过应用程序发送相应的IOCTL来指示PDO的建立的(通过IoInvalidateDeviceRelations让PNP管理器发IRP_MJ_PNP)。
3.3.2 虚拟总线枚举USB设备
(1) 发送枚举USB设备的IRP
当USB通用映射程序服务器端给总线驱动程序发送IRP枚举设备时,它将调用DeviceIoControl创建IRP。该IRP的MajorFunction为IRP_MJ_DEVICE_CONTROL,IoControlCode为IOCTL_BUSENUM_PLUGIN_HARDWARE,IRP的 systembuffer中将存储USB设备枚举需要的信息、各种描述符和PID/VID等。
(2) 虚拟总线驱动处理该IRP(创建设备PDO)
当虚拟总线接收到这个IRP时,首先建立一个PDO与该USB设备对应,初始化PDO数据结构,将IRP的systembuffer节中的数据拷贝到PDO数据结构的DescriptorBuffer中,以备以后调用。将该设备的PID/VID放在PDO数据结构的HardwareIDs中,最后调用IoInvalidateDeviceRelations函数通知PNP管理器其总线关系发生了变化,PNP管理器会根据PID/VID寻找设备驱动并加载。
(3) USB设备驱动程序加载(创建设备FDO)
在加载设备驱动时,USB设备驱动调用AddDevice例程创建自己的FDO。USB设备驱动会发出一系列的IRP请求,该IRP附带URB(USB请求块)参数,提出USB设备枚举过程的一系列请求,获得设备描述符、配置描述符、选择接口描述符,配置端点等。而这些IRP也有一定的特点,其MajorFunction为IRP_MJ_INTERNAL_DEVICE_CONTROL,该IRP的下一I/O栈单元的Parameters.DeviceIoControl.IoControlCode字段为IOCTL_INTERNAL_USB_SUBMIT_URB。因此,为了专门处理URB,虚拟总线驱动注册一个例程:
DriverObject﹥MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = Bus_UsbCtl
其中,Bus_UsbCtl主要完成USB设备驱动程序传递下来的各种请求。
总之,虚拟总线驱动屏蔽了操作本地USB设备和远程USB设备的接口区别,当动态配置USB设备完成后,可以像访问本地设备一样访问远程设备。
结语
USB Over IP技术在实现远程网络连接方便性的同时,还具备安装简便、USB性能可靠性强等优势。本文提出的端口映射方案能够不必修改现有的应用程序和外围设备驱动程序,即可在局域网中方便地共享外围设备。然而,USB设备有4种传输方式,不同的传输方式有不同的侧重点。例如,同步传输适用于传输大量的、速度恒定且对周期有要求的数据,不必支持数据触发机制;而批量传输适用于传输大量的且对传输时间和传输速率无要求的数据,必须有数据触发机制。因此,为了取得良好的效果,不同的传输需要采用不同的传输控制策略,这将需要进一步的研究和探索。