實(shí)戰(zhàn)Linux Bluetooth編程(四) L2CAP層編程
作者:Sam (甄峰) sam_code@hotmail.com
(L2CAP協(xié)議簡(jiǎn)介,L2CAP在BlueZ中的實(shí)現(xiàn)以及L2CAP編程接口)
?
一:L2CAP協(xié)議簡(jiǎn)介:
Logical Link Control and AdaptationProtocol(L2CAP)
?
邏輯連接控制和適配協(xié)議 (L2CAP)為上層協(xié)議提供面向連接和無連接的數(shù)據(jù)服務(wù),并提供多協(xié)議功能和分割重組操作。L2CAP充許上層協(xié)議和應(yīng)用軟件傳輸和接收最大長(zhǎng)度為 64K 的L2CAP數(shù)據(jù)包。
L2CAP 基于通道(channel) 的概念。通道 (Channel) 是位于基帶(baseband)連接之上的邏輯連接。每個(gè)通道以多對(duì)一的方式綁定一個(gè)單一協(xié)議 (singleprotocol)。多個(gè)通道可以綁定同一個(gè)協(xié)議,但一個(gè)通道不可以綁定多個(gè)協(xié)議。每個(gè)在通道里接收到的 L2CAP 數(shù)據(jù)包被傳到相應(yīng)的上層協(xié)議。多個(gè)通道可共享同一個(gè)基帶連接。
?
L2CAP處于Bluetooth協(xié)議棧的位置如下:
Bluetooth編程(四)?
也就是說,所有L2CAP數(shù)據(jù)均通過HCI傳輸?shù)絉emote Device。且上層協(xié)議的數(shù)據(jù),大都也通過L2CAP來傳送。
?
?
L2CAP可以發(fā)送Command。例如連接,斷連等等。
Bluetooth編程(四)?
?
下面看Command例子:Connection Request:
Bluetooth編程(四)?
?
其中PSM比較需要注意,L2CAP 使用L2CAP連接請(qǐng)求(Connection Request)命令中的PSM字段實(shí)現(xiàn)協(xié)議復(fù)用。L2CAP可以復(fù)用發(fā)給上層協(xié)議的連接請(qǐng)求,這些上層協(xié)議包括服務(wù)發(fā)現(xiàn)協(xié)議SDP(PSM =0x0001)、RFCOMM(PSM = 0x0003)和電話控制(PSM = 0x0005)等。
?
?
?
?
?
?
二:L2CAP編程方法:
?
L2CAP編程非常重要,它和HCI基本就是LinuxBluetooth編程的基礎(chǔ)了。幾乎所有協(xié)議的連接,斷連,讀寫都是用L2CAP連接來做的。
?
1.創(chuàng)建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 bindsocket");
??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
?unsignedshort?l2_psm;?//與前面PSM對(duì)應(yīng),這一項(xiàng)很重要
?bdaddr_t?l2_bdaddr;?????//RemoteDevice BDADDR
?unsignedshort?l2_cid;?
};
?
4. 發(fā)送數(shù)據(jù)到Remote Device:
send()或write()都可以。
?
5. 接收數(shù)據(jù):
revc() 或read()
?
?
以下為實(shí)例:
注:在Bluetooth下,主動(dòng)去連接的一端作為主機(jī)端。被動(dòng)等別人連接的作為Client端。
?
?
?
?
?
?
背景知識(shí)1:Bluetooth設(shè)備的狀態(tài)
之前HCI編程時(shí),是用?ioctl(HCIGETDEVINFO)得到某個(gè)DeviceInfo(hci_dev_info).其中flags當(dāng)時(shí)解釋的很簡(jiǎn)單。其實(shí)它存放著Bluetooth Device(例如:USBBluetooth Dongle)的當(dāng)前狀態(tài):
其中,UP,Down狀態(tài)表示此Device是否啟動(dòng)起來??梢允褂胕octl(HCIDEVUP)等修改這些狀態(tài)。
另外:就是Inquiry Scan, PAGEScan這些狀態(tài):
Sam在剛開始自己做L2CAP層連接時(shí),使用另一臺(tái)Linux機(jī)器插USB Bluetooth Dongle作RemoteDevice。怎么也沒法使用inquiry掃描到remote設(shè)備,也沒法連接remote設(shè)備,甚至無法使用l2pingping到remote設(shè)備。覺得非常奇怪,后來才發(fā)現(xiàn)Remote Device狀態(tài)設(shè)置有問題。沒有設(shè)置PSCAN和ISCAN。
Inquiry Scan狀態(tài)表示設(shè)備可被inquiry. PageScan狀態(tài)表示設(shè)備可被連接。
#hciconfig hci0 iscan
#hciconfig hci0 pscan
或者:#hciconfig hci0 piscan
就可以設(shè)置為PSCAN或者iSCAN狀態(tài)了。
編程則可以使用ioctl(HCISETSCAN) . dev_opt =SCAN_INQUIRY;dr.dev_opt = SCAN_PAGE;dr.dev_opt = SCAN_PAGE |SCAN_INQUIRY;
則可以inquiry或者connect了。
?