linux平台实现USB虚拟总线驱动一(原理以及开发流程)

17 篇文章 1 订阅
12 篇文章 0 订阅

                                     by fanxiushu 2019-11-07 转载或引用请注明原始作者。

之前的文章阐述过在windows平台下,如何实现USB虚拟总线驱动, 以及如何在windows平台采集真实USB设备的数据,
然后通过网络传输,达到 ”延长“ USB线缆的效果。
 相关链接如下:
https://blog.csdn.net/fanxiushu/article/details/51420096  USB设备驱动开发之远程访问USB设备(一USB设备数据采集端)
https://blog.csdn.net/fanxiushu/article/details/51494169  USB设备驱动开发之远程访问USB设备(二 USB设备虚拟端)
https://blog.csdn.net/fanxiushu/article/details/51559720  USB设备驱动开发之远程访问USB设备( 三 虚拟USB控制器和根集线器)

以上都是windows平台下的,其中第一个链接是关于如何实现采集端的,后面两个链接阐述USB虚拟端。
当初想要实现USB虚拟驱动的目的比较另类,也在上面的文章中说明了。
因为偶尔会在macOS系统中做些iOS手机数据备份开发什么的,而我的macOS系统是装到vmware虚拟机中的,
结果每次都需要把手机USB数据线插到笔记本电脑上,总感觉这跟线多余,于是总想着有没有办法去掉,于是才有了上面的开发的文章。
实现的效果如下图这样的:


简单解释一下,图中A部分”Fanxiushu Virtual USB Host Controller“ 和 ”Fanxiushu Virtual USB Root HUB“ 是我实现的USB虚拟总线驱动,
这个总线驱动需要虚拟控制器和根集线器的,否则vmware这样的软件无法识别的,
而这个 驱动下“的Port1端口插入了” iPhone手机的,其实真实iPhone手机插到我的另一台windows台式机器上,
这里通过网络传输,然后模拟成”插入“ ”Fanxiushu Virtual USB Host Controller“ 的效果。
但是 Port1端口显示的是 ”Vmware USB Device“ , 说明这个USB设备已经被vmware接管了。
紧接着看图中的B部分。显示”Apple Fanxiushu-USB-Device1“设备已经连接进去vmware虚拟机中,
再看图中的 C部分, 在虚拟机中macOS系统中,已经识别到了我的iPhone手机。
这就是当初实现USB虚拟驱动,想要达到的目的。

本文即将描述的就是linux平台下的USB虚拟总线驱动的实现,也可以叫虚拟USB控制器驱动。
同时,也在以前的文章中,阐述过如何在linux平台中采集真实USB设备的数据,
链接如下:
https://blog.csdn.net/fanxiushu/article/details/73478924   USB驱动开发之远程访问USB设备扩展(linux平台USB设备数据采集端)

配合本文的虚拟USB总线驱动,可以实现在linux平台之间任意的共享USB设备,
如果再配合windows平台下的实现,则可实现windows,linux平台之间任意共享USB设备。
同时也可以把虚拟总线驱动单独拿出来,用于模拟各类通用的USB设备。
比如模拟USB摄像头,如下链接描述的就是利用USB总线驱动模拟 USB摄像头:
https://blog.csdn.net/fanxiushu/article/details/52761644  USB设备驱动开发之扩展(利用USB虚拟总线驱动模拟USB摄像头)
也可以模拟USB声卡,U盘,USB键盘鼠标,游戏手柄等,只有想不到的没有办不到的,因为USB接口太通用了。
也可以模拟某些私有协议的USB设备,当然前提是必须知道这类设备的USB通讯协议格式。

linux下实现USB虚拟总驱动并没有windows平台那么有用,因为使用linux的人太少了。
不过考虑在linux服务器下,尤其是作为桌面云服务器的linux宿主机,这个USB虚拟总线驱动用处却是比较大。
远程桌面,需要解决的一个问题就是USB设备的远程共享。
一般是终端的设备采集到USB设备数据,发送到云桌面服务器的宿主机端,linux宿主机使用虚线USB总线驱动模拟出USB设备,
然后就像上图vmware虚拟机把iPhone设备接管到vmware虚拟机那样,把这个模拟的设备转嫁到对应的虚拟机中。
当然这是其中一个办法,还有就是直接从终端设备采集到的USB数据,传输到虚拟机内部,
然后虚拟机内部的操作系统开发出的USB虚拟总线驱动模拟出对应的USB设备。
至于哪个方法比较好,取决于具体的情况。

