在Java应用程序中访问USB设备

  Java平台一直都在发生这种平台无关性的问题。虽然无关性有很多好处,但它也使得编写与硬件交互的Java应用程序的过程变得相当复杂。在本文中,研究科学家蒋清野讨论了两个问题这些项目,它们通过提供使Java应用程序可以使用USB设备的API而使这个过程变得更容易。虽然这两个项目仍然处于萌芽状态,但是它们都显示出了良好的前景,并且已经成为一些实用的应用程序程序的基础。
  通用串行总线(Universal Serial Bus USB)规范的第一个版本发表于1996年1月。因为它的优点、数据传输率高、使用方便和灵活,USB在计算机行业里获得已经广泛接受。今天,许多周边设备和装置都是通过USB接口连接到计算机上的。目前,大多数通用用途的网络都提供了对USB设备的支持,并且用C或C++可以相对容易地开发访问这些外部设置的应用程序。但是,Java编程语言在设计上对硬件访问提供的支持很少,因此编写与USB设备交互的应用程序是相当困难的。IBM的
  Dan Streetman首先开始在Java语言中提供对USB设备的访问的努力。2001年,他的通过项目Java规范请求(Java Specific 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接口称为主控器(主机控制器)。主控器有两个标准??开放主控器接口(Compaq的Open Host Controller Interface,OHCI)和通用主控器接口(Intel的Universal Host Controller Interface,UHCI)。这两个标准提供了同样的能力,并可用于所有的USB设备,UHCI的硬件实现更简单一些,但是需要更复杂的设备驱动程序(从而CPU的负担更大一些)。
  USB物理互连是分层的星形拓朴,最多有七层。一个集线器是每个星形的中心,USB主机被认为是根集线器。每一段连线都是集线器与USB设备的点对点连接,晚上可以为系统提供更多附加点的另一个集线器,也可以是一个提供功能的某种设备。主机使用主/从协议与USB设备通信。这种方式解决了包冲突的问题,但同时也阻止了附加的设备各处建立直接通信所有传输的数据均由主控器发起的。
  数据从主机流向设备称为下行(下游)或者输出(输出)传输,数据从设备流向主机称为上行(上游)或者输入(输入)传输数据传输发生在主机和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实现、引导支持和其他可以提升Linux USB支持的类。这个实现通过虚拟USB文件系统(usbdevfs)访问USB设备。
  ·usb.windows:这个包包含usb.core.Host对象的Windows实现、bootstrapping支持和其他可以提升Windows USB支持的类。这个实现仍然一个非常初级的阶段。
  ·usb.remote:这个包是usb.core API的远程版本。它包括一个RMI代理和一个守护程序应用程序,它让Java应用程序可以远程访问计算机上的USB设备
  。util:该包提供了一些有用的实用程序,可以将固件下载到USB设备上、将USB系统的内容转储到XML中、以及将仅批量I/O的USB设备工具转换成一个助手(·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表示具有一个或多个总线的USB控制器
  类说明
  配置提供对设备所支持的USB配置的访问,以及对与该配置关联接口的访问描述符
  具有USB类型的实体的基本类
  设备提供对USB设备的访问
  DeviceDescriptor提供对USB设备接口的一些访问
  EndPoint提供对USB接口接口的访问、在给定设备配置中构造设备数据输入或者输出
  HostFactory包含引导方法
  Hub提供对USB集线器接口以及集线器操作的访问
  接口描述一组端点,并与一个特定的设备配置相关联的端口
  标识符为USB设备提供稳定的字符串标识符,便于在操作和故障诊断时使用
  jUSB API访问一个USB设备的正常流程如下:
  ·通过·从Ho​​stFactory获取USB Host进行Bootstrap。
  ·从Ho​​st访问USB总线,然后从该总线访问USB根集线器(即USB Device)。
  ·获取集线器上可用的USB端口数量,遍历所有端口以找到正确的设备。
  ·访问附加到特定端口上的USB设备。可以用一个设备的PortIdentifier直接从Host访问它,也可以通过从root hub开始遍历USB Bus找到它。
  ·用ControlMessage与该设备直接交互,或者从该设备的当前配置中要求一个接口,并与该接口上可用的端点进行I/O。
  清单1展示了如何使用jUSB API获取USB系统中的内容。这个程序编写为只是查看根集线器上可用的USB设备,但是很这里的逻辑对应于上述步骤1到步骤4。
  清单1.使用jUSB API获取USB系统的内容
  import usb.core.*;
  公共类ListUSB
  {
  公共静态void main(String[]args)
  {
  尝试
  {
  //通过从HostFactory获取USB主机进行引导。
  主机host=HostFactory.getHost();
  //获取主机上可用的USB总线列表。
  Bus[]总线=host.getBusses();
  int Total_bus=总线长度;
  //遍历所有USB总线。
  for(int i=0;i<total_bus;i++)
  {
  //访问USB总线上的根集线器并获取
  根集线器上可用的USB端口数。
  设备根=bus<i>.getRootHub();
  int Total_port=root.getNumPorts();//遍历根集线器
  上所有可用的USB端口。
  需要注意的是
  //编号从1开始,而不是从0开始。
  for(int j=1;j&lt;=total_port;j++)
  {
  //获取连接到端口的设备。
  设备device=root.getChild(j);
  if(device!=null)
  {
  //USB设备可用,请在此处执行操作。
  }
  }
  }
  }catch(Exception e)
  {
  System.out.println(e.getMessage());清单2展示了在应用程序成功地找到了设备的条件下,如何与Interface和EndPoint进行批量I/O。该代码段也可以修改为执行或者控制中断I/O。它对应于上述
  步骤
  5
  清单
  2.使用jUSB API执行批量I/O
  if(device!=null)
  {//获取设备的当前配置以及当前配置下可用的接口
  数量。配置config=device.getConfiguration();int Total_interface=config.getNumInterfaces();//遍历接口for(int k=0;k&lt;total_interface;k++){//访问当前接口并获取该接口上可用的端点数量。接口itf=config.getInterface(k,0);int Total_ep=itf.getNumEndpoints();//遍历所有端点。for(int l=0;l&lt;total_ep;l++){//访问端点,并获取其I/O类型。端点ep=itf.getEndpoint(l);字符串io_type=ep.getType();布尔输入=ep.isInput();//如果端点是输入端点,则获取其//InputStream并读入数据。if(输入){输入流中;in=ep.getInputStream();//在这里读入数据in.close();}//如果Endpoint是输出Endpoint,则获取其//OutputStream并写出数据。别的
  {
  输出流输出;
  输出=ep.getOutputStream();
  //在这里写出数据。
  关闭();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。·获得连接到根集线器的UsbDevices清单。遍历所有低级集线器以找到正确的UsbDevice。·用控制消息(UsbControlIrp)与UsbDevice直接交互,或者从UsbDevice的相应UsbConfiguration中要求一个UsbInterface并与该UsbInterface上可用的UsbEndpoint进行·如果一个UsbEndpoint用于进行I/O,则打开与其关联的UsbPipe。通过这个UsbPipe可以同步或者异步提交上行数据(从USB设备到主机)和下行数据(从主机到USB)·当应用程序不再需要访问该UsbDevice时,关闭这个UsbPipe并释放相应的UsbInterface。在清单3中,我们使用JSR-80 API获取USB系统的内容。该程序间接遍历USB系统上的设备所有USB集线器并查找连接到主机上的所有USB设备。可能代码对应于上述步骤1到步骤3。清单3.使用JSR-80 API获取USB系统的内容import javax.usb.*;导入java.util.List;公共类TraverseUSB{公共静态void main(String argv[]){尝试{
  //访问系统USB服务,并访问root
  //hub。然后遍历根集线器。
  UsbServices服务=UsbHostManager.getUsbServices();
  UsbHub rootHub=services.getRootUsbHub();
  遍历(rootHub);
  }catch(Exception e){}
  }
  public static void traverse(UsbDevice device)
  {
  if(device.isUsbHub())
  {
  //这是一个USB Hub,遍历集线器。
  列表attachedDevices=((UsbHub)device).getAttachedUsbDevices();
  for(int i=0;i&lt;attachedDevices.size();i++)
  {
  遍历((UsbDevice)attachedDevices.get(i));
  }
  }
  else
  {
  //这是USB功能,而不是集线器。
  //做一点事。清单
  4展示了在应用程序成功地找到设备后,如何与Interface和EndPoint进行I/O
  。清单4.使用JSR-80 API进行I/O public static void testIO(UsbDevice device){try{//访问USB设备的活动配置,获取//该配置中可用的所有接口。UsbConfiguration config=device.getActiveUsbConfiguration();列表totalInterfaces=config.getUsbInterfaces();//遍历所有接口,并访问该接口可用的I/O端点。for(int i=0;i&lt;totalInterfaces.size();i++){UsbInterface interf=(UsbInterface)totalInterfaces.get(i);interf.claim();列表totalEndpoints=interf.getUsbEndpoints();for(int j=0;j&lt;totalEndpoints.size();j++){//访问特定端点,确定其数据流的方向//以及数据传输的类型,并打开//I的数据管道/O。UsbEndpoint ep=(UsbEndpoint)totalEndpoints.get(i);int方向=ep.getDirection();int类型=ep.getType();UsbPipe管道=ep.getUsbPipe();管道.open();//此处通过USB管道执行I/O。管道.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功能了。