Bluetooth协议栈学习之SDP(2009-06-08 15:46:15)

Sam一年前在Linux下写了一个类似Windows下BTW的库--BTX。现在需要添加新功能时发现很多知识点都忘记了。所以决定在这次学习中,把一些bluez API记录下来。这几天又想,这样还不够,不如把Linux下的Bluetooth编程基础给记录下来吧。

 

 

前言:随着嵌入式系统的飞速发展,很多嵌入式平台上需要Bluetooth设备和应用。但在Linux下如何对Bluetooth编程,一直没有一份很好的中文文档。Sam结合自己的工作,一步一步将一些有用的东西记录下来,希望对其它Linux下Bluetooth编程的朋友有点帮助。

 

一:Bluetooth基本概念:

 

Bluetooth是爱立信、诺基亚、东芝、IBM和Intel 5家公司在1998年联合推出的一项无线网络技术。其宗旨是提供一种短距离、低成本的无线传输应用技术。在行业协会筹备阶段,需要一个极具有表现力的名字来命名这项高新技术。行业组织人员,在经过一夜关于欧洲历史和未来无限技术发展的讨论后,有些人认为用Blatand国王的名字命名再合适不过了。Blatand国王将现在的挪威,瑞典和丹麦统一起来;就如同这项即将面世的技术,将标准不一的短距离无线传输技术统一起来。

Intel负责半导体芯片和传输软件的开发,爱立信负责无线射频和移动电话软件的开发,IBM和东芝负责笔记本电脑接口规格的开发。

 

蓝牙是无线数据和语音传输的开放式标准,它将各种通信设备、计算机及其终端设备、各种数字数据系统、甚至家用电器采用无线方式联接起来。它的传输距离为10cm~10m,如果增加功率或是加上某些外设便可达到100m的传输距离。它采用2.4GHz ISM频段和调频、跳频技术,使用权向纠错编码、ARQ、TDD和基带协议。TDMA每时隙为0.625μs,基带符合速率为1Mb/s。蓝牙支持64kb/s实时语音传输和数据传输,语音编码为CVSD,发射功率分别为1mW、2.5mW和100mW,并使用全球统一的48比特的设备识别码。由于蓝牙采用无线接口来代替有线电缆连接,具有很强的移植性,并且适用于多种场合,加上该技术功耗低、对人体危害小,而且应用简单、容易实现,所以易于推广。

 

蓝牙技术的系统结构分为三大部分:底层硬件模块、中间协议层和高层应用。底层硬件部分包括无线跳频(RF)、基带(BB)和链路管理(LM)。无线跳频层通过2.4GHz无需授权的ISM频段的微波,实现数据位流的过滤和传输,本层协议主要定义了蓝牙收发器在此频带正常工作所需要满足的条件。基带负责跳频以及蓝牙数据和信息帧的传输。链路管理负责连接、建立和拆除链路并进行安全控制。

 

关于bluetooth协议栈,接下来再谈。

 

 

当前已经实现的Bluetooth栈有以下各种:

1. Widcomm:  第一个windows上的协议栈,由Widcomm公司开发,也就是现在的Broadcom.

 
2. Microsoft Windows stack: Windows 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, 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-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,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.
9. BCHS-Bluecore Host Software: 蓝牙芯片CSR的协议栈,同时他也提供了一些上层应用的Profile的库,当然了它也是为嵌入式产品了,支持的Profile有:A2DP,AVRCP,PBAP,BIP,BPP,CTP,DUN,FAX,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的清量级协议栈。

 

 

Linux下Bluetooth协议栈的实现. Linux下开放的蓝牙协议栈主要包括IBM公司的BlueDrekar,Nokia公司的Affix, 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协议栈有多层结构,最底层的硬件协议在硬件中就已经实现了。(例如broadcom的芯片中,底层硬件协议已经包含于芯片之中了)。软件级别的协议实现,从HCI这一层起就可以了。 BlueZ对各层协议的实现是依托于Socket的。BlueZ首先创建了一个新的Socket中的协议--PF_BLUETOOTH (AF_BLUETOOTH=31). (也就是说,Socket()的第一个参数:domain必须是:PF_BLUETOOTH )。这也意味着,地址类型需要使用Bluetooth所定义的。

 

其实很简单,就是在net_families(网络协议列表)中添加了PF_BLUETOOTH这一项。如果对Linux Kernel有了解的话,就知道这个注册动作一定在Bluetooth init部分作的。

