本文作者的部落格地址:http://blog.sina.com.cn/samzhen1977
作者:Sam (甄峰) sam_code@hotmail.com
(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了。