郑春芳,郑灵翔,石江宏
(厦门大学 信息科学与技术学院 ATR实验室,福建 厦门 361005)
随着无线技术的不断发展,蓝牙产品逐渐走入人们的生活。目前蓝牙在PC端支持良好,应用比较广泛。但在嵌入式系统中,由于蓝牙接口的兼容性问题,使其在嵌入式领域的应用大打折扣。因此,如何在嵌入式系统中支持蓝牙设备成为当前嵌入式领域的研究热点。
作为通用串行总线标准,USB接口以其连接方便、兼容性好和传输速度快等特点被更多的硬件系统支持。针对USB蓝牙适配器在嵌入式Linux下的应用,开发其驱动程序,为蓝牙设备在嵌入式Linux下进一步应用提供前提条件。本文详细阐述USB蓝牙设备的HCI传输层,给出实现方案中关键的函数和数据结构,并在MP2530上实现。
该嵌入系统采用Linux-2.6.20内核版本,采用在X86架构的PC机端上交叉编译,在目标板MP2530上运行。目标板MP2530是专门为嵌入式多媒体处理器MMSP2+设计的开发平台,其内部集成有ARM926EJ和ARM946E的双核,带有2D和3D的图形加速器。此外,MP2530还提供丰富的外围数据接口,包含6路UART通道,USB主机 、USB设备2.0、SD卡读写通道、以太网控制器等。MP2530支持基于通用串行总线1.1版本和开放式主机控制器1.0版本,支持高、低速USB设备。
按照蓝牙协议的逻辑功能,整个蓝牙协议栈分为3个部分[1],如图1所示。
图1 蓝牙协议栈体系结构
底层硬件实块包括射频 RF(Rodio Fraquency)、基带BB(Base Band)和链路管理 LMP(Link Manager Protocol);中间层协议包括逻辑链路控制和适应协议L2CAP(Logical Link Control and Adaptation Layer protocol)、服务发现协议 SDP(Service Discovery Protocol)、串口仿真协议 RFCOMM和电话通信协议TCS-BIN;高层应用包括拨号网络、耳机、局域网访问、文件传输等,分别对应一种应用模式。
在BB与LMP上和L2CAP之间还有一个主机控制接口HCI(Host Controller Interface)层,它提供对下层基带控制器、链路管理器的命令接口,以及对硬件状态和控制注册成员的访问。该接口还提供对蓝牙基带的统一访问模式。
Linux蓝牙协议栈又称BlueZ,是一个开放性的协议。BlueZ采用模块化设计,其组织结构如图2所示。它包含内核和用户态2大模块。其中内核模块是由设备驱动层、蓝牙核心及主机控制接口 HCI(Host Control Interface)层、Bluetooth协议核心、逻辑链路控制和适配协议L2CAP(Logical Link Control and Adaptation Protocol)、SCO 音频层、其他Bluetooth服务组成。而用户态模块则包括BlueZ工具集和蓝牙应用程序。
图2 BlueZ组织结构
对于USB设备,其传输层即USB传输子层是介于主机和主控制器之间,负责主机与主控制器间的数据传输。
从蓝牙的协议层模型可以看出HCI位于蓝牙系统的上层(L2CAP,SDP 和 RFCOMM)和链路管理层(LMP)及基带之间,它为这些上层提供进行LM和BB的统一接口方式。USB蓝牙设备通过模块内的USB设备控制器和总线与主机控制器相连,负责与主机的数据交换,它在USB固件的控制下,将接收到的HCI协议分组交给HCI固件处理或将来自链路管理器和基带控制器的数据传送至主机。HCI固件负责解释从主机接收到的HCI分组并交给链路管理器和基带控制器处理,或采集蓝牙模块各部件的状态信息并递交给主机。主机与蓝牙USB模块关系如图3所示。
1)HCI终端要求 USB蓝牙设备可看作高速设备,其固件配置由2个接口组成。接口0为固定设置,包含BULK和中断终端;接口1提供可扩展的同步带宽占用方式。接口1提供4种设置,以提供基于同步带宽需求的占用方式。其缺省接口为空,可使设备支持非同步带宽。当通过选择接口呼叫调整同步带宽占用方式时,终端可自由选择2种接口,因此任何未处理的BULK或中断事务都无需中止或重新提交。
图3 主机与蓝牙USB模块关系
2)控制终端要求 任何USB蓝牙设备都有一个用于配置和控制该设备控制终端—终端0。终端0允许主机向主控制器发送HCI指令。当USB固件在具有蓝牙类别码的终端上接收一个分组时,它应将该分组视为一个HCI指令分组。
3)BULK终端要求 BULK终端用来传输ACL数据,由于数据完整性及带宽请求,因此需用到BULK终端。BULK具有检错和纠错能力。Linux下的蓝牙设备允许连接多个ACL链路,因此经过此通道的数据流可能来自或发往1个或多个设备数据。为避免阻塞,主控制器采用类似于共享终端模型的流控制模型。默认状态下,BULK最大分组尺寸为64字节,因此BULK通过总线1 ms传输1个或多个64字节的分组帧。
4)中断终端要求 中断终端能够保证事件以可预测并及时的方式传递。事件分组可在一定允许延时条件下通过USB发送。对于蓝牙设备,中断终端的时间间隔为1 ms。USB软件和固件无需对传送到主控制器的事件充分了解。
5)同步终端要求 同步终端用于传递SCO数据,主要是音视频数据。时间是该数据类型的重要因素。它允许数据损坏或丢失,但必须保证有恒定的数据流。USB固件以固定速率将数据内容传递到主控制器的SCO先进先出队列(FIFO)。如果FIFO满,则新数据将覆盖原有数据。蓝牙设备可以支持3个64 kb/s语音信道,可以接收不同编码方式的数据―8 bit或16 bit的线性音频编码。默认状态下,同步终端的最大分组尺寸为64字节。如果无需支持3条16 bit编码的语音信道,最大分组尺寸为32字节。
4.1.1 结构体hci_dev
在嵌入式Linux系统中,任何一种设备在HCI层都被抽象为hci_dev结构体。当蓝牙设备接入系统时,不论以何种方式接入,都要向HCI层或蓝牙核心层注册为一个hci_dev设备。hci_dev结构体主要包含:
1)设备名称(name)、设备地址(bdaddr)、设备标志(flags)、设备类型(type)等设备属性;
2)分组类型(pkt_type)、链接策略(link_policy)、链接模式(link_mode);
3)3 种类型任务队列:CMD任务、发送任务、接收任务;
4)7 种设备操作, 包括 open,close,flush,send,destruct,notify 和 ioctl。
另外还有设备查询状态、查询返回结果、设备锁等。
4.1.2 结构体hci_usb
hci_usb结构体定义了指向hci_dev和usb_device的2个指针。usb_device定义蓝牙usb设备,这样通过这2个指针可将HCI层与蓝牙设备联系起来。hci_usb还定义HCI操作USB设备时所用到的其他数据,诸如数据缓存大小、USB接口、主机端的端点描述符和队列等。
4.1.3 结构体bt_usb_data
结构体bt_usb_data描述USB接口蓝牙模块的数据结构,包含传送数据的缓冲区、I/O端点、消息队列以及缓冲区的消息串和消息串读写位置索引、传送缓冲区的指针以及表示设备是否被激活、是否存在、是否打开等状态标志位。
在嵌入式Linux系统中,所有设备驱动程序,都必须向内核注册其设备驱动。对于蓝牙USB设备驱动注册,该过程首先创建一个指向usb_dirvier类型的结构体hci_usb_driver,具体内容包括指定驱动程序的名字(hci_usb)、探测函数(hci_usb_probe)、断开函数(hci_usb_disconnect)、 中断函数 (hci_usb_suspend)、 恢复函数(hci_usb_resume)、所支持蓝牙设备列表(bluetooth_ids)。 并由函数module_init、hci_usb_init、usb_register 依次 执 行 ,usb_register最 终 调 用usb_register_driver完成驱动向内核注册。
当卸载驱动程序时,要在内核中注销驱动程序,释放其占有的资源。该过 程 与 驱 动 注 册 类 似 , 由 module_exit、hci_usb_exit、usb_deregister依次执行,最终 usb_deregister调用usb_deregister_driver完成驱动注销。
当USB设备插入主机时,主机控制器HCI使用控制通道枚举,并给其分配唯一设备地址(1~127),同时读取该设备的class,subclass和protocol等设备描述符。
如果读取到的设备描述符信息与hci_usb驱动程序注册到USB核心的信息相匹配,则将设备与相应的驱动程序进行绑定。调用hci_usb_driver中指定的探测函数hci_usb_probe,初始化结构体hci_usb中部分数据,并注册HCI设备。蓝牙设备探测函数hci_usb_probe具体探测过程如图4所示。
当蓝牙设备断开连接时,通过调用hci_usb_driver中指定的断开函数hci_usb_disconnect完成。其过程主要包括清空设备的驱动数据,关闭设备,释放与设备相关的资源,并注销内核中相应的HCI设备及释放所占用的资源。
如果要中断蓝牙设备连接 (只是暂时中断,未释放资源),则通过调用中断函数hci_usb_suspend来完成。其过程是保存hci_dev数据信息,中断HCI与设备连接,互斥访问设备等待队列并把数据信息加入设备等待队列的尾部。如果要恢复被中断过的蓝牙设备的连接,可通过调用恢复函数hci_usb_resume来完成。其过程是互斥访问设备等待队列,取出队列头的hci_dev数据,恢复HCI与设备的连接。若错误,则返回I/O error。
把交叉编译过的蓝牙USB设备驱动程序以及必要的程序和库移到根文件系统中,启动开发板,在板上USB接口处插上USB蓝牙设备。
加载USB蓝牙驱动,执行hciconfig,可以看到当然蓝牙USB设备使用状态,如图5所示。
图5 加载后USB蓝牙设备状态
本文在硬件平台MP2530上开发实现嵌入式Linux下蓝牙USB设备驱动程序。实验结果显示,基于该驱动的USB蓝牙设备可与其他蓝牙设备正常通讯,其速度比以K单位计算的串口要快得多。USB蓝牙设备驱动程序的开发,为蓝牙设备与其他硬件设备的连接提供了一种高效便捷的途径,同时大大拓展了蓝牙设备的应用。
[1]金 纯,许光辰,孙 睿.蓝牙技术[M].北京:电子工业出版社,2001.
[2]梁军学,郁 滨.Linux蓝牙协议栈的USB设备驱动[J].计算机工程,2008(5):274-275.
[3]钱志鸿,杨 帆,周求湛.蓝牙技术原理、开发与应用[M].北京:北京航空航天大学出版社,2006.
[4]吴艳玮,任长明.蓝牙HCI USB传输层规范[J].计算机工程,2002(2):235-236.
[5]梁正平,毋国庆,肖 敬.Linux中USB设备驱动程序研究[J].计算机应用研究,2004,21(6):70-72.
[6]巍 骛,张焕强,方贵明.基于Linux的USB驱动程序实现[J].计算机应用,2002,22(8):17-19.