同样,各个协议层(如:HCI,L2CAP,HID等)都将自己的行为规范添加到PF_BLUETOOTH协议中。

 

2.应用程序层实现:

虽然Kernel层已经将Bluetooth协议栈完全实现了,但如果要使用起来,还是非常不方便的。毕竟应用程序与kernel最方便的交流通道就是ioctl().这非常不直观。于是,BlueZ又提供了一套API,这个API帮助开发者方便的与Kernel层协议打交道。当然,这些API底层的实现其就是是ioctl.

 

BlueZ的时间,基本就是这样了。下面咱们具体研究如何使用BlueZ所提供的这套API。

 

 

 

1. HCI层协议概述:

HCI提供一套统一的方法来访问Bluetooth底层。如图所示:

 

实战Linux <wbr>Bluetooth编程(三) <wbr>HCI层编程

 

从图上可以看出,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则比较特殊,它一部分在软件中实现,用来给上层协议和程序提供访问接口(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结构如下:

实战Linux <wbr>Bluetooth编程(三) <wbr>HCI层编程

OpCode用来唯一标识HCI Command.它由2部分组成,10bit的Opcode Command. 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,这个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 Packet结构如下:

实战Linux <wbr>Bluetooth编程(三) <wbr>HCI层编程

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不能立刻得知结果,则发送此类Event。Host发送的Command执行要与Remote设备打交道,则必然无法立刻得知结果,所以会发送Command States Event.例如:

HCI Connect。

Command Subsequently Completend:Command延后完成Event。例如:连接已建立。

 

下图是一个Command-Event例子:

实战Linux <wbr>Bluetooth编程(三) <wbr>HCI层编程

从这里可以看出,如果Host发送的Command是与Remote device有关的,则会先发送Command States Event 。等动作真正完成了,再发送 Command Subsequently Completend。

 

HCI ACL与SCO数据,这里就不多讲了。只需要明白,l2cap数据是通过ACL数据传输给remote device的。

下图很明白的展示了l2cap数据如何一步一步转化为USB数据并传递给底层协议的。

实战Linux <wbr>Bluetooth编程(三) <wbr>HCI层编程

很明显,一个l2cap包会按照规则先切割为多个HCI数据包。HCI数据包再通过HCI-usb这一层传递给USB设备。每个包又通过USB driver发送到底层。

 

 

2. HCI protocol的实现:

(稍后添加)

 

 

3. HCI 层的编程:

正如上一节所说,HCI是沟通上层协议以及程序与底层硬件协议的通道。所以,通过HCI发送的Command都是上层协议或者应用程序发送给Bluetooth Dongle的。它命令Bluetooth Dongle(或其中的硬件协议)去做什么何种动作。

 

3.0:得到Host上插入Dongle数目以及Dongle信息:

我们先复习一下socket的概念:

使用函数socket()建立一个Socket,就如同你有一部电话.bind()则是把这个电话和某个电话号码(网络地址)对应起来。

类似的,我们可以把Host理解为一个房间,这个房间有多部电话(Dongle)。

当使用socket() 打开一个HCI protocol的socket,表明得到这个房间的句柄。HOST可能会有多个Dongle。换句话说,这个房间可以有多个电话号码。所以HCI会提供一套指令去得到这些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.此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,正确多少,错误多少,等等。
};

 

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的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_dev打开的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.

其中,参数一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中有定义。

至于event.如果设置,它会被setsockopt设置于Socket。

 

例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;

 

 

3.1.4:几个更基础的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 比较

 

 

3.1.5: 得到指定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.

 

 

 

3.1.6: 读写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不同。

 

3.1.7:得到HCI Version:

int hci_read_local_version(int dd, struct hci_version *ver, int to)

 

 

3.1.8:得到已经UP的Dongle BDaddr:

int hci_devba(int dev_id, bdaddr_t *bdaddr);

dev_id: Dongle Device ID.

bdaddr:输出参数,指定Dongle如果UP, 则放置其BDAddr。

 

3.1.9: 得到Dongle Info:

int hci_devinfo(int dev_id, struct hci_dev_info *di)

dev_id: Dongle Device ID.

di: 此Dongle信息。

出错返回 -1。

注意,这个Function的做法与3.0的方法完全一致。

 

3.1.10:从hciX中得到X:

int hci_devid(const char *str)

str: 类似 hci0这样的字串。

如果hciX对应的Device ID(X)是现实存在且UP。则返回此设备Device ID。 

 