回到正题.
linux下的USB虚拟总线驱动框架比起windows来说太简单了。
只需调用几个函数,注册几个回调函数,就能实现一个虚拟USB总线驱动框架。

linux内核从2.6版本开始,就实现了一种叫 platform总线的虚拟总线,这是一种通用的虚拟总线框架结构。
为什么会有这么一种架构,
因为在硬件的世界中,有些外设与CPU通讯是使用标准的总线的,比如 USB总线,I2C总线,PCI总线等等,
但是有些外设是与CPU连在一起,这些外设直接扩展到CPU的地址空间,比如SoC。
如果两类设备按照两套逻辑来处理,显然会给系统内核造成不必要的罗嗦和混乱,所以规定所有的设备都具有总线,
只不过Soc使用的虚拟总线,这就是platofrm总线的由来。
我们在开发虚拟USB总线驱动的时候,就是需要使用platfrom总线。使用它的方式也是很简单。

我们在代码中定义  platform_driver 和 platform_device 数据结构, 如下代码:

/// 驱动入口 

static struct platform_driver host_driver = {

.probe = host_add_device,

.remove = host_remove_device,

.suspend = host_suspend,

.resume = host_resume,

.driver = {

   .name = "usb_host", /// 和下面的device一样

   .owner = THIS_MODULE,

    },

};

 

static void platform_device_release(struct device *dev)

{

// do  nothing, is virtual host

printk("-- usb_host: platform_device_release\n");

}

static struct platform_device host_device = {

.name = "usb_host",

.id = -1,

.dev = {

    .release = platform_device_release,

     },

};
 

其中host_add_device,host_remove_device,host_suspend,host_resume是回调函数,
如果熟悉windows驱动,也比较好理解 host_add_device和host_remove_device含义,
host_add_deivce相当于windows中的AddDevice回调函数,是虚拟总线驱动加载的时候被调用,
host_remove_deivce是在驱动卸载时候被调用。

定义如上两个结构之后,在驱动初始化入口函数中调用 platform_driver_register注册总线驱动,
调用 platform_device_register 注册总线设备,如下伪代码:

static int __init host_driver_init(void)

{

int ret;

。。。

ret = platform_driver_register(&host_driver);

。。。。

//注册一个平台总线设备

ret = platform_device_register(&host_device);

。。。。

printk("-- usb_host: drive init ok.\n");

return 0;

}
在退出函数注销,如下伪代码:

static void __exit host_driver_exit(void)

{

platform_device_unregister(&host_device);

platform_driver_unregister(&host_driver);

printk("--- usb_host : driver exit.\n");

}
初始化模块:

module_init(host_driver_init);

module_exit(host_driver_exit);

这样,platform 总线驱动就建立起来了。是不是比起windows实现虚拟总线驱动简单得多了。
接着我们在host_add_device回调函数中初始化USB总线驱动,创建HCD,也就是 USB主机控制器。

开始之前,先大致来了解linux平台下,USB  Host端,也就是主机端驱动的总体框架流程。
主要分为三层:
 1, USB 设备驱动, 这里就是具体的USB设备,负责主机与USB设备通信。
            |
2,  USB Core , 负责连接和管理上下两层,并且对上面的USB设备驱动提供API接口,对下面对的USB主机驱动提供API接口。
            |
3, USB 主机控制器驱动,负责控制管理插入的USB设备。
       比如最常见的EHCI(USB2),XCHI(USB3),OCHI(USB1)主机控制器驱动


我们这里需要实现的就是第3个部分,USB主机控制器,同时管理我维护着我们的“虚拟USB设备”。 
在host_add_device回调函数中,调用usbcore提供的 usb_create_hcd 创建主机控制器,
然后调用 usb_add_hcd 函数把主机控制器加入到普拉头发柔美总线设备中,这样一个USB主机控制器就建立起来了。
当然还需要在usb_add_device回调函数做一些其他相关的工作。
其中 usb_add_hcd函数内部的实现很复杂,有兴趣可以去阅读linux内核源代码。
其中一个重要的就是在usb_add_hcd内部会创建一个root  hub 虚拟根集线器设备,用于管理插到主机上的USB设备或USBHUB,
usb_create_hcd函数会要求传递一个hc_driver数据结构变量。
这里边定义了所有关于USB数据交换,状态查询,USB控制等回调函数。用于查询和管理USB设备状态,URB数据传输。
 把hc_driver里边相关的回调函数实现了,就等于是完整的实现了一个USB控制器驱动。
因此,我们的主要任务就是实现hc_driver结构里边的回调函数。

