热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

支持基于Linux的USB设备的三种方法

文章标题:支持基于Linux的USB设备的三种方法。Linux是中国IT实验室的一个技术频道。包含桌面应用,Linux系统管理,内核研究,嵌入式系统和开源等一些基本分类

  引言
  
  通用串行总线(USB)是一种快速而灵活地连接配件与计算机工作站的接口,其应用非常广泛。Linux中除了包含对USB主机控制器的驱动,还含有USB设备控制器,尤其是集成在Strongarm SA1110处理器上的控制器的驱动。这些控制器驱动通过使用USB可使基于Linux的嵌入式系统与主机 (运行的可以是Linux,或不是)进行通信。这里提供三种方法给运行Linux操作系统的嵌入式系统增加USB支持,可采用其中一种与USB主机展开通信。
  第一种,最复杂的设备采用专门编写的内核模块解析标准USB总线上通行的错综复杂的高层协议;相应的USB主机定制驱动和应用程序来完成连接。第二种,有些基于Linux的设备把总线当作一种简单的运行在主机上的点对点串行连接使用;主机应用程序采用主机操作系统提供的USB编程界面,而其外在表现则仿佛是在通过一种典型的串行端口进行通信。第三种,另有一些设备把USB看作一种以太网络,它们用主机作网关,把USB设备与办公LAN或 Internet相连接。通常的做法是使用专门的主机驱动实现它。
  最佳方案的选择取决于研发所需时间,以及针对具体嵌入式应用,要把USB接口作成什么样。以下对这三种方法如何在基于Linux的USB设备上的应用逐一进行描述。本文是关于如何在基于Linux的照相机和PDA之类的USB设备上使用Linux的论述,在此,USB是指由方形连接器而非扁平矩形连接器构成的USB设备。
  
  内核模块
  
  把USB加到基于Linux的设备上的第一种方法是编写一个定制的Linux内核模块。这种方法通常要求相应开发主机操作系统(Windows、Linux以及其它OS)的驱动。
  借助定制内核模块在设备中的安装,可以进行文件系统仿真等,使嵌入式应用将其USB主机当作远程存储设备对待。这一方法的另一潜在用途是构成一种存储转发字符设备,从嵌入式应用程序中缓冲数据流,直到USB主机连接完成建立为止。
  对于基于Strongarm的Linux设备,其USB应用内核模块调用sa1100_usb_open(),对管理芯片的板上USB设备控制器外设的内核代码进行初始化。然后该模块调用sa1100_usb_get_descriptor_ptr()和 sa1100_usb_set_string_descriptor(),通过枚举过程对USB主机的给定USB描述符进行设置。这些描述符包括设备供货商及产品的数字标识符、正文字符串等主机可用来对设备进行识别的信息。甚至有一个序列号域,以便主机唯一地识别设备或对USB上相同设备的多个实例加以区分。
  内核模块必须在开始USB通信前完成USB描述符的建立,这是因为枚举过程由USB设备控制器驱动,一旦USB主机连上后会自动执行。一切准备就绪后,USB设备模块便调用sa1100_usb_start(),告诉内核接受来自主机的USB连接请求。如果模块在USB主机连上前调用 sa1100_set_configured_ callback(),那么内核将会在枚举过程结束时调用所提供的回调函数。回调函数能很好地对设备完成连接状态进行可视化指示。
  如果USB通信不再需要,那么设备的内核模块便调用sa1100_usb_stop(),然后是 sa1100_usb_close(),关闭SA1100的USB控制器。
  Strongarm USB控制器支持数据传输作业的bulk-in 和bulk-out。在从USB主机接收数据包时,内核模块调用sa1100_usb_recv(),把数据缓冲区和回调函数地址传递给它。然后内核的底层USB设备控制代码对来自主机的bulk-out包进行检索,把内容放于缓冲区中,并调用回调函数。
  回调函数必须从接收缓冲区提取数据并保存于其它位置或者把缓冲区空间加到一个队列中,为下一个数据包的接收分配新的缓冲区。而后回调函数二次调用sa1100_usb_recv(),在需要时进行下一个数据包的接收。过程与对USB主机的数据传输相类似。在聚集起一帧的数据量后,内核模块将数据的地址、长度和回调地址传递给 sa1100_usb_send()。传输完成时,内核调用回调函数。
  主机端USB驱动的几个例子在主流的Linux版本以及 Linux内核档案组织分配的原始内核源中都有提供。用于Handspring Visor(drivers/usb/serial/visor.c)的模块是编写较为简洁易懂的模块之一,作为USB主机端模块的模板 (drivers/usb/usb-skeleton.c)使用。
  
  高速串行
  
  对于大多数实际应用来说, 可以把USB总线当作一种高速串行端口考虑。如此在某些类型的嵌入式设备和应用中对它进行原型模拟是有意义的。Strongarm处理器的Linux内核提供现成的USB设备驱动专工于此,称作usb-char。
  在希望与USB主机通信时,Linux USB设备应用程序只是打开对其usb-char设备节点(字符型,最大10,最小240)的连接,然后开始读写数据即可。read()和 write()操作将一直返回错误值直到USB主机连上为止。一旦连接建立和枚举完成,便开始通信,就像USB是一种点对点串行端口一样。
  由于这种USB数据传递方法十分直接且实用,因此usb-char设备得到高效使用。它还为其它USB通信方法的实现提供了重要的参照基准。
  usb-char的实际动作从usbc_open()功能开始,部分内容示于列表1中。
  列表1:打开USB上的串行连接
  static int usbc_open(struct inode *pInode, struct file *pFile)
  {
  int retval = 0;
  /* start usb core */
  sa1100_usb_open(_sb-char?;
  /* allocate memory for in-transit USB packets */
  tx_buf = (char*) kmalloc(TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA);
  packet_buffer = (char*) kmalloc(RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA);
  /* allocate memory for the receive buffer; the contents of this
  buffer are provided during read() */
  rx_ring.buf = (char*) kmalloc(RBUF_SIZE, GFP_KERNEL);
  /* set up USB descriptors */
  twiddle_descriptors();
  /* enable USB i/o */
  sa1100_usb_start();
  /* set up to receive a packet */
  kick_start_rx();
  return 0;
  twiddle_descriptors ()功能建立起设备的USB描述符。在描述符全部建起后,准备从USB主机枚举并接收一个数据帧。kick_start_rx()所需的代码大多数情况下只是一种对sa1100_usb_recv() 的调用以建立回调而已。当USB主机发送数据包时,设备的内核通过回调调用rx_done_callback_packet_buffer()函数,把数据包的内容移入usb-char 设备点上由read()返回的FIFO队列。
  
  主机
  
  对于运行Linux的USB主机,usb-char相应的USB主机模块称为usbserial模块。大多数Linux版本都包括Usbserial模块,尽管通常不是自动装入。在USB与设备的连接建立之前,usbserial 由modprobe 或 insmod载入。
  一旦USB设备开始枚举,主机上的应用程序便用usbserial设备点(字符型,最大188,最小0以上)之一与设备进行通信。这些节点通常命名为/dev/ttyUSBn。Usbserial模块在内核报文日志记录中报告它把哪个节点指定给USB设备使用:
  usbserial.c: 通用转换器删除
  usbserial.c: 通用转换器当前连到ttyUSB0上。连接建立后,USB主机上的应用程序便通过读写指定的节点与USB设备进行通信。
  Linux 主机上usbserial模块的一种替代选择是一种称作libusb(libusb.sourceforge.net)的库。这种库使用低层内核系统调用进行USB数据传输,而不是通过usbserial模块,在某种程度上跨Linux内核版本建立和使用时更方便。Libusb库还提供大量有用的调试功能,这一点在对运行在USB链路上的复杂通信协议进行除错时有帮助。用libusb与采用usb-char的USB设备进行通信时,Linux主机应用程序使用usb_open()函数建立与该设备的连接。然后应用程序使用usb_bulk_read()和usb_bulk_write()与设备交换数据。
  
  USB上的以太网
  
  另一种选择是把USB作为一种以太网络来对待。Linux具有在主机和设备端均可实现这种功能的模块。由于iPAQ硬件既没有可接入的串行端口也没有一种专用的网络接口,因此,iPAQ 的Linux内核专门采用这种通信策略,在StrongARM的Linux内核中,usb-eth模块(arch/arm/mach- sa1100/usb-eth.c)对用USB作物理媒介的虚构以太网设备进行仿真。一旦创建后,这一网络界面便被指定一个IP地址,否则作为通常的以太网硬件对待。一旦USB主机连上后,usb-eth模块便能使USB设备“看到” Internet(如果存在Internet的话),ping测其它IP地址,甚至“谈论”DHCP, HTTP, NFS, telnet, 和e-mail。简言之,任何在实际的以太网界面上运行的应用将不折不扣地在usb-eth接口上得到实现,因为它们不能分辨出其正在使用的不是实在的以太网硬件。
  在Linux主机上,相应的Ethernet-over-USB内核模块称为usbnet。当usbnet模块得到安装且设备的USB连接建立完成时,usbnet模块便针对主机端内核及用户应用创建一个与实际硬件酷似的虚构以太网界面,主机端应用程序通过运行设备IP地址 ping测,可以检查USB设备的存在。如果ping测成功,设备便加上了。
  
  结语
  
  Linux不再只是USB主机使用,当今它也是USB设备的合适选择,Linux 下的USB通信是非常灵活和易用的。
推荐阅读
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • windows便签快捷键_用了windows十几年,没想到竟然这么好用!隐藏的功能你知道吗?
    本文介绍了使用windows操作系统时的一些隐藏功能,包括便签快捷键、截图功能等。同时探讨了windows和macOS操作系统之间的优劣比较,以及人们对于这两个系统的不同看法。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • 如何在联想win10专业版中修改账户名称
    本文介绍了在联想win10专业版中修改账户名称的方法,包括在计算机管理中找到要修改的账户,通过重命名来修改登录名和属性来修改显示名称。同时指出了windows10家庭版无法使用此方法的限制。 ... [详细]
author-avatar
suny
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有