1、底层硬件模块、中间协议层和高层应用。底层硬件部分包括无线跳频(RF)、基带(BB)和链路管理(LM)。无线跳频层通过2.4GHz无需授权的ISM频段的微波,实现数据位流的过滤和传输,本层协议主要定义了蓝牙收发器在此频带正常工作所需要满足的条件。基带负责跳频以及蓝牙数据和信息帧的传输。链路管理负责连接、建立和拆除链路并进行安全控制。关于bluetooth协议栈,接下来再谈。当前已经实现的Bluetooth栈有以下各种:1. Widcomm: 第一个windows上的协议栈,由Widcomm公司开发,也就是现在的Broadcom. 2. Microsoft Windows stack: Windo
2、ws XP SP2中包括了这个内建的协议栈,开发者也可以调用其API开发第三方软件。3. Toshiba stack: 它也是基于Windows的,不支持第三方开发,但它把协议栈授权给一些laptop商(sony, asus等,我的本本上就是Toshiba的)。它支持的Profile有: SPP, DUN, FAX, LAP, OPP, FTP, HID, HCRP, PAN, BIP, HSP, HFP , A2DP, AVRCP, GAVDP) 4. BlueSoleil: 著名的IVT公司的产品该产品可以用于桌面和嵌入式,他也支持第三方开发,DUN, FAX, HFP, HSP, LAP
3、, OBEX, OPP, PAN SPP, AV, BIP, FTP, GAP, HID, SDAP, and SYNC。5. Bluez: Linux官方协议栈,该协议栈的上层用Socket封装,便于开发者使用,通过DBUS与其它应用程序通信。6. Affix: NOKIA公司的协议栈,在Symbian系统上运行. 7. BlueDragon:东软公司产品,好像2002年6月就通过了蓝牙的认证,支持的Profile:SDP、Serial-DevB、AVCTP、AVRCP-Controller、AVRCP-Target、Headset-AG、Headset-HS、OPP-Client、OPP-
4、Server、CT-GW、CT-Term、Intercom、FT-Server、FT-Client、GAP、SDAP、Serial-DevA、AVDTP、GAVDP、A2DP-Source、A2DP-Sink. 8. BlueMagic:美国Open Interface 公司for portable embedded divce的协议栈,iphone(apple),nav-u(sony)等很多电子产品都用该商业的协议栈,BlueMagic 3.0是第一个通过bluetooth 协议栈1.1认证的协议栈,那么我现在就在用它,那么该栈用起来简单,API清晰明了。实现了的profile有:HCI,L
5、2CAP,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. 9. BCHS-Bluecore Host Software: 蓝牙芯片CSR的协议栈,同时他也提供了一些上层应用的Profile的库,当然了它也是为嵌入式产品了,支持的Profile有:A2DP,AVRCP,PBAP,BIP,BPP,CTP,DUN,FAX,
6、FM API,FTP GAP,GAVDP,GOEP,HCRP,Headset,HF1.5,HID,ICP,JSR82,LAP Message Access Profile,OPP,PAN,SAP,SDAP,SPP,SYNC,SYNC ML。10. Windows CE:微软给Windows CE开发的协议栈,但是windows ce本身也支持其它的协议栈 11. BlueLet:IVT公司for embedded product的清量级协议栈。BlueZ Linux下Bluetooth协议栈的实现. Linux下开放的蓝牙协议栈主要包括IBM公司的BlueDrekar,Nokia公司的Affi
7、x, Axis公司的OpenBT和官方协议栈BlueZ。我们主要面对Bluez来探讨。BlueZ基础代码均是由Maxim Krasnyansky完成的。包括:HCI,L2CAP,RFCOMM和基本socket的实现。他就职于Qualcomm(高通)。Marcel Holtmann开发层的协议和应用,包括:BNEP, CMTP等。当然,这些中也有Maxim Krasnyansky的参预。有部分代码由Nokia提供的。Bluez是如何实现Bluetooth协议栈的呢? 它分2部分实现:1.Kernel层实现:正如上一篇所谈到的,bluetooth协议栈有多层结构,最底层的硬件协议在硬件中就已经实现
8、了。(例如broadcom的芯片中,底层硬件协议已经包含于芯片之中了)。软件级别的协议实现,从HCI这一层起就可以了。 BlueZ对各层协议的实现是依托于Socket的。BlueZ首先创建了一个新的Socket中的协议-PF_BLUETOOTH (AF_BLUETOOTH=31). (也就是说,Socket()的第一个参数:domain必须是:PF_BLUETOOTH )。这也意味着,地址类型需要使用Bluetooth所定义的。其实很简单,就是在net_families(网络协议列表)中添加了PF_BLUETOOTH这一项。如果对Linux Kernel有了解的话,就知道这个注册动作一定在Bl
9、uetooth init部分作的。同样,各个协议层(如:HCI,L2CAP,HID等)都将自己的行为规范添加到PF_BLUETOOTH协议中。2.应用程序层实现:虽然Kernel层已经将Bluetooth协议栈完全实现了,但如果要使用起来,还是非常不方便的。毕竟应用程序与kernel最方便的交流通道就是ioctl().这非常不直观。于是,BlueZ又提供了一套API,这个API帮助开发者方便的与Kernel层协议打交道。当然,这些API底层的实现其就是是ioctl. BlueZ的时间,基本就是这样了。下面咱们具体研究如何使用BlueZ所提供的这套API。 HCI 1. HCI层协议概述:HCI
10、提供一套统一的方法来访问Bluetooth底层。如图所示:从图上可以看出,Host Controller Interface(HCI) 就是用来沟通Host和Module。Host通常就是PC, Module则是以各种物理连接形式(USB,serial,pc-card等)连接到PC上的bluetooth Dongle。在Host这一端:application,SDP,L2cap等协议都是软件形式提出的(Bluez中是以kernel层程序)。在Module这一端:Link Manager, BB, 等协议都是硬件中firmware提供的。而HCI则比较特殊,它一部分在软件中实现,用来给上层协议和
11、程序提供访问接口(Bluez中,hci.c hci_usb.c,hci_sock.c等).另一部分也是在Firmware中实现,用来将软件部分的指令等用底层协议明白的方式传递给底层。居于PC的上层程序与协议和居于Modules的下层协议之间通过HCI沟通,有4种不同形式的传输:Commands, Event, ACL Data, SCO/eSCO Data。1.1. HCI Command:HCI Command是Host向Modules发送命令的一种方式。HCI Command Packet结构如下:OpCode用来唯一标识HCI Command.它由2部分组成,10bit的Opcode C
12、ommand. 6bit的Opcode Group。1.1.1: OpCode Group:Linux Kernel(BlueZ)中,/include/net/bluetooth/hci.h中定义了OpCode Group。#define OGF_LINK_CTL 0x01 #define OGF_LINK_POLICY 0x02 #define OGF_HOST_CTL 0x03 #define OGF_INFO_PARAM 0x04 #define OGF_STATUS_PARAM 0x05 它们代表了不同的Command Group:OGF_LINK_CTL: Link control,
13、这个Command Group中的Command允许Host控制与其它bluetooth device 的连接。OGF_LINK_POLICY :Link Policy。这个Command Group中的Command允许调整Link Manager control. OGF_HOST_CTL: Control and Baseband. 1.1.2: Opcode Command:用来在同一个Group内唯一识别Command。/include/net/bluetooth/hci.h中定义。1.2: HCI Event:Modules向Host发送一些信息,使用HCI Event。Event
14、 Packet结构如下:HCI Event分3种:Command complete Event, Command States Event,Command Subsequently Completend. Command complete Event: 如果Host发送的Command可以立刻有结果,则会发送此类Event。也就是说,如果发送的Command只与本地Modules有关,不与remote设备打交道,则使用Command complete Event。例如:HCI_Read_Buffer_Size. Command States Event:如果Host发送的Command不能立刻
15、得知结果,则发送此类Event。Host发送的Command执行要与Remote设备打交道,则必然无法立刻得知结果,所以会发送Command States Event.例如:HCI Connect。Command Subsequently Completend:Command延后完成Event。连接已建立。下图是一个Command-Event例子:从这里可以看出,如果Host发送的Command是与Remote device有关的,则会先发送Command States Event 。等动作真正完成了,再发送 Command Subsequently Completend。HCI ACL与SC
16、O数据,这里就不多讲了。只需要明白,l2cap数据是通过ACL数据传输给remote device的。下图很明白的展示了l2cap数据如何一步一步转化为USB数据并传递给底层协议的。很明显,一个l2cap包会按照规则先切割为多个HCI数据包。HCI数据包再通过HCI-usb这一层传递给USB设备。每个包又通过USB driver发送到底层。2. HCI protocol的实现:(稍后添加) 3. HCI 层的编程:正如上一节所说,HCI是沟通上层协议以及程序与底层硬件协议的通道。所以,通过HCI发送的Command都是上层协议或者应用程序发送给Bluetooth Dongle的。它命令Blue
17、tooth Dongle(或其中的硬件协议)去做什么何种动作。3.0:得到Host上插入Dongle数目以及Dongle信息:我们先复习一下socket的概念:使用函数socket()建立一个Socket,就如同你有一部电话.bind()则是把这个电话和某个电话号码(网络地址)对应起来。类似的,我们可以把Host理解为一个房间,这个房间有多部电话(Dongle)。当使用socket() 打开一个HCI protocol的socket,表明得到这个房间的句柄。HOST可能会有多个Dongle。换句话说,这个房间可以有多个电话号码。所以HCI会提供一套指令去得到这些Dongle。/ 0. 分配一个
18、空间给 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(Cant allocate memory);exit(1); dl-dev_num = HCI_MAX_DEV;dr = dl-dev_req;/1. 打开一个HCI socket.此socket
19、相当于一个房间。if (ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) 0) t open HCI socket./ 2. 使用HCIGETDEVLIST,得到所有dongle的Device ID。存放在dl中。if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) dev_id;ioctl(ctl, HCIGETDEVINFO, (void *) &di);这样就能得到所有Dongle信息。struct hci_dev_info uint16_t dev_id; /dongle Device ID char n
20、ame8; /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 features8;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;stru
21、ct hci_dev_stats stat; /此Dongle的数据信息,如发送多少个ACL Packet,正确多少,错误多少,等等。;3.0.1: UP和Down Bluetooth Dongle:ioctl(ctl, HCIDEVUP, hdev) ioctl(ctl, HCIDEVDOWN, hdev) ctl:为使用socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)打开的Socket. hdev: Dongle Device ID.(所以上面的Socket不需要bind,因为这边指定了) 3.1 BlueZ提供的HCI编程接口一(针对本地Dongle
22、的API系列):3.1。1 打开一个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打开并绑定。3.1.2: 关闭一个HCI Socket:int hci_close_dev(int dd) /简单的关闭使用hci_open_de
23、v打开的Socket。3.1.3: 向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. 其中,
24、参数一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 参数 i
25、nt rlen; /Response 参数长度 ogf,ocf不用多说,对应前面的图就明白这是Group Code和Command Code。这两项先确定下来,然后可以查HCI Spec。察看输入参数(cparam)以及输出参数(rparam)含义。至于他们的结构以及参数长度,则在/include/net/bluetooth/hci.h中有定义。至于event.如果设置,它会被setsockopt设置于Socket。例1:得到某个连接的Policy Setting. HCI Spec以及/include/net/bluetooth/hci.h中均可看到,OGF=OGF_LINK_POLICY(
26、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 =
27、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;memset(&HCI_Request, 0, sizeof(HCI_Request);Command_Param, 0 , sizeof(Command_Param);Response_Param, 0 , sizeof(Response_Param);/ 2.填写Command输入参数 Command_Param.handle = Connect_handle;HCI_Request.ogf = OGF_LINK_POLICY; /C
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1