[硬件驱动_蓝牙]蓝牙Bluez的编程实现
蓝牙的各个协议栈的简介
1.1、蓝牙技术
蓝牙(Bluetooth)技术是由Ericsson、IBM、Intel、Nokia和Toshiba公司于1998年5月共同提出开发的,并联合成立了蓝牙特殊利益小组(SIG),负责开发无线协议规范并设定交互操作的需求。其本质是设备间的无线链接,意在于代替有线电缆。
1.1、蓝牙协议栈
协议栈是指一组协议的集合,举个例子,把大象装到冰箱里,总共要3步。每步就是一个协议,3步组成一个协议栈。把应用层数据包发出去,也要好几步,TCP/UDP头,IP头,ether头,每步也是一个协议。另外每层都有一些特殊的协议。所有这些统称协议栈。蓝牙协议栈就是SIG定义的一组协议的规范,目标是允许遵循规范的蓝牙应用能够进行相互间操作,如图1.1蓝牙协议栈
图1.1 蓝牙协议栈
在蓝牙协议体系中,底层、中间层、应用层按序排列构成了蓝牙协议栈,如左图所示。底层(硬件层)和中间协议层(软件层)之间 的接口使用主机控制器接口(HCI)。HCI是软硬件之间必不可少的接口,其功能是解释并传递两层之间的消息和数据。软件通过HCI调用底层LMP/BB和RF等硬件。HCI以下的功能由蓝牙设备实施;HCI以上的功能由软件运行,在主机上实现。HCI对于上、下两层数据的传输都是透明的。
在蓝牙协议栈中,最主要的是蓝牙核心协议,包括基带协议(BP)、链路管理协议(LMP)、链接控制和适配协议(L2CAP)、服务发现协议(SDP)等。蓝牙设备基本上都需要核心协议,其他协议则按蓝牙设备的需要而选定。
1.2、蓝牙技术的特点
1.2.1、蓝牙协议栈体系结构
整个蓝牙协议体系结构可分为底层硬件模块、中间协议层和高端应用层三大部分。链路管理层(LMP)、基带层(BBP)和蓝牙无线电信道构成蓝牙的底层模块。BBP层负责跳频和蓝牙数据及信息帧的传输。
LMP层:负责连接的建立和拆除以及链路的安全和控制,它们为上层软件模块提供了不同的访问人口,但是两个模块接口之间的消息和数据传递必须通过蓝牙主机控制器接口的解释才能进行。也就是说,中间协议层包括逻辑链路控制与适配协议(L2CAP)、服务发现协议(SDP)、串口仿真协议(RFCOMM)和电话控制协议规范(TCS)。
L2CAP:完成数据拆装、服务质量控制、协议复用和组提取等功能,是其他上层协议实现的基础,因此也是蓝牙协议栈的核心部分。
SDP:为上层应用程序提供一种机制来发现网络中可用的服务及其特性。在蓝牙协议栈的最上部是高端应用层,它对应于各种应用模型的剖面,是剖面的一部分。目前定义了13种剖面。
1.2.2、蓝牙协议栈低层模块
蓝牙的低层模块是蓝牙技术的核心,是任何蓝牙设备都必须包括的部分。
蓝牙工作在2.4GHZ的ISM频段。采用了蓝牙结束的设备讲能够提供高达720kbit/s 的数据交换速率。
蓝牙支持电路交换和分组交换两种技术,分别定义了两种链路类型,即面向连接的同步链路(SCO)和面向无连接的异步链路(ACL)。
为了在很低的功率状态下也能使蓝牙设备处于连接状态,蓝牙规定了三种节能状态,即停等(Park)状态、保持(Hold)状态和呼吸(Sniff)状态。这几种工作模式按照节能效率以升序排依次是:Sniff模式、Hold模式、Park模式。
蓝牙采用三种纠错方案:1/3前向纠错(FEC)、2/3前向纠错和自动重发(ARQ)。前向纠错的目的是减少重发的可能性,但同时也增加了额外开销。然而在一个合理的无错误率环境中,多余的投标会减少输出,故分组定义的本身也保持灵活的方式,因此,在软件中可定义是否采用FEC。一般而言,在信道的噪声干扰比较大时蓝牙系统会使用前向纠错方案,以保证通信质量:对于SCO链路,使用1/3前向纠错;对于ACL链路,使用2/3前向纠错。在无编号的自动请求重发方案中,一个时隙传送的数据必须在下一个时隙得到收到的确认。只有数据在收端通过了报头错误检测和循环冗余校验(CRC)后认为无错时,才向发端发回确认消息,否则返回一个错误消息。
蓝牙系统的移动性和开放性使得安全问题变得及其重要。虽然蓝牙系统所采用的调频技术就已经提供了一定的安全保障,但是蓝牙系统仍然需要链路层和应用层的安全管理。在链路层中,蓝牙系统提供了认证、加密和密钥管理等功能。每个用户都有一个个人标识码(PIN),它会被译成128bit的链路密钥 (Link Key)来进行单双向认证。一旦认证完毕,链路就会以不同长度的密码(Encryphon Key)来加密(此密码以shit为单位增减,最大的长度为128bit)链路层安全机制提供了大量的认证方案和一个灵活的加密方案(即允许协商密码的长度)。当来自不同国家的设备互相通信时,这种机制是极其重要的,因为某些国家会指定最大密码长度。蓝牙系统会选取微微网中各个设备的最小的最大允许密码长度。例如,美国允许128bit的密码长度,而西班牙仅允许48bit,这样当两国的设备互通时,将选择48bit来加密。蓝牙系统也支持高层协议栈的不同应用体内的特殊的安全机制。例如两台计算机在进行商业卡信息交流时,一台计算机就只能访问另一台计算机的该项业务,而无权访问其他业务。蓝牙安全机制依赖PIN在设备间建立信任关系,一旦这种关系建立起来了,这些PIN就可以存储在设备中以便将来更快捷地连接。
1.2.3、软件模块
L2CAP是数据链路层的一部分,位于基带协议之上。L2CAP向上层提供面向连接的和无连接的数据服务,它的功能包括:协议的复用能力、分组的分割和重新组装(Segmentation And Reaassembly)以及提取(Group Abstraction)。L2CAP允许高层协议和应用发送和接受高达64K Byte的数据分组。
SDP为应用提供了一个发现可用协议和决定这些可用协议的特性的方法。蓝牙环境下的服务发现与传统的网络环境下的服务发现有很大的不同,在蓝牙环境下,移动的RF环境变化很大,因此业务的参数也是不断变换的。SDP将强调蓝牙环境的独特的特性。蓝牙使用基于客户/服务器机制定义了根据蓝牙服务类型和属性发现服务的方法,还提供了服务浏览的方法。
RFCOMM是射频通信协议,它可以仿真串行电缆接口协议,符合ETSI0710串口仿真协议。通过RFCOMM,蓝牙可以在无线环境下实现对高层协议,如PPP、TCP/IP、WAP等的支持。另外,RFCOMM可以支持AT命令集,从而可以实现移动电话机和传真机及调制解调器之间的无线连接。
蓝牙对语音的支持是它与WLAN相区别的一个重要的标志。蓝牙电话控制规范是一个基于ITU-T建议Q.931的采用面向比特的洗衣,它定义了用于蓝牙设备间建立语音和数据呼叫的呼叫控制信令以及用于处理蓝牙TCS设备的移动性管理过程。
1.3、蓝牙的一些Profile
蓝牙里面profile的定义,profile既 是配置文件,配置文件定义了可能的应用,蓝牙配置文件表达了一般行为,蓝牙设备可以通过这些行为与其它设备进行通信。蓝牙技术定义了广泛的配置文件,描述 了许多不同类型的使用案例。按照蓝牙规格中提供的指导,开发商可以创建应用程序以与其它符合蓝牙规格的设备协同工作。 到目前为止,蓝牙一共有22个profile,在www.bluetooth.com§上有详细的文档说明。
已经实现了的协议栈:
Widcomm: 第一个windows上的协议栈,由Widcomm公司开发,也就是现在的Broadcom .
Microsoft Windows stack: Windows XP SP2中包括了这个内建的协议栈,开发者也可以调用其API开发第三方软件。
Toshiba stack: 它也是基于Windows的,不支持第三方开发,但它把协议栈授权给一些laptop商)。它支持的Profile有: SPP, DUN, FAX, LAP, OPP, FTP, HID, HCRP, PAN, BIP, HSP, HFP , A2DP, AVRCP, GAVDP)
BlueSoleil: 著名的IVT公司的产品,这个应该是个中国公司。该产品可以用于桌面和嵌入式,他也支持第三方开发,DUN, FAX, HFP, HSP, LAP, OBEX, OPP, PAN SPP, AV, BIP, FTP, GAP, HID, SDAP, and SYNC。
Bluez: Linux官方协议栈,该协议栈的上层用Socket封装,便于开发者使用,通过DBUS与其它应用程序通信。
Affix: NOKIA公司的协议栈,在Symbian系统上运行。
BlueDragon:东软公司产品,支持的Profile:SDP、Serial-DevB、AVCTP、AVRCP-Controller、AVRCP-Target、Headset-AG、Headset-HS、OPP-Client、OPP-Server、CT-GW、CT-Term、Intercom、FT-Server、FT-Client、GAP、SDAP、Serial-DevA、AVDTP、GAVDP、A2DP-Source、A2DP-Sink。
BlueMagic:美国Open Interface 公司for portable embedded divce的协议栈,iphone(apple),nav-u(sony)等很多电子产品都用该商业的协议栈,BlueMagic 3.0是第一个通过bluetooth 协议栈1.1认证的协议栈,那么我现在就在用它,那么该栈用起来简单,API清晰明了。实现了的profile有:HCI,L2CAP,RFCOMM,A/V,Remote,Control,A/V,Streaming,BIP,BPP,DUN,FAX,FTP,GAP,Hands-Free,and,Headset,HCRP,HID,OBEX,OPP,PAN,BNEP,PBAP,SAP,SPP,Synchronization,SyncML,Telephony,XML.
BCHS-Bluecore Host Software: 蓝牙芯片CSR的协议栈,同时他也提供了一些上层应用的Profile的库。
Windows CE:微软给Windows CE开发的协议栈,但是windows ce本身也支持其它的协议栈。
BlueLet:IVT公司for embedded product的清量级协议栈。
2、Bluez和D-Bus
2.1、Bluez和D-Bus体系结构
The BlueZ D-Bus interfaces aim to provide seamless Bluetooth technology integration into the desktop. A central Bluetooth daemon "hcid"(planned to be renamed to bluetoothd) is responsible for take care of all tasks that can’t or shouldn’t be handled inside the Linux kernel. These jobs include PIN code and link key management for the authentication and encryption, caching of device names and services and also central control of the Bluetooth hardware. The interface exported allows to abstract the internals of GNOME, KDE, Maemo, OpenMoko, ... applications from any technical details of the Bluetooth specification. Even other application will get access to the Bluetooth technology without any hassle.
Bluez和D-bus接口,提供了蓝牙技术和桌面系统的完美集成。蓝牙的中心守护进程hcid的职责就是处理那些不能被linux内核处理的任务,包括处理为鉴权和加密过程中需要的PIN码和密钥、缓存设备的名称和服务类型,同时也是蓝牙硬件的控制中心。
The BlueZ D-Bus services are exported through the system message bus. Every D-Bus enabled desktop has a system message bus instance running. This bus is used to broadcast system events, such as new hardware devices, network connection status, and so forth. The session message bus is not suitable for this architecture since the Bluetooth hardware/connections are shared by all desktop sessions.
Bluez和D-bus服务通过系统消息总线提供。每个D - Bus使桌面有一个系统消息总线实例运行。这个bus是用来广播系统时间,如新的硬件设备、网络连接状态等等。会话消息总线对这种体系结构是不适合的,因为蓝牙硬件/连接被所有桌面会话共享。
The BlueZ D-Bus Architecture goal are:
- Abstract Bluetooth HCI commands/events。
- Provide an easy interface to setup Bluetooth adapter and manage the services
Bluez和D-bus体系结构的目标:
- 抽象hci层命令和事件。
- 提供简单的接口来启动蓝牙适配器和管理蓝牙服务。
The hcid is the main entity of the architecture. It implements methods to setup the Bluetooth adapters, retrieve remote device properties, control the pairing procedure and control the services registration/searches. The following figure shows a high level relationship between the entities.
Hcid是该体系结构的主体。它实现启动蓝牙适配器的方法、获取远端设备属性、控制配对过程和控制服务的注册和搜索。下图显示了一个高层次的实体之间的关系。
2.2、D-Bus介绍
什么是D-Bus?
D-BUS 是一种进程间通信的方式,从架构上来说,分为三层:
- 一个库,libdbus,允许2个进程间交换信息。
- 一个消息总线守护进程, 它使用libdbus库。其他进程都可以与它连接。它可以将消息从一个进程发给另外任意数量的其他进程。现在有一些基于特定应用框架的dbus库函数封装,例如libdbus-glib 和libdbus-qt,也有与一些语言绑定的形式,例如Python等。这些封装的API旨在令D-BUS编程更加简单,libdbus倾向于提供更低层次的调用。很多libdbus API只在绑定的组件中可用。
- libdbus仅支持一对一的连接,就像原始 socket通讯方式一样。但它传递的不是以字节为单位的数据流,而是具有一定意义的消息包。消息的消息头部表示消息种类,消息体用来装载数据。 Libdbus也可以允许实现特定的传输通道,从而来完成比如像认证之类的应用细节(libdbus also abstracts the exact transport used (sockets vs. whatever else), and handles details such as authentication.)。
消息总线守护进程将D-bus上连接的所有程序构成一个轮形hub。Libdbus为中心,它和应用程序建立一对一的连接。每个应用程序通过通道发送消息到消息总线,然后总线进程将消息转发到其他连接到hub的应用程序。可以把消息总线理解为一个路由器。Dbus服务在一个操作系统中存在多个进程。第一个进程是一个全局进程,就如sendmail 或Apache 的系统守护进程一样。这个进程具有高度的安全限制,一般用于系统进程间的通讯。其他的dbus进程都是用户进程,针对于每个登录的用户建立。这些实例允许用户会话中的应用程序相互通信。Dbus全局进程和用户进程是相互独立的,他们并没有内在的依赖关系。
D-Bus应用
有很多种IPC或者网络通信系统,如:CORBA,DCE,DCOM,DCOP,XML-RPC,SOAP,MBUS,ICE等。Dbus的目的主要是下面两点:
- 在同一个桌面会话中,进行桌面应用程序之间的通讯。
- 桌面程序和内核或者守护进程之间通信。
D-Bus概念
对象路径(Native Objects and Object Paths):D-Bus的底层接口,和libdbus相关,它提供一种叫对象路径(object path),用于让高层接口绑定到各个对象中去,允许远端应用程序指向他们。Object path就像一个文件路径。
方法和信号(Methods and Signals):每个对象都有一些成员,有两种成员:方法(methods)和信号(signals),在对象中,方法可以被调用。信号会被广播,感兴趣的对象可以处理这个信号,同时信号中也可以带有相关的数据。
接口(Interfaces):每个对象都有一个或者多个接口,一个接口就是多个方法和信号的集合。这个概念和Glib, Qt或者Java中的是一致的。接口定义了对象实例的类型。dbus使用简单的命名空间字符串来表示接口,如org.freedesktop.Introspectable。可以说dbus接口相当于C++中的纯虚类。
代理(Proxies):使用代理对象就是让调用者感觉在直接使用远程对象一样。d-bus的底层接口完成了一些比较低级和繁琐的调用过程,比如必须先调用创建方法形成消息包,然后发送,然后等待接受和处理返回的消息。所以,高层的接口就可以使用代理对象提供的接口屏蔽这些细节。所以,当调用代理对象的方法时,代理内部会转换成dbus的方法调用,等待消息返回,对返回结果解包,返回给相应的方法。可以看看下面的例子,使用dbus底层接口编写的代码:
Message message = new Message(”/remote/object/path”, “MethodName”, arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {
} else {
Object returnValue = reply.getReturnValue();
}
使用代理对象编写的代码:
Proxy proxy = new Proxy(getBusConnection(), “/remote/object/path”);
Object returnValue = proxy.MethodName(arg1, arg2);
客户端代码减少很多。
总线名称(Bus Names):当一个应用程序连接上bus daemon时,daemon会分配一个唯一的名字给它。以冒号(:)开始,这些名字在daemon的生命周期中是不会改变的,可以认为这些名字就是一个 IP地址。当这个名字映射到应用程序的连接上时,应用程序可以说拥有这个名字。同时应用可以声明额外的容易理解的名字,比如可以取一个名字 com.mycompany.TextEditor,可以认为这些名字就是一个域名。其他应用程序可以往这个名字发送消息,执行各种方法。
名字还有第二个重要的用途,可以用于跟踪应用程序的生命周期。当应用退出(或者崩溃)时,与bus的连接将被OS内核关掉,bus将会发送通知,告诉剩余的应用程序,该程序已经丢失了它的名字。名字还可以检测应用是否已经启动,这可以用来实现单实例启动程序。
地址(Addresses):使用d-bus的应用程序既可以是server也可以是client,server监听到来的连接,client连接到server,一旦连接建立,消息就可以流转。如果使用dbus daemon,所有的应用程序都是client,daemon监听所有的连接,应用程序初始化连接到daemon。dbus地址指明server将要监听的地方,client将要连接的地方,例如,地址:unix:path=/tmp/abcdef表明 server将在/tmp/abcdef路径下监听unix域的socket,client也将连接到这个socket。一个地址也可以指明是TCP /IP的socket,或者是其他的。
当使用bus daemon时,libdbus会从环境变量中(DBUS_SESSION_BUS_ADDRESS)自动认识“会话daemon”的地址。如果是系统 daemon,它会检查指定的socket路径获得地址,也可以使用环境变量(DBUS_SESSION_BUS_ADDRESS)进行设定。当dbus中不使用daemon时,需要定义哪一个应用是server,哪一个应用是client,同时要指明server的地址,这不是很通常的做法。
D-bus工作原理
Calling a Method – Behind the Scenes
在dbus中调用一个方法包含了两条消息,进程A向进程B发送方法调用消息,进程B向进程A发送应答消息。所有的消息都由daemon进行分派,每个调用的消息都有一个不同的序列号,返回消息包含这个序列号,以方便调用者匹配调用消息与应答消息。调用消息包含一些参数,应答消息可能包含错误标识,或者包含方法的返回数据。
方法调用的一般流程:
1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6.目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口,方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。
bus daemon不对消息重新排序,如果发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并需要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号可以与应答消息进行配对。
Emitting a Signal – Behind the Scenes
在dbus中一个信号包含一条信号消息,一个进程发给多个进程。也就是说,信号是单向的广播。信号可以包含一些参数,但是作为广播,它是没有返回值的。
信号触发者是不了解信号接受者的,接受者向daemon注册感兴趣的信号,注册规则是”match rules”,记录触发者名字和信号名字。daemon只向注册了这个信号的进程发送信号。
信号的一般流程如下:
- 当使用dbus底层接口时,信号需要应用自己创建和发送到daemon,使用dbus高层接口时,可以使用相关对象进行发送,如Glib里面提供的信号触发机制。
- 信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其他参数。
- 任何进程都可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
- daemon检测信号,决定哪些进程对这个信号感兴趣,然后把信号发送给这些进程
- 每个进程收到信号后,如果是使用了dbus高层接口,可以选择触发代理对象上的信号。如果是dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。
2.3、Bluez的安全接口
pin_helper concept has been removed starting with bluez-utils 3.X. and has been replaced with a feature called passkey agents. An application that wants to handle passkey requests must use the "hcid" security interface to register a passkey agent. Currently, two types of passkey agents are supported: default and device specific. A "specific" passkey agent handles all passkey requests for a given remote device while a default handles all requests for which a specific agent was not found. "specific" passkey agents are useful to address pre-defined passkey values or environments where the user interaction is not allowed/difficult.
When the CreateBonding method is called the "hcid" daemon will verify if there is a link key stored in the file system. If it is available an error is returned, and if not, a D-Bus message is sent to the registered passkey agent asking for a passkey.
Each Passkey Agent is represented by a D-Bus object path. The "hcid" distinguishes the agents based on their unique bus names and their object paths.
Pin_help的理念在bluez-util 3.x时已经被移除,并被密钥代理所代替。任何想处理密钥请求的应用程序必须使用hcid安全接口来注册密钥代理。现在支持两种类型的密钥代理:默认和设备特定。一个"特定"的密钥代理处理所有远端的密钥请求,一个默认的密钥代理,处理特定的代理所没有发现的所有密钥请求。"特定"的密钥代理对处理那些和用户交互困难或者不允许用户交互的设备的预定义的密钥值或环境非常有用。
当CreateBonding方法被调用时,hcid守护进程将确认当前的文件系统中是否保存了链接密钥,若可以找到,就返回一个错误。若找不到,D-Bus消息将被发出来为密钥请求的设备,注册一个密钥代理。每个密钥代理被D-Bus对象路径所体现,hcid依据唯一的总线名称和对象路径来区分代理。
Architecture
- Step 1: Represents the passkey agent registration
- Step 2: Represents a client calling CreateBonding
- Step 3: Represents the hcid asking for a passkey valu
体系结构
- 第一步:表示密钥代理注册
- 第二步:表示客户调用CreateBonding
- 第三步:代表hcid请求密钥值。
Message Flow
In the following figure, the "CreateBonding" method call is hidden. The "PIN Request" HCI event is generated when there is not a link available in the file system. In this case "Link Key Request Negative Reply" command is sent triggering the "Pin Request" event.
在下面得图表中,CreateBonding方法的调用被隐藏。当没有一个可用链接时,"PIN Request"HCI层事件被产生,在这种情况下,"Link Key Request Negative Reply"命令被发送来回应"Pin Request" 事件。
- Step 1: Represents the D-Bus message sent to register the default/device specific passkey agent.
- Step 2: Represents the HCI "PIN Request" event sent by the Bluetooth Host Controller.
- Step 3: Represents the D-Bus message sent to the default/device speficic passkey agent requesting a passkey.
- Step 4: Represents the "Auth Complete" event where the status contains "LMP Response Timeout"(The remote didn't type the passkey).
- Step 5: Represents the "hcid" issuing a "Cancel" to a previous Request call.
- Step 6: Represents the D-Bus message sent to release the passkey agent: basically sent when the hcid exits.
- 第一步:D-Bus发送消息来注册默认的或者特定的密钥代理。
- 第二步:蓝牙主机控制器发送HCI 层"PIN Request"事件。
- 第三步:D-Bus发送消息到默认的或者特定的密钥代理,请求密钥。
- 第四步:"Auth Complete"完成事件,如果这种状态包含"LMP Response Timeout"。
- 第五步:hcid发出“Cancel”命令给D-Bus。
- 第六步:当hcid退出时,D - Bus的信息发送到释放密钥代理
2.4、Bluez适配器接口
Description?
The "Adapter" interface provides methods to setup the local adapter(s), search remote devices, pairing and search for services.
This interface is considered stable and no change is planned.
For each available adapter the hcid register an adapter object instance provided in the path "/org/bluez/hci{0, 1, 2, ...}".
适配器接口提供,启动适配器、搜索设别、配对、和搜索服务的方法。这个接口被认为是稳定的,没有改变计划。对每个可用的适配器,hcid为其注册一个适配器对象实例,在路径 "/org/bluez/hci{0, 1, 2, ...}"里。
2.5、Bluez配对
Description
Services that need ask the user to accept/reject an operation such as accept OBEX objects or accept an incoming connection can use the Security API to request the userspace registered authorization agent responsible.
Development warnings/recommendations
- Just one Authorization request per time is allowed.
- NotConnected error is returned if a connection is not found between the devices is not found.
Trusted Devices
The BlueZ daemon keeps a list of trusted devices. Trusted means that authorization is not required to accept incoming connections or other operations that need the user response. Once a given device is added to the list, the BlueZ daemon will reply authorized without call the Authorization agent.
Authorization Agent
Authorization agents are applications responsible for address authorization requests. For more information check the BlueZ D-Bus API and implementation references:
- utils/daemon/auth-agent.c is a authorization agent implementation able to handle device specific and default
- bluez-gnome passkey/authorization agent implementation distributed by the BlueZ community
Canceling
For security reason, only the requestor can cancel a pending authorization operation.
2.6、Bluez绑定
Description
The purpose of bonding is to create a relation between two Bluetooth devices based on a common link key (a bond). The link key is created and exchanged(pairing) during the bonding procedure and is expected to be stored by both Bluetooth devices, to be used for future authentication[definition from Bluetooth Core Spec].
The Bonding procedure is done through the BlueZ Adapter interface.
Development warnings/recommendations
- Just one Bonding request per time is allowed.
- Bonding is not allowed if there is a discovery running.
- Pending remote name is canceled if a CreateBonding message is received.
- The Bonding takes some seconds, therefore it is recommended set a D-Bus callback to handle the message reply for this operation.
Passkey Agent
Currently, two types of passkey agents are supported:
- Device Specific: handles all passkey requests for a given remote device
- Default: Handles the remaining requests(not addressed by device specific agents)
For more information check the BlueZ D-Bus API and implementation references:
- utils/daemon/passkey-agent.c is a passkey agent implementation able to handle device specific and default
- bluez-gnome passkey agent implementation distributed by the BlueZ community=
3、Bluez编程实现
3.1、蓝牙开发关键技术剖析
3.1.1、连接机制分析
物理信道(physical channel)是 蓝牙系统的最底层结构,通过伪随机跳频序列、发送时槽定时、接入码及帧头编码来表征。蓝牙针对不同应用定义了一系列物理信道,包括用于匹克网内设通信的匹 克网物理信道、用于寻呼设备的寻呼扫描物理信道和用于查找设备的查找扫描物理信道。两台设备必须采用相同的物理信道才能进行通信。
主从设备建立连接的过程就是建立相同匹克网信道的过程,该过程确保主从设备以同样的定时和次序进行载波频率的跳变,进行数据传输,同时可以根据匹克网接入码和帧头编码进行数据过滤和解析,避免和其他设备在同一个频段上的相撞。
寻呼扫描物理信道(page scan physical channel)用于主设备寻呼从设备,是设备建立连接的必经阶段。寻呼扫描跳频序列和寻呼请求帧的设备接入码(DAC)是由从设备物理地址运算,处于可被连接模式的从设备以固定的周期(由page scan interval决定)在一个固定的时间窗(由page scan window决定)内以某个跳频频率监听主设备的寻呼请求。
3.1.2、自动连接
为了方便用户的使用,大多蓝牙设备都实现了自动连接功能,根据以上对蓝牙连接机制的分析,设计自动连接方案时必须考虑设备在不同的工作状态下采用不同的物理信道和跳频序列,而不能按照人为的逻辑随意设置,否则会给用户带来不便。
以车载免提装置为例,上电自动连接对驾驶员来说可以提高使用车载免提的自觉性,降低行车期间通话带来的风险。采用搜索方式判断设备是否在有效范围内,按照优先级从低到高连接,最后一次连接的手机为优先级最高的,然后按照配对列表的逆序而优先级依次降低。链路丢失后的自动连接只针对最后一部手机,这样可以实现服务的连续性。
3.1.3、时钟设计
蓝牙核心规范要求时钟频率精度为±20ppm,如果载波频率不稳定,则会发生“过零点”错误(zero-cross)。载波频率由本地时钟(晶振)做为PLL的参考时钟倍频产生。一般说来,蓝牙设备的时钟设计便是指晶振电路的设计及微调。决定晶振工作精度的两个重要参数是制造公差和温度稳定度,如果选择有源晶振作为本地时钟,需要满足:制造公差+温度稳定度≤±20ppm,有源晶振内部集成晶体和相应的振荡电路,匹配精确,频率稳定性高,而且抗干扰性能好,缺点是成本较高。§
图1 震荡电路设计
本文自行设计的振荡电路如图1所示。
选择温度稳定性高、制造公差低的高精度晶体,通过振荡电路设计实现谐振频率的精确调整,这是由晶体负载电容的匹配及可调实现的。负载电容是指CRY_IN和CRY_OUT两端的电容值,在晶体的CRY_IN引脚上并联一个可调电容,调整该电容便可以对谐振频率进行精密微调。晶体负载电容计算公式如下:
Cload=Cint+(Cin+Ctrim)×Cout/(Cin+Ctrim+Cout)
Cint包括IC内部电容(一般为固定值)以及PCB杂散电容(3pF~5pF)。
3.1.4、配对列表管理
为了保证链路级的安全,蓝牙通信要求设备在连接建立前进行“双向认证”。认证成功的前提是设备双方存储了相同的链路密钥Kab,配对是产生初始密钥Kint的阶段,Kint由PIN码、从设备蓝牙地址和主设备发给从设备的一个随机数由一套固定的算法计算出来,只要PIN码一致,主从设备生成的的Kint也是一致的。链路密钥的输入是主从设备的蓝牙地址和主从设备各一随机数,只要主从设备能互换随机数,便能得到一致的Kab。主设备将随机数RandA与Kint异或的结果发给从设备,从设备只将该结果与Kint异或便得到RandA,即
§
配对列表的管理包括添加、替代及删除,添加配对设备是在非易失性存储中存储该设备的蓝牙地址及Kab。删除配对设备需要谨慎处理,如果存在连接,需要先断开连接然后删除,因为如果删掉该设备而有一种应用的连接没有断开,会存在临时密钥用于当前应用,这时不经配对也可以连接上其他应用,违反了蓝牙安全性要求
3.1.5、蓝牙文件传输模式
文件传输的目的是使两个终端之间的数据交换成为可能,传输时使用的协议如图3.1所示,可传送的文件有doc、jpg、ppt、xls、wav等文件,还包括远端文件夹浏览功能。传输文件的设备可归结成C/S结构。客户可从服务器下载文件,或向服务器上传文件。服务器是一种使用对象交换协议(OBEX)文件夹列表格式的远端蓝牙设备,其支持目标交换服务、文件夹浏览功能,还允许客户修改、创建文件或文件夹。
3.2、hci层介绍
3.2.1、hci层介绍
Host Controller Interface(HCI) 就是用来沟通Host和Module。Host通常就是PC, Module则是以各种物理连接形式(USB,serial,pc-card等)连接到PC上的bluetooth Dongle。HCI则比较特殊,它一部分在软件中实现,用来给上层协议和程序提供访问接口(Bluez中,hci.c hci_usb.c,hci_sock.c等).另一部分也是在Firmware中实现,用来将软件部分的指令等用底层协议明白的方式传递给底层。
居于PC的上层程序与协议和居于Modules的下层协议之间通过HCI沟通,有4种不同形式的传输:Commands, Event, ACL Data, SCO/eSCO Data.。
HCI Command:HCI Command是Host向Modules发送命令的一种方式。
HCI Event:Modules向Host发送一些信息,使用HCI Event。
3.2.2、hci层编程
对本地dongle进行操作:
- 得到Host上插入Dongle数目以及Dongle信息
实现步骤如下:
// 0. 分配一个空间给 hci_dev_list_req。这里面将放所有Dongle信息。
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
struct hci_dev_info di;
int i;
if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)))) {
perror("Can't allocate memory");
exit(1);
}
dl->dev_num = HCI_MAX_DEV;
dr = dl->dev_req;
//1. 打开一个HCI socket.
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
perror("Can't open HCI socket.");
exit(1);
}
// 2. 使用HCIGETDEVLIST,得到所有dongle的Device ID。存放在dl中。
if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
perror("Can't get device list");
exit(1);
}
// 3 使用HCIGETDEVINFO,得到对应Device ID的Dongle信息。
di.dev_id = (dr+i)->dev_id;
ioctl(ctl, HCIGETDEVINFO, (void *) &di);
这样就能得到所有Dongle信息。
struct hci_dev_info {
uint16_t dev_id; //dongle Device ID
char name[8]; //Dongle name
bdaddr_t bdaddr; //Dongle bdaddr
uint32_t flags; //Dongle Flags:如:UP,RUNING,Down等。
uint8_t type; //Dongle连接方式:如USB,PC Card,UART,RS232等。
uint8_t features[8];
uint32_t pkt_type;
uint32_t link_policy;
uint32_t link_mode;
uint16_t acl_mtu;
uint16_t acl_pkts;
uint16_t sco_mtu;
uint16_t sco_pkts;
struct hci_dev_stats stat; //此Dongle的数据信息,如发送多少个ACL Packet,正确多少,错误多少,等等。
};
- 打开一个HCI Socket---int hci_open_dev(int dev_id)
这个function用来打开一个HCI Socket。它首先打开一个HCI protocol的Socket(房间),并将此Socket与device ID=参数dev_id的Dongle绑定起来。只有bind后,它才将Socket句柄与Dongle对应起来。
注意,所有的HCI Command发送之前,都需要使用 hci_open_dev打开并绑定。
- 关闭一个HCI Socket
int hci_close_dev(int dd) //简单的关闭使用hci_open_dev打开的Socket。
- 向HCI Socket(对应一个Dongle)发送 request
int hci_send_req(int dd, struct hci_request *r, int to)
BlueZ提供这个function非常有用,它可以实现一切Host向Modules发送Command的功能。
参数1:HCI Socket。
参数2:Command内容。
参数3:以milliseconds为单位的timeout.
下面详细解释此function和用法:
当应用程序需要向Dongle(对应为一个bind后的Socket)发送Command时,调用此function.
其中,参数一dd对应一个使用hci_open_dev()打开的Socket(Dongle)。
参数三to则为等待Dongle执行并回复命令结果的timeout.以毫秒为单位。
参数二hci_request * r 最为重要,首先看它的结构:
struct hci_request {
uint16_t ogf; //Opcode Group
uint16_t ocf; //Opcode Command
int event; //此Command产生的Event类型。
void *cparam; //Command 参数
int clen; //Command参数长度
void *rparam; //Response 参数
int rlen; //Response 参数长度
};
ogf,ocf不用多说,对应前面的图就明白这是Group Code和Command Code。这两项先确定下来,然后可以查HCI Spec。察看输入参数(cparam)以及输出参数(rparam)含义。至于他们的结构以及参数长度,则在~/include/net/bluetooth/hci.h中有定义。
例1:得到某个连接的Policy Setting.
HCI Spec以及~/include/net/bluetooth/hci.h中均可看到,OGF=OGF_LINK_POLICY(0x02). OCF=OCF_READ_LINK_POLICY(0x0C).
因为这个Command用来读取某个ACL连接的Policy Setting。所以输入参数即为此连接Handle.
返回参数则包含3部分,status(Command是否顺利执行), handle(连接Handle)。 policy(得到的policy值)
这就又引入了一个新问题,如何得到某个ACL连接的Handle。
可以使用ioctl HCIGETCONNINFO得到ACL 连接Handle。
ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
Connect_handle = htobs(cr->conn_info->handle);
所以完整的过程如下:
struct hci_request HCI_Request;
read_link_policy_cp Command_Param;
read_link_policy_rp Response_Param;
// 1.得到ACL Connect Handle
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0)
{
return -1;
}
Connect_handle = htobs(cr->conn_info->handle);
memset(&HCI_Request, 0, sizeof(HCI_Request));
memset(&Command_Param, 0 , sizeof(Command_Param));
memset(&Response_Param, 0 , sizeof(Response_Param));
// 2.填写Command输入参数
Command_Param.handle = Connect_handle;
HCI_Request.ogf = OGF_LINK_POLICY; //Command组ID
HCI_Request.ocf = OCF_READ_LINK_POLICY; //Command ID
HCI_Request.cparam = &Command_Param;
HCI_Request.clen = READ_LINK_POLICY_CP_SIZE;
HCI_Request.rparam = &Response_Param;
HCI_Request.rlen = READ_LINK_POLICY_RP_SIZE;
if (hci_send_req(dd, &HCI_Request, to) < 0)
{
perror("\nhci_send_req()");
return -1;
}
//如果返回值状态不对
if (Response_Param.status) {
return -1;
}
//得到当前policy
*policy = Response_Param.policy;
- 几个更基础的function
static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) //bdaddr copy
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)//bdaddr 比较
- 得到指定Dongle BDAddr
int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:输出参数,其中会放置bdaddr.
参数3:以milliseconds为单位的timeout.
- 读写Dongle Name
int hci_read_local_name(int dd, int len, char *name, int to)
int hci_write_local_name(int dd, const char *name, int to)
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。
参数2:读取或设置Name。
参数3:以milliseconds为单位的timeout.
注意:这里的Name与IOCTL HCIGETDEVINFO 得到hci_dev_info中的name不同
- 得到HCI Version
int hci_read_local_version(int dd, struct hci_version *ver, int to)
- 得到已经UP的Dongle BDaddr
int hci_devba(int dev_id, bdaddr_t *bdaddr);
dev_id: Dongle Device ID.
bdaddr:输出参数,指定Dongle如果UP, 则放置其BDAddr
- 得到BDADDR不等于参数bdaddr的Dongle Device ID
int hci_get_route(bdaddr_t *bdaddr)
查找Dongle,发现Dongle Bdaddr不等于参数bdaddr的第一个Dongle,则返回此Dongle Device ID。
所以,如果: int hci_get_route(NULL),则得到第一个可用的Dongle Device ID。
- 将BDADDR转换为字符串
int ba2str(const bdaddr_t *ba, char *str)
对远程dongle进行操作:
-
inquiry 远程Bluetooth Device
int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags)
hci_inquiry()用来命令指定的Dongle去搜索周围所有bluetooth device.并将搜索到的Bluetooth Device bdaddr 传递回来。
参数1:dev_id:指定Dongle Device ID。如果此值小于0,则会使用第一个可用的Dongle。
参数2:len: 此次inquiry的时间长度(每增加1,则增加1.25秒时间)
参数3:nrsp:此次搜索最大搜索数量,如果给0。则此值会取255。
参数4:lap:BDADDR中LAP部分,Inquiry时这块值缺省为0X9E8B33.通常使用NULL。则自动设置。
参数5:ii:存放搜索到Bluetooth Device的地方。给一个存放inquiry_info指针的地址,它会自动分配空间。并把那个空间头地址放到其中。
参数6:flags:搜索flags.使用IREQ_CACHE_FLUSH,则会真正重新inquiry。否则可能会传回上次的结果。
返回值是这次Inquiry到的Bluetooth Device 数目。
注意:如果*ii不是自己分配的,而是让hci_inquiry()自己分配的,则需要调用bt_free()来帮它释放空间。
-
得到指定BDAddr的reomte device Name
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)
参数1:使用hci_open_dev()打开的Socket。
参数2:对方BDAddr.
参数3:name 长度。
参数4:(out)放置name的位置。
参数5:等待时间。
-
读取连接的信号强度
int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
注意,所有对连接的操作,都会有一个参数,handle.这个参数是连接的Handle。前面讲过如何得到连接Handle的。
3.3、L2CAP层编程
3.3.1、L2CAP协议简介
Logical Link Control and Adaptation Protocol(L2CAP)逻辑连接控制和适配协议为上层协议提供面向连接和无连接的数据服务,并提供多协议功能和分割重组操作。L2CAP 充许上层协议和应用软件传输和接收最大长度为 64K 的 L2CAP 数据包。
L2CAP 基于通道(channel) 的概念。 通道 (Channel) 是位于基带 (baseband) 连接之上的逻辑连接。每个通道以多对一的方式绑定一个单一协议 (single protocol)。多个通道可以绑定同一个协议,但一个通道不可以绑定多个协议。 每个在通道里接收到的 L2CAP 数据包被传到相应的上层协议。 多个通道可共享同一个基带连接。也就是说,所有L2CAP数据均通过HCI传输到Remote Device。且上层协议的数据,大都也通过L2CAP来传送。L2CAP可以发送Command。例如连接,断连等等。
3.3.2、L2CAP编程方法
L2CAP编程非常重要,它和HCI基本就是Linux Bluetooth编程的基础了。几乎所有协议的连接,断连,读写都是用L2CAP连接来做的。
1.创建L2CAP Socket
socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
domain=PF_BLUETOOTH, type可以是多种类型。protocol=BTPROTO_L2CAP.
2.绑定:
// Bind to local address
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(&addr.l2_bdaddr, &bdaddr); //bdaddr为本地Dongle BDAddr
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Can't bind socket");
goto error;
}
3.连接
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(addr.l2_bdaddr, src);
addr.l2_psm = xxx;
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("Can't connect");
goto error;
}
注意:
struct sockaddr_l2 {
sa_family_t l2_family; //必须为 AF_BLUETOOTH
unsigned short l2_psm; //与前面PSM对应,这一项很重要
bdaddr_t l2_bdaddr; //Remote Device BDADDR
unsigned short l2_cid;
};
4. 发送数据到Remote Device:
send()或write()都可以。
5. 接收数据:
revc() 或read()
以下为实例:
注:在Bluetooth下,主动去连接的一端作为主机端。被动等别人连接的作为Client端。
背景知识1:Bluetooth设备的状态
之前HCI编程时,是用 ioctl(HCIGETDEVINFO)得到某个Device Info(hci_dev_info).其中flags当时解释的很简单。其实它存放着Bluetooth Device(例如:USB Bluetooth Dongle)的当前状态:
其中,UP,Down状态表示此Device是否启动起来。可以使用ioctl(HCIDEVUP)等修改这些状态。
另外:就是Inquiry Scan, PAGE Scan这些状态:Inquiry Scan状态表示设备可被inquiry. Page Scan状态表示设备可被连接。
参考:L2CAP编程,http://blog.csdn.net/baozhongchao/archive/2009/10/26/4728751.aspx
3.4、SDP协议简介
Service Discovery Protocol(SDP)提供一种能力,让应用程序有方法发现哪种服务可用以及这种服务的特性。服务发现协议(SDP或Bluetooth SDP)在蓝牙协议栈中对蓝牙环境中的应用程序有特殊的含意,发现哪个服务是可用的和确定这些可用服务的特征。SDP定义了bluetooth client发现可用bluetooth server服务和它们的特征的方法。这个协议定义了客户如何能够寻找基于特定属性的服务而不让客户知道可用服务的任何知识。SDP提供发现新服务的方法,在当客户登录到正在操作的蓝牙服务器的一个区域时是可用的时。
Service discovery机制提供client应用程序侦测server应用程序提供的服务的能力,并且能够得到服务的特性。服务的品质包含服务type或服务class。SDP也提供SDP server与SDP client之间的通讯。SDP server维护着一个服务条目(service record)列表.每个服务条目描述一个单独的服务属性。 SDP client可以通过发送SDP request来得到服务条目。如果一个client或者依附于client之上的应用程序决定使用某个service. 它创建一个单独的连接到service提供者。 SDP 只提供侦测Service的机制,但不提供如何利用这些Service的机制。这里其实是说:SDP只提供侦测Service的办法,但如何用,SDP不管。每个Bluetooth Device最多只能拥有一个SDP Server。如果一个Bluetooth Device只担任Client,那它不需要SDP Server。但一个Bluetooth Device可以同时担当SDP Server和SDP client.
Service Record(Service 条目):
一个service是一个实体为另一个实体提供信息,执行动作或控制资源。一个service可以由软件,硬件或软硬件结合提供。所有的Service信息都包含于一个Service Record内。一个Service Record 包含一个Service attribute(Service属性) list。在一个SDP Server内,每个Service Record拥有一个32-bit的唯一性数据。通常,这个唯一性只是在每个SDP Server内部。 如果SDP Server S1 和SDP Server S2拥有同样的一个Service Record。那他们在不同SDP Sever内的独特数值并不一定相同。SDP在SDP Server增加或减少Service Record时,并不会通知SDP client.
Service Attribute(Service 属性):
每个Service属性描述servcie的特性.一个Service Attribute由2部分:
Attribute ID + Attribute Value。
Attribute ID:16-bit无符号整数,用于区别一个Service Record内的其它属性。
Attribute Value:Attribute值。
Service Class:
每个Service 都是某个Service Class的实例. Service Class定义了Service Record中包含的Service 属性。属性ID,属性值都被定义好了。每个Service Class也有一个独特ID。这个Service Class标识符包含在属性值ServiceClassIDList属性中。并描绘为UUID。自从Service Record中的属性格式以及含义依赖于Service Class后,ServiceClassIDList属性变得非常重要。
Searching For Service:
Service Search transaction(事务?)允许client得到Service Record Handle。一旦SDP Client得到Service Record Handle,它就可以请求这个Record内具体属性的值。
如果某个属性值UUID,则可以通过查找UUID查到这个属性。
UUID: universally unique identifier.(唯一性标识符)
SDP协议栈使用request/response模式工作,每个传输过程包括一个request protocol data unit(PDU)和一个response PDU. SDP使用L2CAP连接传输数据。在发送Request PDU但未收到Response PDU之前,不能向同一个server再发送Request PDU。
PDU:protocol Data unit
PDU ID:用来识别PDU。
TransactionID:
用来识别Request PUD以及Response PUD。并用来对比某个Response PUD是否对应着Request PUD。
4、Openobex
4.1、Openobex简介
Welcome to the Open OBEX project. The overall goal of this project is to make an open source implementation of the Object Exchange (OBEX) protocol. OBEX is a session protocol and can best be described as a binary HTTP protocol. OBEX is optimised for ad-hoc wireless links and can be used to exchange all kind of objects like files, pictures, calendar entries (vCal) and business cards (vCard).
OBEX was specified by the IrDA? (Infrared Data Association), and although the protocol is very good for Infrared connections, it is not limited to it. In fact OBEX does not specify the top or bottom API making it very flexible and can run over most transports like TCP/IP and Bluetooth. Therefore OBEX is also called IrOBEX when used over the Infrared medium. There are some transport modules for serial links (cable OBEX) too.
Today, OBEX is builtin in many devices e.g. PDA's like the Palm§ Pilot, and mobile phones like the Sony Ericsson§ R320, R520, T68, T610, T630, K700 and many later phones, Siemens§ S25, S35, S45, S55, S65 Nokia§ NM207 and Nokia 9110 Communicator. The HP Scanner?§ CapShare 920 can also talk OBEX in addition to JetSend. Microsoft§ Windows2000 has also builtin OBEX support.
Link types currently supported by OpenOBEX are:
INFRARED DATA ASSOCIATION ? (IrDA) http://www.irda.org§
USB-IF, Inc. http://www.usb.org§
Bluetooth(TM) http://www.bluetooth.com§
Support for TCP/IP links, file descriptors and custom transports is also provided.
OBEX全称为Object Exchange,中文对象交换,所以称之为对象交换协议。它在此软件当中有着核心地位,文件传输和IrMC同步都会使用到它。OBEX协议构建在IrDA架构的上层. OBEX协议通过简单的使用“PUT”和“GET”命令实现在不同的设备、不同的平台之间方便、高效的交换信息。支持的设备广泛,例如PC,PDA,电话,摄像头,自动答录机,计算器,数据采集器,手表等等。OBEX协议定义了一种柔性的概念——objects。也即是对象。这些对象可以包括文件,诊断信息,电子商务卡片,银行的存款等等。Objects在这里没有高级的技术含义,而是视你的应用而定。OBEX协议小到可作“命令和控制”功能,例如对电视机,录像机等的操作。大道可以做很复杂的操作,例如数据库的事务处理和同步。
OBEX能够具有以下几个特点:
1、 友好的应用——可实现快速开发。
2、 紧缩——可用在资源有限的小型设备上。
3、 跨平台
4、 柔性的数据支持。
5、 方便的作为其他Internet传输协议的上层协议。
6、 可扩展性——提供了对未来需求的扩充支持而不影响以存在的实现。例如可扩展安全,数据压缩等。
7、 可测试可调试。
4.2、Openobex与bluez编程实现
如下表:基于OBEX和BlueZ的数据传输的步骤
步骤 |
函数 |
意义 |
1 |
OBEX_Init() |
用于初始化一个obex instance handle; arg1:OBEX_TRANS_BLUETOOTH用于声明传输协议为bluetooth; arg2:callback function; arg3:flag=OBEX_FL_KEEPSERVER,接收到请求后,服务器可以继续接收其他客户端的请求; |
2 |
OBEX_SetUserData() |
设置用户自己的变量,该函数的使用完全取决于用户自己; |
3 |
BtOBEX_ServerRegister() |
一个专用于蓝牙协议的服务端函数,用于监听客户端发送的请求。该函数内部创建了一个 socket(调用socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM)),bind该socket到本地蓝牙地址,将该socket转化为监听sockt,之后该socket才能够监听端口上来自客户端的连接请求; |
4 |
OBEX_HandleInput() |
函数用于读取并处理接收到的数据,如果没有数据到达,该函数将会阻塞;该函数内部调用了selet()函数,向系统登记了参数handle的客户端sockt与服务端sockt,让系 统监听socket上的事件,如果是服务端socket上有数据到达,则调用accept()函数为客户端创建一个新的sockt,如果OBEX_Init()的flag不是设置为OBEX_FL_KEEPSERVER,则关掉服务端socket,禁止其他客户端的连接请求; |
5 |
CALLBACK : (OBEX_EV_ACCEPTHINT) |
|
6 |
OBEX_ServerAccept() |
该函数返回上述客户端的socket。 函数内部重新创建了一个obex instance handle,并将上述服务器handle的参数复制到该handle,获得服务器handle的fd(accept为客户端创建的socket)后,清除服务器本身的fd; 该函数同时也为新创建的obex instance handle设置callback function及Userdata;至此,已经为客户端创建了一个与服务端完全独立的obex instance handle,此后该服务端的操作都由该handle标识,而服务器的socket则继续监听其他客户端的连接请求。 |
7 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_CONNECT |
8 |
CALLBACK : OBEX_EV_REQHINT |
一个请求即将到来。 调用OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)设置响应操作码 |
9 |
CALLBACK : OBEX_EV_REQCHECK |
第一个接收到的请求包已经被解析 |
10 |
CALLBACK : OBEX_EV_PROGRESS |
收到n个此事件,说明客户端正在传输文件内容 |
11 |
CALLBACK : OBEX_EV_REQ |
OBEX_CMD_PUT OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)设置响应操作码;此时,客户端文件传输完毕,需进行处理: OBEX_ObjectGetNextHeader()分别取得文件的名称与内容; |
12 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_PUT |
13 |
CALLBACK : OBEX_EV_REQHINT |
一个请求即将到来 |
14 |
CALLBACK : OBEX_EV_REQ |
OBEX_CMD_DISCONNECT OBEX_ObjectSetRsp(object,OBEX_RSP_SUCCESS,OBEX_RSP_SUCCESS)设置响应操作码 |
15 |
CALLBACK : OBEX_EV_REQDONE |
OBEX_CMD_DISCONNECT; OBEX_TransportDisconnect(handle)断开连接; 注意:该函数只是将socket关闭,并没有释放handle所占用的资源,所以在应用时需要释放handle占用的资源; |
16 |
OBEX_Cleanup() |
关掉obex handle并释放该handle占用的资源。 (该函数同样关闭了客户端及服务端socket) |
如下表:OpenOBEX的代码说明:
文件编号 |
文件名 |
作用 |
1 |
obex_connect.c/.h |
处理CONNECT PDU,打包和解包 |
2 |
obex_header.c/.h |
PDU处理的公共函数及数据类型定义 |
3 |
obex_object.c/.h |
对象处理函数,客户端和服务器公用代码 |
4 |
obex_server.c/.h |
服务器端处理代码 |
5 |
obex_client.c/.h |
客户端处理代码 |
6 |
obex_transport.c/.h |
传输接口的抽象 |
7 |
irobex.c/.h |
基于红外的传输方式 |
8 |
usbobex.c/.h |
基于USB的传输方式 |
9 |
inobex.c/.h |
基于TCP/IP的传输方式 |
10 |
btobex.c/.h |
基于蓝牙的传输方式 |
Obexftp
5.1、obexftp简介
The main goal of this project is to make mobile devices that feature the OBEX protocol and that adhere to the OBEX FTP standard accessible using an open source implementation.
ObexFTP is a library bundling everything needed for OBEX transfers and exposing it via a simple interface. Quite a number of language bindings are provided using SWIG or other means. There is a sample command line client "obexftp" and a server "obexftpd" included. Besides FTP the ObexFTP library provides access to the PUSH, GOEP and SYNCH services. It runs on Linux, FreeBSD, NetBSD and Win32.
5.2、基于Obexftp的应用程序开发
参考代码
6、参考资料
D-bus官网,http://dbus.freedesktop.org/doc/dbus-tutorial.html§
D-bus中文介绍,http://blog.sina.com.cn/s/blog_5412ede60100eml7.html§
Bluetooth官网,www.bluetooth.com§
Openobex官网,http://dev.zuckschwerdt.org/openobex/wiki/ObexIntroduction§
Openobex API,http://dev.zuckschwerdt.org/openobex/doxygen/