3.1.11:得到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。

 

 

3.1.12: 将BDADDR转换为字符串:

int ba2str(const bdaddr_t *ba, char *str)

 

3.1.13: 将自串转换为BDADDR:

int str2ba(const char *str, bdaddr_t *ba)

 

 

3.2 BlueZ提供的HCI编程接口二(针对Remote Device的API系列):

3.2.1  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()来帮它释放空间。

 

 

3.2.2:得到指定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:等待时间。

 

3.2.3: 读取连接的信号强度:

int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)

注意,所有对连接的操作,都会有一个参数,handle.这个参数是连接的Handle。前面讲过如何得到连接Handle的。

 

 

 

一:L2CAP协议简介:

Logical Link Control and Adaptation Protocol(L2CAP)

 

逻辑连接控制和适配协议 (L2CAP) 为上层协议提供面向连接和无连接的数据服务,并提供多协议功能和分割重组操作。L2CAP 充许上层协议和应用软件传输和接收最大长度为 64KL2CAP 数据包。

  L2CAP 基于 通道(channel) 的概念。 通道 (Channel) 是位于基带 (baseband) 连接之上的逻辑连接。每个通道以多对一的方式绑定一个单一协议 (single protocol)。多个通道可以绑定同一个协议,但一个通道不可以绑定多个协议。 每个在通道里接收到的 L2CAP 数据包被传到相应的上层协议。 多个通道可共享同一个基带连接。

 

L2CAP处于Bluetooth协议栈的位置如下:

实战Linux <wbr>Bluetooth编程(四) <wbr>L2CAP层编程

也就是说,所有L2CAP数据均通过HCI传输到Remote Device。且上层协议的数据,大都也通过L2CAP来传送。

 

 

L2CAP可以发送Command。例如连接,断连等等。

实战Linux <wbr>Bluetooth编程(四) <wbr>L2CAP层编程

 

下面看Command例子:Connection Request:

实战Linux <wbr>Bluetooth编程(四) <wbr>L2CAP层编程

 

其中PSM比较需要注意,L2CAP 使用L2CAP连接请求(Connection Request )命令中的PSM字段实现协议复用。L2CAP可以复用发给上层协议的连接请求,这些上层协议包括服务发现协议SDP(PSM = 0x0001)、RFCOMM(PSM = 0x0003)和电话控制(PSM = 0x0005)等。

 

 

ProtocolPSMReference
SDP0x0001See Bluetooth Service Discovery Protocol (SDP), Bluetooth SIG.
RFCOMM0x0003See RFCOMM with TS 07.10, Bluetooth SIG.
TCS-BIN0x0005See Bluetooth Telephony Control Specification / TCS Binary, Bluetooth SIG.
TCS-BIN-CORDLESS0x0007See Bluetooth Telephony Control Specification / TCS Binary, Bluetooth SIG.
BNEP0x000FSee Bluetooth Network Encapsulation Protocal, Bluetooth SIG.
HID_Control0x0011See Human Interface Device , Bluetooth SIG.
HID_Interrupt0x0013See Human Interface Device, Bluetooth SIG.
UPnP0x0015See [ESDP] , Bluetooth SIG.
AVCTP0x0017See Audio/Video Control Transport Protocol , Bluetooth SIG.
AVDTP0x0019See Audio/Video Distribution Transport Protocol , Bluetooth SIG.
AVCTP_Browsing0x001BSee Audio/Video Remote Control Profile, Bluetooth SIG
UDI_C-Plane0x001DSee the Unrestricted Digital Information Profile [UDI], Bluetooth SIG

 

 

 

 

二: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这些状态:

Sam在刚开始自己做L2CAP层连接时,使用另一台Linux机器插USB Bluetooth Dongle作Remote Device。怎么也没法使用inquiry扫描到remote设备,也没法连接remote设备,甚至无法使用l2ping ping到remote设备。觉得非常奇怪,后来才发现Remote Device状态设置有问题。没有设置PSCAN和ISCAN。

Inquiry Scan状态表示设备可被inquiry. Page Scan状态表示设备可被连接。

#hciconfig hci0 iscan

#hciconfig hci0 pscan

或者:#hciconfig hci0 piscan

就可以设置为PSCAN或者iSCAN状态了。

编程则可以使用ioctl(HCISETSCAN) . dev_opt = SCAN_INQUIRY;dr.dev_opt = SCAN_PAGE;dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;