hc_driver结构比较复杂,这里只实现我们在虚拟控制器驱动需要实现的内容,如下:

///host主机相关结构和回调函数

static struct hc_driver _hc_driver = {

.description = "usb_host",

.product_desc = "Fanxiushu Virtual USB Host Controller",

.hcd_priv_size = sizeof(struct usb_host_t),   //

 

.flags = HCD_USB2, //

 

.start = usb_host_start,   //主机控制器启动

.stop =  usb_host_stop,  //主机控制器停止

 

.urb_enqueue = usb_host_urb_enqueue, //上层的USB设备驱动发起了URB请求,递交到主机控制器中了

.urb_dequeue = usb_host_urb_dequeue,  //上层URB请求取消,或者主机检测到USB设备被拔出了

 

.get_frame_number = usb_host_get_frame_number,

 

        .hub_status_data = usb_host_hub_status,  //查询主机控制器的端口状态,
        .hub_control = usb_host_hub_control,            //设置,清除,查询端口状态。

 

.bus_suspend = usb_host_bus_suspend, //

        .bus_resume = usb_host_bus_resume, //

};


其中hub_status_data和hub_control回调函数的实现,可以查阅usbip的代码,或者借鉴linux内核中其他类似代码。
 无非就是对USB控制器的每个端口状态查询,设置等操作。

重点是urb_enqueue回调函数的实现,这个是USB的通讯核心数据包传递函数。
具体的说,就是上层的USB设备驱动调用usbcore提供的usb_submit_urb 函数的时候,
usb_submit_urb做些其他处理,然后调用usb_hcd_submit_urb函数,
usb_hcd_submit_urb最终进入到我们的主机驱动,调用 urb_enqueue 回调函数, 
如果是真正的USB主机控制器,则在urb_enqueue回调函数中把URB请求递交给USB硬件,
而这里是虚拟主机控制器,因此可以在urb_enqueue中以任何方式传递urb请求数据,
比如在usbip代码中,直接把urb数据通过socket网络传输给对方。
而在我们的代码实现中,为了方便和灵活使用,统一把URB请求数据包传递到应用层,然后在应用层再做其他方面的处理。
当我们的主机驱动处理完这个URB请的时候,调用usbcore提供的usb_hcd_giveback_urb 函数,
通知上层的usb设备驱动,URB请求已经完成。
这时候,上层sub驱动设置的urb回调函数就会被调用,从而上层的usb设备驱动就获取到已经完成的urb数据。

一个完整的urb通讯流程就这样完成了。
现在还有一个问题,如何模拟”插入“和”拔出“USB设备。
在hub_control控制回调函数中,usbcore会查询roothub的设备描述信息,在里边填写我们主机驱动提供的端口数,比如16个。
也就是我们的主机可以提供16个端口同时给16个USB设备。然后每个端口对应一个相应的状态,一开始都是未连接状态。
当我要在某个端口“插入”一个USB设备的时候,改变这个端口状态,然后调用 usb_hcd_poll_rh_status 函数通知usbcore。
usbcore会接着调用 hub_control 回调函数查询端口状态,发现某个端口已经插入了USB设备,
于是调用usb_submit_urb函数获取这个USB设备的设备描述符等相关信息,于是我们的urb_enqueue被调用。
获取描述符,然后根据设备描述符等信息,试图加载对应的USB设备驱动。
USB设备驱动加载之后,接着会调用usb_submit_urb建立起正常的USB设备通信。
在这里,usbcore的行为与windows平台PNP即插即用管理器的行为十分相似。

至此,一个完整的linux平台的USB虚拟控制器驱动内核部分就算实现了,
因为我们的驱动是把URB数据传递到应用层再来处理的。
接着需要处理的就是如何处理URB数据包
1, 如果是模拟一些USB设备,则直接填写相关数据,然后返回给驱动。
2,如果是实现类似usbip功能,则把数据整理打包,再通过网络传递给对方。
这里也就不再赘述。
下图是在 CentOS8系统中(linux内核版本是4.18), 模拟一个USB摄像头的效果图:
USB摄像头的模拟数据是根据以前所写的windows平台的模拟数据,
USB总线驱动不单可以模拟USB摄像头,还能模拟其他通用USB设备,这里为了方便,只模拟了USB摄像头。



未完待续,
下一章主要阐述如何把驱动移植到Android系统中,并且模拟出一个USB摄像头的效果。
 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
