实战Linux Bluetooth编程(四) L2CAP层编程

本文作者的博客地址:http://blog.sina.com.cn/samzhen1977

作者:Sam (甄峰) [email protected]

(L2CAP协议简介,L2CAP在BlueZ中的实现以及L2CAP编程接口)

 

一:L2CAP协议简介:

Logical Link Control and Adaptation Protocol(L2CAP)

 

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

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

 

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


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

 

 

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


 

下面看Command例子:Connection Request:

 

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

 

 

Protocol PSM Reference
SDP 0x0001 See Bluetooth Service Discovery Protocol (SDP), Bluetooth SIG.
RFCOMM 0x0003 See RFCOMM with TS 07.10, Bluetooth SIG.
TCS-BIN 0x0005 See Bluetooth Telephony Control Specification / TCS Binary, Bluetooth SIG.
TCS-BIN-CORDLESS 0x0007 See Bluetooth Telephony Control Specification / TCS Binary, Bluetooth SIG.
BNEP 0x000F See Bluetooth Network Encapsulation Protocal, Bluetooth SIG.
HID_Control 0x0011 See Human Interface Device , Bluetooth SIG.
HID_Interrupt 0x0013 See Human Interface Device, Bluetooth SIG.
UPnP 0x0015 See [ESDP] , Bluetooth SIG.
AVCTP 0x0017 See Audio/Video Control Transport Protocol , Bluetooth SIG.
AVDTP 0x0019 See Audio/Video Distribution Transport Protocol , Bluetooth SIG.
AVCTP_Browsing 0x001B See Audio/Video Remote Control Profile, Bluetooth SIG
UDI_C-Plane 0x001D See 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了。

你可能感兴趣的:(Bluetooth)