则可以inquiry或者connect了。

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: SDP白皮书(Software Defined Perimeter)是一份旨在提供SDP标准规范的文档。SDP 是一种网络安全架构,旨在为企业提供更强大的网络保护和数据隐私。 SDP标准规范文档2.0是SDP架构的更新版本,它对之前版本的不足进行了改进和补充。该文档规范了SDP架构中各个组件的功能和交互方式,为企业和开发者提供了实施和使用SDP的指导。 SDP架构通过建立“黑盒”概念,实现了用程序和网络资源的隐藏。它将用户身份验证、授权和访问控制移到用层之前的连接层,从而提供了更强大的安全性。 在SDP标准规范文档2.0中,个人用户或设备必须经过身份验证才能访问企业网络资源。这通过使用多因素身份验证、公共密钥基础设施(PKI)等技术来实现。此外,SDP还提供了外部和内部访问控制策略的定义和实施,并支持企业灵活调整网络访问权限。 文档中还介绍了SDP架构中的关键组件,如SDP控制器和SDP代理。SDP控制器负责管理和控制用户身份验证和访问权限,而SDP代理则负责不同网络资源之间的安全连接。这些组件的详细功能和协作方式在文档中得到了详细描述。 总之,SDP白皮书-SDP标准规范文档2.0为企业提供了一套完整的SDP架构实施指南。它的推出将帮助企业建立更强大的网络安全防御体系,保护敏感的企业数据免受未经授权的访问和攻击。 ### 回答2: SDP白皮书-Sdp标准规范文档2.0是一个关于软件定义网络(Software-Defined Networking,SDN)的文档。SDN是一种新型的网络架构,通过将网络控制平面与数据平面分离,实现对网络的灵活控制和管理。 SDP白皮书-Sdp标准规范文档2.0对SDN的标准化进行了详细的规范。首先,文档介绍了SDN的基本概念,解释了为什么需要SDN以及其与传统网络架构的区别。然后,文档定义了SDN的架构,包括控制平面、数据平面和用层。控制平面负责对网络进行管理和控制,数据平面负责转发数据包,用层则提供各种网络用和服务。 此外,文档还规定了SDN网络中的各个组件的功能和接口。例如,控制器负责与网络设备交互,并根据用的需求对网络进行配置;交换机和路由器则负责数据包的转发和处理。文档还对SDN网络中的安全性和性能进行了要求和指导,确保网络的可靠性和稳定性。 该文档的2.0版本相比于之前的版本做了一些更新和改进。例如,增加了对新的网络技术和协议的支持,包括IPv6、软定义广域网(SD-WAN)等。此外,该版本还完善了一些功能和接口的规范,提高了网络的灵活性和扩展性。 总之,SDP白皮书-Sdp标准规范文档2.0是一个重要的文档,它定义了SDN网络的标准和规范,为SDN的发展和用提供了重要的参考依据。通过遵守该文档的规范,可以实现对网络的灵活控制和管理,提高网络的性能和安全性。 ### 回答3: SDP白皮书是指“软件定义网络(SDN)-分布式控制平面(SDPCP)白皮书”。这份白皮书旨在定义SDPCP的标准规范和要求,以促进SDN的发展和实施。 SDPCP是SDN架构中的核心组件之一,它负责接收来自用程序的指令,并将其转化为网络设备可理解的指令,从而实现控制网络流量的功能。SDPCP还负责与其他组件通信,如SDN控制器和网络设备,以确保网络的有效管理和运行。 在SDP标准规范文档2.0中,包含了对SDPCP的详细描述和要求。首先,文档规定了SDPCP的基本功能和架构,包括其与其他SDN组件的接口和通信方式。其次,文档对SDPCP的性能和可靠性要求进行了明确规定,以确保它能够满足各种网络环境下的需求。 此外,文档还规定了SDPCP的安全性要求,包括对认证、加密和访问控制的要求,以确保数据和网络的安全性。另外,标准还对SDPCP的配置和管理提供了指导,以便管理员能够轻松地部署和维护SDPCP。 最后,标准规范文档2.0还提供了一些可选的扩展功能和接口,以满足特定用场景下的需求。这些扩展功能包括对SDPCP的垂直集成和自定义,使其能够适不同的用需求。 总的来说,SDP白皮书中的SDPCP标准规范文档2.0对SDN架构中的分布式控制平面提供了详细的描述和规范,以促进SDN的发展和实施,并满足不同网络环境下的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值