dW 登录 | 注册 IBM developerWorks® 技术主题 软件下载 社区 技术讲座 搜索 developerWorks 打印本页面用电子邮件发送本页面新浪微博人人网腾讯微博搜狐微博网易微博DiggFacebookTwitterDeliciousLinked In developerWorks 中国技术主题Java technology文档库 在 Java 应用程序中访问 USB 设备 介绍 USB、jUSB 和 JSR-80 Java 平台一直都以其平台无关性自豪。虽然这种无关性有许多好处,但是它也使得编写与硬件交互的 Java 应用程序的过程变得相当复杂。在本文中,研究科学家蒋清野讨论了两个项目,它们通过提供使Java 应用程序可以使用 USB 设备的 API 而使这个过程变得更容易。虽然这两个项目仍然处于萌芽状态,但是它们都显示了良好的前景,并已经成为一些实用应用程序的基础。 1 评论: 蒋清野 ([email protected]), 研究科学家, HappyFox Engineering Solutions 2003 年 10 月 25 日 + 内容 在 IBM Bluemix 云平台开发并部署您的下一个应用。 现在就开始免费试用 通用串行总线(Universal Serial Bus USB)规范的第一个版本发表于 1996年 1月。因为它的低成本、高数据传输率、使用容易和灵活性,USB 在计算机行业里获得了广泛接受。今天,许多周边设备和装置都是通过 USB 接口连接到计算机上的。目前,大多数一般用途的操作系统都提供了对 USB 设备的支持,并且用 C 或者 C++ 可以相对容易地开发访问这些外设的应用程序。不过,Java 编程语言在设计上对硬件访问提供的支持很少,所以编写与 USB 设备交互的应用程序是相当困难的。 IBM 的 Dan Streetman 最早开始了在 Java 语言中提供对 USB 设备的访问的努力。2001年,他的项目通过 Java 规范请求(Java Specification Request,JSR)过程被接受为 Java 语言的候选扩展标准。这个项目现在称为 JSR-80 并且指定了官方包 javax.usb 。同时,在 2000年 6月,Mojo Jojo 和 David Brownell 在 SourceForge 开始了 jUSB 项目。这两个项目都开发出了 Linux 开发人员可以使用的包,尽管它们都还很不完善。这两个项目也都开始试图向其他操作系统上的 Java 应用程序提供对 USB 设备的访问,尽管它们都还没有开发出可以使用的包(参阅 参考资料 中有关本文中讨论的这两个项目及其他项目的资料)。 在本文中,将对 jUSB 和 JSR-80 项目作一个简要介绍,不过,我们首先要看一下 USB 协议的具体细节,这样您就可以理解这两个项目是如何与 USB 设备交互的。我们还将提供代码片段以展示如何用这两个项目的 API 访问 USB 设备。 USB 介绍 1994年,一个由四个行业伙伴(Compaq、Intel、Microsoft 和 NEC)组成的联盟开始制定 USB 协议。该协议最初的目的是将 PC 与电话相连并提供容易扩展和重新配置的 I/O 接口。1996年 1月,发表了 USB 规范的第一个版本,1998年 9月发表了后续版本(版本 1.1)。这个规范允许 127台设备同时连接到一起,总的通信带宽限制为 12 Mbps。后来,又有三个成员(Hewlett-Packard、Lucent 和 Philips)加入了这个联盟。2000年 4月,发表了 USB 规范的 2.0版本,它支持高达 480 Mbps 的传输率。今天,USB 在高速(视频、图像、储存)和全速(音频、宽带、麦克风)数据传输应用中起了关键作用。它还使各种低速设备(键盘、鼠标、游戏外设、虚拟现实外设)连接到 PC 上。 USB 协议有严格的层次结构。在所有 USB 系统中,只有一个主设备,到主计算机的的 USB 接口称为 主控器(host controller)。主控器有两个标准――开放主控器接口(Compaq 的 Open Host Controller Interface,OHCI)和通用主控器接口(Intel 的 Universal Host Controller Interface,UHCI)。这两个标准提供了同样的能力,并可用于所有的 USB 设备,UHCI 的硬件实现更简单一些,但是需要更复杂的设备驱动程序(因而 CPU 的负荷更大一些)。 USB 物理互连是分层的星形拓朴,最多有七层。一个 hub 是每个星形的中心,USB 主机被认为是 root hub。每一段连线都是 hub 与 USB 设备的点对点连接,后者可以是为系统提供更多附加点的另一个 hub,也可以是一个提供功能的某种设备。主机使用主/从协议与 USB 设备通信。这种方式解决了包冲突的问题,但是同时也阻止了附加的设备彼此建立直接通信。 所有传输的数据都是由主控器发起的。数据从主机流向设备称为 下行(downstream)或者 输出(out)传输,数据从设备流向主机称为 上 行(upstream)或者 输入(in)传输。数据传输发生在主机和 USB 设备上特定的 端点(endpoint) 之间,主机与端点之间的数据链接称为 管道(pipe)。 一个给定的 USB 设备可以有许多个端点,主机与设备之间数据管道的数量与该设备上端点的数量相同。一个管道可以是单向或者是双向的,一个管道中的数据流与所有其他管道中的数据流无关。 USB 网络中的通信可以使用下面四种数据传输类型中的任意一种: 控制传输:这些是一些短的数据包,用于设备控制和配置,特别是在设备附加到主机上时。 批量传输:这些是数量相对大的数据包。像扫描仪或者 SCSI 适配器这样的设备使用这种传输类型。 中断传输:这些是定期轮询的数据包。主控器会以特定的间隔自动发出一个中断。 等时传输:这些是实时的数据流,它们对带宽的要求高于可靠性要求。音频和视频设备一般使用这种传输类型。 像串行端口一样,计算机上每一个 USB 端口都由 USB 控制器指定了一个惟一的标识数字(端口 ID)。当 USB 设备附加到 USB 端口上时,就将这个 惟一端口 ID 分配给这台设备,并且 USB 控制器会读取 设备描述符。设备描述符包括适用于该设备的全局信息、以及设备的 配置信息。配置定义了一台 USB 设备的功能和 I/O 行为。一台 USB 设备可以有一个或者多个配置,这由它们相应的配置描述符所描述。每一个配置都有一个或者多个 接口,它可以视为一个物理通信渠道 ;每一个接口有零个或者多个端点,它可以是数据提供者或者数据消费者,或者同时具有这两种身份。接口由接口描述符描述,端点由端点描述符描述。并且一台 USB 设备可能还有字符串描述符以提供像厂商名、设备名或者序列号这样的附加信息。 正如您所看到的,像 USB 这样的协议为使用 Java 这种强调平台和硬件无关性的语言的开发人员提出了挑战。现在让我们看两个试图解决这个问题的项目。 回页首 jUSB API jUSB 项目是由 Mojo Jojo 和 David Brownell 于 2000年 6月创立的。其目标是提供一组免费的、在 Linux 平台上访问 USB 设备的 Java API。这个 API 是按照 Lesser GPL (LGPL)条款发表的,这意味着您可以在专有和免费软件项目中使用它。这个 API 提供了对多个物理 USB 设备的多线程访问,并支持本机和远程设备。具有多个接口的设备可以同时被多个应用程序(或者设备驱动程序)所访问,其中每一个应用程序(或者设备驱动程序)都占据一个不同的接口。该 API 支持控制传输、批量传输和中断传输,不支持等时传输,因为等时传输用于媒体数据(如音频和视频),JMF API 已经在其他标准设备驱动程序上对此提供了很好的支持(参阅 参考资料)。当前,该 API 可以在具有 Linux 2.4 核心或者以前的 2.2.18 核心的 GNU/Linux 版本上工作。因此可支持大多数最新的版本,例如,该 API 可以在没有任何补丁或者升级的 Red Hat 7.2 和 9.0 上工作。 jUSB API 包括以下包: usb.core : 这个包是 jUSB API 的核心部分。它使得 Java 应用程序可以从 USB 主机访问 USB 设备。 usb.linux : 这个包包含 usb.core.Host 对象的 Linux 实现、bootstrapping 支持和其他可以提升 Linux USB 支持的类。这个实现通过虚拟 USB 文件系统( usbdevfs )访问 USB 设备。 usb.windows : 这个包包含 usb.core.Host 对象的 Windows 实现、bootstrapping 支持和其他可以提升 Windows USB 支持的类。这个实现仍然处于非常初级的阶段。 usb.remote : 这个包是 usb.core API 的远程版本。它包括一个 RMI proxy 和一个 daemon 应用程序,它让 Java 应用程序可以访问远程计算机上的 USB 设备。 usb.util : 这个包提供了一些有用的实用程序,可以将 firmware下载到 USB 设备上、将 USB 系统的内容转储到 XML 中、以及将只有 bulk I/O 的 USB 设备工具转换成一个套接字(socket)。 usb.devices : 这个可选包收集了用 jUSB API 访问不同 USB 设备的 Java 代码,包括柯达数码相机和 Rio 500 MP3 播放器。这些 API 经过特别编写以简化访问特定 USB 设备的过程,并且不能用于访问其他设备。这些 API 是在 usb.core API 之上构建的,它们可以工作在所有支持 jUSB 的操作系统上。 usb.view : 这个可选包提供了基于 Swing 的 USB 树简单浏览器。它是一个展示 jUSB API 应用的很好的示例程序。 尽管 usb.core.Host 对象的实现对于不同的操作系统是不同的,但是 Java 程序员只需要理解 usb.core 包就可以用 jUSB API 开始应用程序的开发。表 1 列出了 usb.core 的接口和类,Java 程序员应该熟悉它们: 表 1. jUSB 中的接口和类 接口 说明 Bus 将一组 USB 设备连接到 Host 上 Host 表示具有一个或者多个 Bus 的 USB 控制器 类 说明 Configuration 提供对设备所支持的 USB 配置的访问,以及对与该配置关联的接口的访问 Descriptor 具有 USB 类型的描述符的实体的基类 Device 提供对 USB 设备的访问 DeviceDescriptor 提供对 USB 设备描述符的访问 EndPoint 提供对 USB 端点描述符的访问、在给定设备配置中构造设备数据输入或者输出 HostFactory 包含 bootstrapping 方法 Hub 提供对 USB hub 描述符以及一些 hub 操作的访问 Interface 描述一组端点,并与一个特定设备配置相关联 PortIdentifier 为 USB 设备提供稳定的字符串标识符,以便在操作和故障诊断时使用 用 jUSB API 访问一台 USB 设备的正常过程如下: 通过从 HostFactory 得到 USB Host 进行 Bootstrap。 从 Host 访问 USB Bus ,然后从这个 Bus 访问 USB root hub(即 USB Device )。 得到 hub 上可用的 USB 端口数量,遍历所有端口以找到正确的 Device 。 访问附加到特定端口上的 USB Device 。可以用一台 Device 的 PortIdentifier 直接从 Host 访问它,也可以通过从 root hub 开始遍历 USB Bus 找到它。 用 ControlMessage 与该 Device 直接交互,或者从该 Device 的当前 Configuration 中要求一个 Interface, 并与该 Interface 上可用的 Endpoint 进行 I/O 。 清单 1 展示了如何用 jUSB API 获得 USB 系统中的内容。这个程序编写为只是查看 root hub 上可用的 USB 设备,但是很容易将它改为遍历整个 USB 树。这里的逻辑对应于上述步骤 1 到步骤 4。 清单 1. 用 jUSB API 获得 USB 系统的内容 import usb.core.*; public class ListUSB { public static void main(String[] args) { try { // Bootstrap by getting the USB Host from the HostFactory. Host host = HostFactory.getHost(); // Obtain a list of the USB buses available on the Host. Bus[] bus = host.getBusses(); int total_bus = bus.length; // Traverse through all the USB buses. for (int i=0; i<total_bus; i++) { // Access the root hub on the USB bus and obtain the // number of USB ports available on the root hub. Device root = bus[i].getRootHub(); int total_port = root.getNumPorts(); // Traverse through all the USB ports available on the // root hub. It should be mentioned that the numbering // starts from 1, not 0. for (int j=1; j<=total_port; j++) { // Obtain the Device connected to the port. Device device = root.getChild(j); if (device != null) { // USB device available, do something here. } } } } catch (Exception e) { System.out.println(e.getMessage()); } } 清单 2 展示了在应用程序成功地找到了 Device 的条件下,如何与 Interface 和 EndPoint 进行批量 I/O。 这个代码段也可以修改为执行控制或者中断 I/O。它对应于上述步骤 5。 清单 2. 用 jUSB API 执行批量 I/O if (device != null) { // Obtain the current Configuration of the device and the number of // Interfaces available under the current Configuration. Configuration config = device.getConfiguration(); int total_interface = config.getNumInterfaces(); // Traverse through the Interfaces for (int k=0; k<total_interface; k++) { // Access the currently Interface and obtain the number of // endpoints available on the Interface. Interface itf = config.getInterface(k, 0); int total_ep = itf.getNumEndpoints(); // Traverse through all the endpoints. for (int l=0; l<total_ep; l++) { // Access the endpoint, and obtain its I/O type. Endpoint ep = itf.getEndpoint(l); String io_type = ep.getType(); boolean input = ep.isInput(); // If the endpoint is an input endpoint, obtain its // InputStream and read in data. if (input) { InputStream in; in = ep.getInputStream(); // Read in data here in.close(); } // If the Endpoint is and output Endpoint, obtain its // OutputStream and write out data. else { OutputStream out; out = ep.getOutputStream(); // Write out data here. out.close(); } } } } jUSB 项目在 2000年 6月到 2001年 2月期间非常活跃。该 API 的最新的版本 0.4.4发表于 2001年 2月 14日。从那以后只提出了很少的改进,原因可能是 IBM 小组成功地成为了 Java 语言的候选扩展标准。不过,基于 jUSB 已经开发出一些第三方应用程序,包括 JPhoto 项目(这是一个用 jUSB 连接到数码照相机的应用程序)和 jSyncManager 项目(这是一个用 jUSB 与使用 Palm 操作系统的 PDA 同步的应用程序)。 回页首 JSR-80 API (javax.usb) 正如前面提到的,JSR-80 项目是由 IBM 的 Dan Streetman 于 1999年创立的。2001年,这个项目通过 Java 规范请求(JSR)过程被接受为 Java 语言的候选扩展标准。这个项目现在称为 JSR-80 并且被正式分派了 Java 包 javax.usb 。这个项目使用 Common Public License 的许可证形式,并通过 Java Community Process 进行开发。这个项目的目标是为 Java 平台开发一个 USB 接口,可以从任何 Java 应用程序中完全访问 USB 系统。JSR-80 API 支持 USB 规范所定义的全部四种传输类型。目前,该 API 的 Linux 实现可以在支持 2.4 核心的大多数最新 GNU/Linux 版本上工作,如 Red Hat 7.2 和 9.0。 JSR-80 项目包括三个包: javax-usb ( javax.usb API)、 javax-usb-ri (操作系统无关的基准实现的公共部分)以及 javax-usb-ri-linux (Linux 平台的基准实现,它将公共基准实现链接到 Linux USB 堆栈)。所有这三个部分都是构成 Linux 平台上 java.usb API 完整功能所必需的。在该项目的电子邮件列表中可以看到有人正在致力于将这个 API 移植到其他操作系统上(主要是 Microsoft Windows),但是还没有可以工作的版本发表。 尽管 JSR-80 API 的操作系统无关的实现在不同的操作系统上是不同的,但是 Java 程序员只需要理解 javax.usb 包就可以开始开发应用程序了。表 2 列出了 javax.usb 中的接口和类, Java 程序员应该熟悉它们: 表 2. JSR-80 API 中的接口和类 接口 说明 UsbConfiguration 表示 USB 设备的配置 UsbConfigurationDescriptor USB 配置描述符的接口 UsbDevice USB 设备的接口 UsbDeviceDescriptor USB 设备描述符的接口 UsbEndpoint USB 端点的接口 UsbEndpointDescriptor USB 端点描述符的接口 UsbHub USB hub 的接口 UsbInterface USB 接口的接口 UsbInterfaceDescriptor USB 接口描述符的接口 UsbPipe USB 管道的接口 UsbPort USB 端口的接口 UsbServices javax.usb 实现的接口 类 说明 UsbHostManager javax.usb 的入口点 用 JSR-80 API 访问 USB 设备的正常过程如下: 通过从 UsbHostManager 得到相应的 UsbServices 进行 Bootstrap。 通过 UsbServices 访问 root hub。在应用程序中 root hub 就是一个 UsbHub 。 获得连接到 root hub 的 UsbDevice s 清单。遍历所有低级 hub 以找到正确的 UsbDevice 。 用控制消息( UsbControlIrp )与 UsbDevice 直接交互,或者从 UsbDevice 的相应 UsbConfiguration 中要求一个 UsbInterface 并与该 UsbInterface 上可用的 UsbEndpoint 进行 I/O。 如果一个 UsbEndpoint 用于进行 I/O,那么打开与它关联的 UsbPipe 。通过这个 UsbPipe 可以同步或者异步提交上行数据(从 USB 设备到主计算机)和下行数据(从主计算机到 USB 设备)。 当应用程序不再需要访问该 UsbDevice 时,关闭这个 UsbPipe 并释放相应的 UsbInterface 。 在清单 3 中,我们用 JSR-80 API 获得 USB 系统的内容。这个程序递归地遍历 USB 系统上的所有 USB hub 并找出连接到主机计算机上的所有 USB 设备。这段代码对应于上述步骤 1 到步骤 3。 清单 3. 用 JSR-80 API 获得 USB 系统的内容 import javax.usb.*; import java.util.List; public class TraverseUSB { public static void main(String argv[]) { try { // Access the system USB services, and access to the root // hub. Then traverse through the root hub. UsbServices services = UsbHostManager.getUsbServices(); UsbHub rootHub = services.getRootUsbHub(); traverse(rootHub); } catch (Exception e) {} } public static void traverse(UsbDevice device) { if (device.isUsbHub()) { // This is a USB Hub, traverse through the hub. List attachedDevices = ((UsbHub) device).getAttachedUsbDevices(); for (int i=0; i<attachedDevices.size(); i++) { traverse((UsbDevice) attachedDevices.get(i)); } } else { // This is a USB function, not a hub. // Do something. } } } 清单 4 展示了在应用程序成功地找到 Device 后,如何与 Interface 和 EndPoint 进行 I/O。这段代码还可以修改为进行所有四种数据传输类型的 I/O。它对应于上述步骤 4 到步骤 6。 清单 4. 用 JSR-80 API 进行 I/O public static void testIO(UsbDevice device) { try { // Access to the active configuration of the USB device, obtain // all the interfaces available in that configuration. UsbConfiguration config = device.getActiveUsbConfiguration(); List totalInterfaces = config.getUsbInterfaces(); // Traverse through all the interfaces, and access the endpoints // available to that interface for I/O. for (int i=0; i<totalInterfaces.size(); i++) { UsbInterface interf = (UsbInterface) totalInterfaces.get(i); interf.claim(); List totalEndpoints = interf.getUsbEndpoints(); for (int j=0; j<totalEndpoints.size(); j++) { // Access the particular endpoint, determine the direction // of its data flow, and type of data transfer, and open the // data pipe for I/O. UsbEndpoint ep = (UsbEndpoint) totalEndpoints.get(i); int direction = ep.getDirection(); int type = ep.getType(); UsbPipe pipe = ep.getUsbPipe(); pipe.open(); // Perform I/O through the USB pipe here. pipe.close(); } interf.release(); } } catch (Exception e) {} } JSR-80 项目从一开始就非常活跃。2003年 2月发表了 javax.usb API、RI 和 RI 的 0.10.0 版本。看起来这一版本会提交给 JSR-80 委员会做最终批准。预计正式成为 Java 语言的扩展标准后,其他操作系统上的实现会很快出现。Linux 开发者团体看来对 JSR-80 项目的兴趣比 jUSB 项目更大,使用 Linux 平台的 javax.usb API 的项目数量在不断地增加。 回页首 结束语 jUSB API 和 JSR-80 API 都为应用程序提供了从运行 Linux 操作系统的计算机中访问 USB 设备的能力。JSR-80 API 提供了比 jUSB API 更多的功能,很有可能成为 Java 语言的扩展标准。目前,只有 Linux 开发人员可以利用 jUSB 和 JSR-80 API 的功能。不过,有人正在积极地将这两种 API 移植到其他操作系统上。Java 开发人员应该在不久就可以在其他操作系统上访问 USB 设备。从现在起就熟悉这些 API,当这些项目可以在多个平台上发挥作用时,您就可以在自己的应用程序中加入 USB 功能了。 参考资料 您可以参阅本文在 developerWorks 全球站点上的 英文原文. 有关 USB 规范的更多信息,请访问 USB.org。 访问 SourceForge 上的 jUSB 项目主页。 有关 JSR-80 项目的更多信息,请访问其 主页或者其 在 Java Community Process 中的页面。 查找更多有关 jPhoto 项目的内容。 了解 jSyncManager项目。 有关 JMF 项目的更多内容,参阅 Eric Olson 的全面性的“ Java Media Framework 基础”教程( developerWorks,2002年 5月)。 可以在 developerWorks Java 技术专区 中找到关于 Java 编程各个方面的数百篇文章。 加入 developerWorks 中文社区,查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。 条评论 请 登录 或 注册 后发表评论。 添加评论: 注意:评论中不支持 HTML 语法 有新评论时提醒我剩余 1000 字符 共有评论 (1) 非常不错! 由 javaku 于 2012年05月28日发布 报告滥用 IBM PureSystems IBM PureSystems™ 系列解决方案是一个专家集成系统 developerWorks 学习路线图 通过学习路线图系统掌握软件开发技能 软件下载资源中心 软件下载、试用版及云计算 回页首 帮助 联系编辑 提交内容 订阅源 在线浏览每周时事通讯 新浪微博 报告滥用 使用条款 第三方提示 隐私条约 浏览辅助 IBM 教育学院教育培养计划 IBM 创业企业全球扶持计划 ISV 资源 (英语) dW 中国每周时事通讯 选择语言: English 中文 日本語

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值