Linux下Bluez的编程实现
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
藍(lán)牙的各個協(xié)議棧的簡介?2
1.1、藍(lán)牙技術(shù)?2
1.1、藍(lán)牙協(xié)議棧?2
1.2、藍(lán)牙技術(shù)的特點?4
1.2.1、藍(lán)牙協(xié)議棧體系結(jié)構(gòu)?4
1.2.2、藍(lán)牙協(xié)議棧低層模塊?5
1.2.3、軟件模塊?5
1.3、藍(lán)牙的一些Profile 6
2、Bluez和D-Bus 8
2.1、Bluez和D-Bus體系結(jié)構(gòu)?8
2.2、D-Bus介紹?10
2.3、Bluez的安全接口?14
2.4、Bluez適配器接口?19
2.5、Bluez配對?19
2.6、Bluez綁定?20
3、Bluez編程實現(xiàn)?21
3.1、藍(lán)牙開發(fā)關(guān)鍵技術(shù)剖析?21
3.1.1、連接機(jī)制分析?21
3.1.2、自動連接?21
3.1.3、時鐘設(shè)計?22
3.1.4、配對列表管理?23
3.1.5、藍(lán)牙文件傳輸模式?23
3.2、hci層介紹?23
3.2.1、hci層介紹?23
3.2.2、hci層編程?24
3.3、L2CAP層編程?28
3.3.1、L2CAP協(xié)議簡介?28
3.3.2、L2CAP編程方法?29
3.4、SDP協(xié)議簡介?30
4、Openobex 31
4.1、Openobex簡介?31
4.2、Openobex與bluez編程實現(xiàn)?33
Obexftp 35
5.1、obexftp簡介?35
5.2、基于Obexftp的應(yīng)用程序開發(fā)?35
6、參考資料?35
?
藍(lán)牙的各個協(xié)議棧的簡介
1.1、藍(lán)牙技術(shù)
??藍(lán)牙(Bluetooth)技術(shù)是由Ericsson、IBM、Intel、Nokia和Toshiba公司于1998年5月共同提出開發(fā)的,并聯(lián)合成立了藍(lán)牙特殊利益小組(SIG),負(fù)責(zé)開發(fā)無線協(xié)議規(guī)范并設(shè)定交互操作的需求。其本質(zhì)是設(shè)備間的無線鏈接,意在于代替有線電纜。
1.1、藍(lán)牙協(xié)議棧
協(xié)議棧是指一組協(xié)議的集合,舉個例子,把大象裝到冰箱里,總共要3步。每步就是一個協(xié)議,3步組成一個協(xié)議棧。把應(yīng)用層數(shù)據(jù)包發(fā)出去,也要好幾步,TCP/UDP頭,IP頭,ether頭,每步也是一個協(xié)議。另外每層都有一些特殊的協(xié)議。所有這些統(tǒng)稱協(xié)議棧。藍(lán)牙協(xié)議棧就是SIG定義的一組協(xié)議的規(guī)范,目標(biāo)是允許遵循規(guī)范的藍(lán)牙應(yīng)用能夠進(jìn)行相互間操作,如圖1.1藍(lán)牙協(xié)議棧
?
圖1.1?藍(lán)牙協(xié)議棧
在藍(lán)牙協(xié)議體系中,底層、中間層、應(yīng)用層按序排列構(gòu)成了藍(lán)牙協(xié)議棧,如左圖所示。底層(硬件層)和中間協(xié)議層(軟件層)之間?的接口使用主機(jī)控制器接口(HCI)。HCI是軟硬件之間必不可少的接口,其功能是解釋并傳遞兩層之間的消息和數(shù)據(jù)。軟件通過HCI調(diào)用底層LMP/BB和RF等硬件。HCI以下的功能由藍(lán)牙設(shè)備實施;HCI以上的功能由軟件運(yùn)行,在主機(jī)上實現(xiàn)。HCI對于上、下兩層數(shù)據(jù)的傳輸都是透明的。
在藍(lán)牙協(xié)議棧中,最主要的是藍(lán)牙核心協(xié)議,包括基帶協(xié)議(BP)、鏈路管理協(xié)議(LMP)、鏈接控制和適配協(xié)議(L2CAP)、服務(wù)發(fā)現(xiàn)協(xié)議(SDP)等。藍(lán)牙設(shè)備基本上都需要核心協(xié)議,其他協(xié)議則按藍(lán)牙設(shè)備的需要而選定。
?
1.2、藍(lán)牙技術(shù)的特點
1.2.1、藍(lán)牙協(xié)議棧體系結(jié)構(gòu)
整個藍(lán)牙協(xié)議體系結(jié)構(gòu)可分為底層硬件模塊、中間協(xié)議層和高端應(yīng)用層三大部分。鏈路管理層(LMP)、基帶層(BBP)和藍(lán)牙無線電信道構(gòu)成藍(lán)牙的底層模塊。BBP層負(fù)責(zé)跳頻和藍(lán)牙數(shù)據(jù)及信息幀的傳輸。
LMP層:負(fù)責(zé)連接的建立和拆除以及鏈路的安全和控制,它們?yōu)樯蠈榆浖K提供了不同的訪問人口,但是兩個模塊接口之間的消息和數(shù)據(jù)傳遞必須通過藍(lán)牙主機(jī)控制器接口的解釋才能進(jìn)行。也就是說,中間協(xié)議層包括邏輯鏈路控制與適配協(xié)議(L2CAP)、服務(wù)發(fā)現(xiàn)協(xié)議(SDP)、串口仿真協(xié)議(RFCOMM)和電話控制協(xié)議規(guī)范(TCS)。
L2CAP:完成數(shù)據(jù)拆裝、服務(wù)質(zhì)量控制、協(xié)議復(fù)用和組提取等功能,是其他上層協(xié)議實現(xiàn)的基礎(chǔ),因此也是藍(lán)牙協(xié)議棧的核心部分。
SDP:為上層應(yīng)用程序提供一種機(jī)制來發(fā)現(xiàn)網(wǎng)絡(luò)中可用的服務(wù)及其特性。在藍(lán)牙協(xié)議棧的最上部是高端應(yīng)用層,它對應(yīng)于各種應(yīng)用模型的剖面,是剖面的一部分。目前定義了13種剖面。
1.2.2、藍(lán)牙協(xié)議棧低層模塊
藍(lán)牙的低層模塊是藍(lán)牙技術(shù)的核心,是任何藍(lán)牙設(shè)備都必須包括的部分。
藍(lán)牙工作在2.4GHZ的ISM頻段。采用了藍(lán)牙結(jié)束的設(shè)備講能夠提供高達(dá)720kbit/s?的數(shù)據(jù)交換速率。
藍(lán)牙支持電路交換和分組交換兩種技術(shù),分別定義了兩種鏈路類型,即面向連接的同步鏈路(SCO)和面向無連接的異步鏈路(ACL)。
為了在很低的功率狀態(tài)下也能使藍(lán)牙設(shè)備處于連接狀態(tài),藍(lán)牙規(guī)定了三種節(jié)能狀態(tài),即停等(Park)狀態(tài)、保持(Hold)狀態(tài)和呼吸(Sniff)狀態(tài)。這幾種工作模式按照節(jié)能效率以升序排依次是:Sniff模式、Hold模式、Park模式。
藍(lán)牙采用三種糾錯方案:1/3前向糾錯(FEC)、2/3前向糾錯和自動重發(fā)(ARQ)。前向糾錯的目的是減少重發(fā)的可能性,但同時也增加了額外開銷。然而在一個合理的無錯誤率環(huán)境中,多余的投標(biāo)會減少輸出,故分組定義的本身也保持靈活的方式,因此,在軟件中可定義是否采用FEC。一般而言,在信道的噪聲干擾比較大時藍(lán)牙系統(tǒng)會使用前向糾錯方案,以保證通信質(zhì)量:對于SCO鏈路,使用1/3前向糾錯;對于ACL鏈路,使用2/3前向糾錯。在無編號的自動請求重發(fā)方案中,一個時隙傳送的數(shù)據(jù)必須在下一個時隙得到收到的確認(rèn)。只有數(shù)據(jù)在收端通過了報頭錯誤檢測和循環(huán)冗余校驗(CRC)后認(rèn)為無錯時,才向發(fā)端發(fā)回確認(rèn)消息,否則返回一個錯誤消息。
藍(lán)牙系統(tǒng)的移動性和開放性使得安全問題變得及其重要。雖然藍(lán)牙系統(tǒng)所采用的調(diào)頻技術(shù)就已經(jīng)提供了一定的安全保障,但是藍(lán)牙系統(tǒng)仍然需要鏈路層和應(yīng)用層的安全管理。在鏈路層中,藍(lán)牙系統(tǒng)提供了認(rèn)證、加密和密鑰管理等功能。每個用戶都有一個個人標(biāo)識碼(PIN),它會被譯成128bit的鏈路密鑰(Link Key)來進(jìn)行單雙向認(rèn)證。一旦認(rèn)證完畢,鏈路就會以不同長度的密碼(Encryphon Key)來加密(此密碼以shit為單位增減,最大的長度為128bit)鏈路層安全機(jī)制提供了大量的認(rèn)證方案和一個靈活的加密方案(即允許協(xié)商密碼的長度)。當(dāng)來自不同國家的設(shè)備互相通信時,這種機(jī)制是極其重要的,因為某些國家會指定最大密碼長度。藍(lán)牙系統(tǒng)會選取微微網(wǎng)中各個設(shè)備的最小的最大允許密碼長度。例如,美國允許128bit的密碼長度,而西班牙僅允許48bit,這樣當(dāng)兩國的設(shè)備互通時,將選擇48bit來加密。藍(lán)牙系統(tǒng)也支持高層協(xié)議棧的不同應(yīng)用體內(nèi)的特殊的安全機(jī)制。例如兩臺計算機(jī)在進(jìn)行商業(yè)卡信息交流時,一臺計算機(jī)就只能訪問另一臺計算機(jī)的該項業(yè)務(wù),而無權(quán)訪問其他業(yè)務(wù)。藍(lán)牙安全機(jī)制依賴PIN在設(shè)備間建立信任關(guān)系,一旦這種關(guān)系建立起來了,這些PIN就可以存儲在設(shè)備中以便將來更快捷地連接。
1.2.3、軟件模塊
L2CAP是數(shù)據(jù)鏈路層的一部分,位于基帶協(xié)議之上。L2CAP向上層提供面向連接的和無連接的數(shù)據(jù)服務(wù),它的功能包括:協(xié)議的復(fù)用能力、分組的分割和重新組裝(Segmentation And Reaassembly)以及提取(Group Abstraction)。L2CAP允許高層協(xié)議和應(yīng)用發(fā)送和接受高達(dá)64K Byte的數(shù)據(jù)分組。
SDP為應(yīng)用提供了一個發(fā)現(xiàn)可用協(xié)議和決定這些可用協(xié)議的特性的方法。藍(lán)牙環(huán)境下的服務(wù)發(fā)現(xiàn)與傳統(tǒng)的網(wǎng)絡(luò)環(huán)境下的服務(wù)發(fā)現(xiàn)有很大的不同,在藍(lán)牙環(huán)境下,移動的RF環(huán)境變化很大,因此業(yè)務(wù)的參數(shù)也是不斷變換的。SDP將強(qiáng)調(diào)藍(lán)牙環(huán)境的獨(dú)特的特性。藍(lán)牙使用基于客戶/服務(wù)器機(jī)制定義了根據(jù)藍(lán)牙服務(wù)類型和屬性發(fā)現(xiàn)服務(wù)的方法,還提供了服務(wù)瀏覽的方法。
RFCOMM是射頻通信協(xié)議,它可以仿真串行電纜接口協(xié)議,符合ETSI0710串口仿真協(xié)議。通過RFCOMM,藍(lán)牙可以在無線環(huán)境下實現(xiàn)對高層協(xié)議,如PPP、TCP/IP、WAP等的支持。另外,RFCOMM可以支持AT命令集,從而可以實現(xiàn)移動電話機(jī)和傳真機(jī)及調(diào)制解調(diào)器之間的無線連接。
藍(lán)牙對語音的支持是它與WLAN相區(qū)別的一個重要的標(biāo)志。藍(lán)牙電話控制規(guī)范是一個基于ITU-T建議Q.931的采用面向比特的洗衣,它定義了用于藍(lán)牙設(shè)備間建立語音和數(shù)據(jù)呼叫的呼叫控制信令以及用于處理藍(lán)牙TCS設(shè)備的移動性管理過程。
1.3、藍(lán)牙的一些Profile
藍(lán)牙里面profile的定義,profile既是配置文件,配置文件定義了可能的應(yīng)用,藍(lán)牙配置文件表達(dá)了一般行為,藍(lán)牙設(shè)備可以通過這些行為與其它設(shè)備進(jìn)行通信。藍(lán)牙技術(shù)定義了廣泛的配置文件,描述了許多不同類型的使用案例。按照藍(lán)牙規(guī)格中提供的指導(dǎo),開發(fā)商可以創(chuàng)建應(yīng)用程序以與其它符合藍(lán)牙規(guī)格的設(shè)備協(xié)同工作。 到目前為止,藍(lán)牙一共有22個profile,在www.bluetooth.com§上有詳細(xì)的文檔說明。
已經(jīng)實現(xiàn)了的協(xié)議棧:
Widcomm:?第一個windows上的協(xié)議棧,由Widcomm公司開發(fā),也就是現(xiàn)在的Broadcom .
Microsoft Windows stack: Windows XP SP2中包括了這個內(nèi)建的協(xié)議棧,開發(fā)者也可以調(diào)用其API開發(fā)第三方軟件。
Toshiba stack:?它也是基于Windows的,不支持第三方開發(fā),但它把協(xié)議棧授權(quán)給一些laptop商)。它支持的Profile有:?SPP, DUN, FAX, LAP, OPP, FTP, HID, HCRP, PAN, BIP, HSP, HFP , A2DP, AVRCP, GAVDP)
BlueSoleil:?著名的IVT公司的產(chǎn)品,這個應(yīng)該是個中國公司。該產(chǎn)品可以用于桌面和嵌入式,他也支持第三方開發(fā),DUN, FAX, HFP, HSP, LAP, OBEX, OPP, PAN SPP, AV, BIP, FTP, GAP, HID, SDAP, and SYNC。
Bluez: Linux官方協(xié)議棧,該協(xié)議棧的上層用Socket封裝,便于開發(fā)者使用,通過DBUS與其它應(yīng)用程序通信。
Affix: NOKIA公司的協(xié)議棧,在Symbian系統(tǒng)上運(yùn)行。
BlueDragon:東軟公司產(chǎn)品,支持的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。
BlueMagic:美國Open Interface?公司for portable embedded divce的協(xié)議棧,iphone(apple),nav-u(sony)等很多電子產(chǎn)品都用該商業(yè)的協(xié)議棧,BlueMagic 3.0是第一個通過bluetooth?協(xié)議棧1.1認(rèn)證的協(xié)議棧,那么我現(xiàn)在就在用它,那么該棧用起來簡單,API清晰明了。實現(xiàn)了的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.
BCHS-Bluecore Host Software:?藍(lán)牙芯片CSR的協(xié)議棧,同時他也提供了一些上層應(yīng)用的Profile的庫。
Windows CE:微軟給Windows CE開發(fā)的協(xié)議棧,但是windows ce本身也支持其它的協(xié)議棧。
BlueLet:IVT公司for embedded product的清量級協(xié)議棧。
2、Bluez和D-Bus
2.1、Bluez和D-Bus體系結(jié)構(gòu)
?
The?BlueZ?D-Bus?interfaces aim to provide seamless Bluetooth technology integration into the desktop. A central Bluetooth daemon "hcid"(planned to be renamed to bluetoothd) is responsible for take care of all tasks that can’t or shouldn’t be handled inside the Linux kernel. These jobs include PIN code and link key management for the authentication and encryption, caching of device names and services and also central control of the Bluetooth hardware. The interface exported allows to abstract the internals of GNOME, KDE,Maemo,?OpenMoko, ... applications from any technical details of the Bluetooth specification. Even other application will get access to the Bluetooth technology without any hassle.
Bluez和D-bus接口,提供了藍(lán)牙技術(shù)和桌面系統(tǒng)的完美集成。藍(lán)牙的中心守護(hù)進(jìn)程hcid的職責(zé)就是處理那些不能被linux內(nèi)核處理的任務(wù),包括處理為鑒權(quán)和加密過程中需要的PIN碼和密鑰、緩存設(shè)備的名稱和服務(wù)類型,同時也是藍(lán)牙硬件的控制中心。
The BlueZ?D-Bus?services are exported through the system message bus. Every D-Bus enabled desktop has a system message bus instance running. This bus is used to broadcast system events, such as new hardware devices, network connection status, and so forth. The session message bus is not suitable for this architecture since the Bluetooth hardware/connections are shared by all desktop sessions.
Bluez和D-bus服務(wù)通過系統(tǒng)消息總線提供。每個D - Bus使桌面有一個系統(tǒng)消息總線實例運(yùn)行。這個bus是用來廣播系統(tǒng)時間,如新的硬件設(shè)備、網(wǎng)絡(luò)連接狀態(tài)等等。會話消息總線對這種體系結(jié)構(gòu)是不適合的,因為藍(lán)牙硬件/連接被所有桌面會話共享。
The BlueZ D-Bus Architecture goal are:
-
Abstract Bluetooth HCI commands/events。
-
Provide an easy interface to setup Bluetooth adapter and manage the services
Bluez和D-bus體系結(jié)構(gòu)的目標(biāo):
-
抽象hci層命令和事件。
-
提供簡單的接口來啟動藍(lán)牙適配器和管理藍(lán)牙服務(wù)。
The hcid is the main entity of the architecture. It implements methods to setup the Bluetooth adapters, retrieve remote device properties, control the pairing procedure and control the services registration/searches. The following figure shows a high level relationship between the entities.
Hcid是該體系結(jié)構(gòu)的主體。它實現(xiàn)啟動藍(lán)牙適配器的方法、獲取遠(yuǎn)端設(shè)備屬性、控制配對過程和控制服務(wù)的注冊和搜索。下圖顯示了一個高層次的實體之間的關(guān)系。
?
2.2、D-Bus介紹
什么是D-Bus?
D-BUS?是一種進(jìn)程間通信的方式,從架構(gòu)上來說,分為三層:
一個庫,libdbus,允許2個進(jìn)程間交換信息。
一個消息總線守護(hù)進(jìn)程, 它使用libdbus庫。其他進(jìn)程都可以與它連接。它可以將消息從一個進(jìn)程發(fā)給另外任意數(shù)量的其他進(jìn)程。現(xiàn)在有一些基于特定應(yīng)用框架的dbus庫函數(shù)封裝,例如libdbus-glib?和libdbus-qt,也有與一些語言綁定的形式,例如Python等。這些封裝的API旨在令D-BUS編程更加簡單,libdbus傾向于提供更低層次的調(diào)用。很多libdbus API只在綁定的組件中可用。
libdbus僅支持一對一的連接,就像原始?socket通訊方式一樣。但它傳遞的不是以字節(jié)為單位的數(shù)據(jù)流,而是具有一定意義的消息包。消息的消息頭部表示消息種類,消息體用來裝載數(shù)據(jù)。Libdbus也可以允許實現(xiàn)特定的傳輸通道,從而來完成比如像認(rèn)證之類的應(yīng)用細(xì)節(jié)(libdbus also abstracts the exact transport used (sockets vs. whatever else), and handles details such as authentication.)。
消息總線守護(hù)進(jìn)程將D-bus上連接的所有程序構(gòu)成一個輪形hub。Libdbus為中心,它和應(yīng)用程序建立一對一的連接。每個應(yīng)用程序通過通道發(fā)送消息到消息總線,然后總線進(jìn)程將消息轉(zhuǎn)發(fā)到其他連接到hub的應(yīng)用程序。可以把消息總線理解為一個路由器。Dbus服務(wù)在一個操作系統(tǒng)中存在多個進(jìn)程。第一個進(jìn)程是一個全局進(jìn)程,就如sendmail?或Apache?的系統(tǒng)守護(hù)進(jìn)程一樣。這個進(jìn)程具有高度的安全限制,一般用于系統(tǒng)進(jìn)程間的通訊。其他的dbus進(jìn)程都是用戶進(jìn)程,針對于每個登錄的用戶建立。這些實例允許用戶會話中的應(yīng)用程序相互通信。Dbus全局進(jìn)程和用戶進(jìn)程是相互獨(dú)立的,他們并沒有內(nèi)在的依賴關(guān)系。
D-Bus應(yīng)用
有很多種IPC或者網(wǎng)絡(luò)通信系統(tǒng),如:CORBA,DCE,DCOM,DCOP,XML-RPC,SOAP,MBUS,ICE等。Dbus的目的主要是下面兩點:
在同一個桌面會話中,進(jìn)行桌面應(yīng)用程序之間的通訊。
桌面程序和內(nèi)核或者守護(hù)進(jìn)程之間通信。
D-Bus概念
對象路徑(Native Objects and Object Paths):D-Bus的底層接口,和libdbus相關(guān),它提供一種叫對象路徑(object path),用于讓高層接口綁定到各個對象中去,允許遠(yuǎn)端應(yīng)用程序指向他們。Object path就像一個文件路徑。
方法和信號(Methods and Signals):每個對象都有一些成員,有兩種成員:方法(methods)和信號(signals),在對象中,方法可以被調(diào)用。信號會被廣播,感興趣的對象可以處理這個信號,同時信號中也可以帶有相關(guān)的數(shù)據(jù)。
接口(Interfaces):每個對象都有一個或者多個接口,一個接口就是多個方法和信號的集合。這個概念和Glib, Qt或者Java中的是一致的。接口定義了對象實例的類型。dbus使用簡單的命名空間字符串來表示接口,如org.freedesktop.Introspectable。可以說dbus接口相當(dāng)于C++中的純虛類。
代理(Proxies):使用代理對象就是讓調(diào)用者感覺在直接使用遠(yuǎn)程對象一樣。d-bus的底層接口完成了一些比較低級和繁瑣的調(diào)用過程,比如必須先調(diào)用創(chuàng)建方法形成消息包,然后發(fā)送,然后等待接受和處理返回的消息。所以,高層的接口就可以使用代理對象提供的接口屏蔽這些細(xì)節(jié)。所以,當(dāng)調(diào)用代理對象的方法時,代理內(nèi)部會轉(zhuǎn)換成dbus的方法調(diào)用,等待消息返回,對返回結(jié)果解包,返回給相應(yīng)的方法。可以看看下面的例子,使用dbus底層接口編寫的代碼:
Message message = new Message(”/remote/object/path”, “MethodName”, arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {
} else {
Object returnValue = reply.getReturnValue();
}
使用代理對象編寫的代碼:
Proxy proxy = new Proxy(getBusConnection(), “/remote/object/path”);
Object returnValue = proxy.MethodName(arg1, arg2);
客戶端代碼減少很多。
總線名稱(Bus Names):當(dāng)一個應(yīng)用程序連接上bus daemon時,daemon會分配一個唯一的名字給它。以冒號(:)開始,這些名字在daemon的生命周期中是不會改變的,可以認(rèn)為這些名字就是一個?IP地址。當(dāng)這個名字映射到應(yīng)用程序的連接上時,應(yīng)用程序可以說擁有這個名字。同時應(yīng)用可以聲明額外的容易理解的名字,比如可以取一個名字?com.mycompany.TextEditor,可以認(rèn)為這些名字就是一個域名。其他應(yīng)用程序可以往這個名字發(fā)送消息,執(zhí)行各種方法。
名字還有第二個重要的用途,可以用于跟蹤應(yīng)用程序的生命周期。當(dāng)應(yīng)用退出(或者崩潰)時,與bus的連接將被OS內(nèi)核關(guān)掉,bus將會發(fā)送通知,告訴剩余的應(yīng)用程序,該程序已經(jīng)丟失了它的名字。名字還可以檢測應(yīng)用是否已經(jīng)啟動,這可以用來實現(xiàn)單實例啟動程序。
地址(Addresses):使用d-bus的應(yīng)用程序既可以是server也可以是client,server監(jiān)聽到來的連接,client連接到server,一旦連接建立,消息就可以流轉(zhuǎn)。如果使用dbus daemon,所有的應(yīng)用程序都是client,daemon監(jiān)聽所有的連接,應(yīng)用程序初始化連接到daemon。dbus地址指明server將要監(jiān)聽的地方,client將要連接的地方,例如,地址:unix:path=/tmp/abcdef表明?server將在/tmp/abcdef路徑下監(jiān)聽unix域的socket,client也將連接到這個socket。一個地址也可以指明是TCP /IP的socket,或者是其他的。
當(dāng)使用bus daemon時,libdbus會從環(huán)境變量中(DBUS_SESSION_BUS_ADDRESS)自動認(rèn)識“會話daemon”的地址。如果是系統(tǒng)?daemon,它會檢查指定的socket路徑獲得地址,也可以使用環(huán)境變量(DBUS_SESSION_BUS_ADDRESS)進(jìn)行設(shè)定。當(dāng)dbus中不使用daemon時,需要定義哪一個應(yīng)用是server,哪一個應(yīng)用是client,同時要指明server的地址,這不是很通常的做法。
D-bus工作原理
Calling a Method – Behind the Scenes
在dbus中調(diào)用一個方法包含了兩條消息,進(jìn)程A向進(jìn)程B發(fā)送方法調(diào)用消息,進(jìn)程B向進(jìn)程A發(fā)送應(yīng)答消息。所有的消息都由daemon進(jìn)行分派,每個調(diào)用的消息都有一個不同的序列號,返回消息包含這個序列號,以方便調(diào)用者匹配調(diào)用消息與應(yīng)答消息。調(diào)用消息包含一些參數(shù),應(yīng)答消息可能包含錯誤標(biāo)識,或者包含方法的返回數(shù)據(jù)。
方法調(diào)用的一般流程:
1.使用不同語言綁定的dbus高層接口,都提供了一些代理對象,調(diào)用其他進(jìn)程里面的遠(yuǎn)端對象就像是在本地進(jìn)程中的調(diào)用一樣。應(yīng)用調(diào)用代理上的方法,代理將構(gòu)造一個方法調(diào)用消息給遠(yuǎn)端的進(jìn)程。
2.在DBUS的底層接口中,應(yīng)用需要自己構(gòu)造方法調(diào)用消息(method call message),而不能使用代理。
3.方法調(diào)用消息里面的內(nèi)容有:目的進(jìn)程的bus name,方法的名字,方法的參數(shù),目的進(jìn)程的對象路徑,以及可選的接口名稱。
4.方法調(diào)用消息是發(fā)送到bus daemon中的。
5.bus daemon查找目標(biāo)的bus name,如果找到,就把這個方法發(fā)送到該進(jìn)程中,否則,daemon會產(chǎn)生錯誤消息,作為應(yīng)答消息給發(fā)送進(jìn)程。
6.目標(biāo)進(jìn)程解開消息,在dbus底層接口中,會立即調(diào)用方法,然后發(fā)送方法的應(yīng)答消息給daemon。在dbus高層接口中,會先檢測對象路徑,接口,方法名稱,然后把它轉(zhuǎn)換成對應(yīng)的對象(如GObject,QT中的QObject等)的方法,然后再將應(yīng)答結(jié)果轉(zhuǎn)換成應(yīng)答消息發(fā)給daemon。
7.bus daemon接受到應(yīng)答消息,將把應(yīng)答消息直接發(fā)給發(fā)出調(diào)用消息的進(jìn)程。
8.應(yīng)答消息中可以包容很多返回值,也可以標(biāo)識一個錯誤發(fā)生,當(dāng)使用綁定時,應(yīng)答消息將轉(zhuǎn)換為代理對象的返回值,或者進(jìn)入異常。
bus daemon不對消息重新排序,如果發(fā)送了兩條消息到同一個進(jìn)程,他們將按照發(fā)送順序接受到。接受進(jìn)程并需要按照順序發(fā)出應(yīng)答消息,例如在多線程中處理這些消息,應(yīng)答消息的發(fā)出是沒有順序的。消息都有一個序列號可以與應(yīng)答消息進(jìn)行配對。
Emitting a Signal – Behind the Scenes
在dbus中一個信號包含一條信號消息,一個進(jìn)程發(fā)給多個進(jìn)程。也就是說,信號是單向的廣播。信號可以包含一些參數(shù),但是作為廣播,它是沒有返回值的。
信號觸發(fā)者是不了解信號接受者的,接受者向daemon注冊感興趣的信號,注冊規(guī)則是”match rules”,記錄觸發(fā)者名字和信號名字。daemon只向注冊了這個信號的進(jìn)程發(fā)送信號。
信號的一般流程如下:
當(dāng)使用dbus底層接口時,信號需要應(yīng)用自己創(chuàng)建和發(fā)送到daemon,使用dbus高層接口時,可以使用相關(guān)對象進(jìn)行發(fā)送,如Glib里面提供的信號觸發(fā)機(jī)制。
信號包含的內(nèi)容有:信號的接口名稱,信號名稱,發(fā)送進(jìn)程的bus name,以及其他參數(shù)。
任何進(jìn)程都可以依據(jù)”match rules”注冊相關(guān)的信號,daemon有一張注冊的列表。
daemon檢測信號,決定哪些進(jìn)程對這個信號感興趣,然后把信號發(fā)送給這些進(jìn)程
每個進(jìn)程收到信號后,如果是使用了dbus高層接口,可以選擇觸發(fā)代理對象上的信號。如果是dbus底層接口,需要檢查發(fā)送者名稱和信號名稱,然后決定怎么做。
2.3、Bluez的安全接口
pin_helper concept has been removed starting with bluez-utils 3.X. and has been replaced with a feature called passkey agents. An application that wants to handle passkey requests must use the "hcid" security interface to register a passkey agent. Currently, two types of passkey agents are supported: default and device specific. A "specific" passkey agent handles all passkey requests for a given remote device while a default handles all requests for which a specific agent was not found. "specific" passkey agents are useful to address pre-defined passkey values or environments where the user interaction is not allowed/difficult.
When the?CreateBonding?method is called the "hcid" daemon will verify if there is a link key stored in the file system. If it is available an error is returned, and if not, a D-Bus message is sent to the registered passkey agent asking for a passkey.
Each Passkey Agent is represented by a D-Bus object path. The "hcid" distinguishes the agents based on their unique bus names and their object paths.
Pin_help的理念在bluez-util 3.x時已經(jīng)被移除,并被密鑰代理所代替。任何想處理密鑰請求的應(yīng)用程序必須使用hcid安全接口來注冊密鑰代理。現(xiàn)在支持兩種類型的密鑰代理:默認(rèn)和設(shè)備特定。一個"特定"的密鑰代理處理所有遠(yuǎn)端的密鑰請求,一個默認(rèn)的密鑰代理,處理特定的代理所沒有發(fā)現(xiàn)的所有密鑰請求。"特定"的密鑰代理對處理那些和用戶交互困難或者不允許用戶交互的設(shè)備的預(yù)定義的密鑰值或環(huán)境非常有用。
當(dāng)CreateBonding方法被調(diào)用時,hcid守護(hù)進(jìn)程將確認(rèn)當(dāng)前的文件系統(tǒng)中是否保存了鏈接密鑰,若可以找到,就返回一個錯誤。若找不到,D-Bus消息將被發(fā)出來為密鑰請求的設(shè)備,注冊一個密鑰代理。每個密鑰代理被D-Bus對象路徑所體現(xiàn),hcid依據(jù)唯一的總線名稱和對象路徑來區(qū)分代理。
Architecture
-
Step 1: Represents the passkey agent registration
-
Step 2: Represents a client calling?CreateBonding
-
Step 3: Represents the hcid asking for a passkey valu
體系結(jié)構(gòu)
-
第一步:表示密鑰代理注冊
-
第二步:表示客戶調(diào)用CreateBonding
-
第三步:代表hcid請求密鑰值。
Message Flow
In the following figure, the "CreateBonding" method call is hidden. The "PIN Request" HCI event is generated when there is not a link available in the file system. In this case "Link Key Request Negative Reply" command is sent triggering the "Pin Request" event.
在下面得圖表中,CreateBonding方法的調(diào)用被隱藏。當(dāng)沒有一個可用鏈接時,"PIN Request"HCI層事件被產(chǎn)生,在這種情況下,"Link Key Request Negative Reply"命令被發(fā)送來回應(yīng)"Pin Request"?事件。
-
Step 1: Represents the D-Bus message sent to register the default/device specific passkey agent.
-
Step 2: Represents the HCI "PIN Request" event sent by the Bluetooth Host Controller.
-
Step 3: Represents the D-Bus message sent to the default/device speficic passkey agent requesting a passkey.
-
Step 4: Represents the "Auth Complete" event where the status contains "LMP Response Timeout"(The remote didn't type the passkey).
-
Step 5: Represents the "hcid" issuing a "Cancel" to a previous Request call.
-
Step 6: Represents the D-Bus message sent to release the passkey agent: basically sent when the hcid exits.
-
第一步:D-Bus發(fā)送消息來注冊默認(rèn)的或者特定的密鑰代理。
-
第二步:藍(lán)牙主機(jī)控制器發(fā)送HCI?層"PIN Request"事件。
-
第三步:D-Bus發(fā)送消息到默認(rèn)的或者特定的密鑰代理,請求密鑰。
-
第四步:"Auth Complete"完成事件,如果這種狀態(tài)包含"LMP Response Timeout"。
-
第五步:hcid發(fā)出“Cancel”命令給D-Bus。
-
第六步:當(dāng)hcid退出時,D - Bus的信息發(fā)送到釋放密鑰代理
2.4、Bluez適配器接口
Description?
The "Adapter" interface provides methods to setup the local adapter(s), search remote devices, pairing and search for services.
This interface is considered stable and no change is planned.
For each available adapter the hcid register an adapter object instance provided in the path "/org/bluez/hci{0, 1, 2, ...}".
適配器接口提供,啟動適配器、搜索設(shè)別、配對、和搜索服務(wù)的方法。這個接口被認(rèn)為是穩(wěn)定的,沒有改變計劃。對每個可用的適配器,hcid為其注冊一個適配器對象實例,在路徑?"/org/bluez/hci{0, 1, 2, ...}"里。
2.5、Bluez配對
Description
Services that need ask the user to accept/reject an operation such as accept OBEX objects or accept an incoming connection can use the?Security?API to request the userspace registered authorization agent responsible.
Development warnings/recommendations
Just one Authorization request per time is allowed.
NotConnected error is returned if a connection is not found between the devices is not found.
Trusted Devices
The BlueZ daemon keeps a list of trusted devices. Trusted means that authorization is not required to accept incoming connections or other operations that need the user response. Once a given device is added to the list, the BlueZ daemon will reply authorized without call the Authorization agent.
Authorization Agent
Authorization agents are applications responsible for address authorization requests. For more information check the BlueZ D-Bus API and implementation references:
-
utils/daemon/auth-agent.c is a authorization agent implementation able to handle device specific and default
-
bluez-gnome?passkey/authorization agent implementation distributed by the BlueZ community
Canceling
For security reason, only the requestor can cancel a pending authorization operation.
2.6、Bluez綁定
Description
The purpose of bonding is to create a relation between two Bluetooth devices based on a common link key (a bond). The link key is created and exchanged(pairing) during the bonding procedure and is expected to be stored by both Bluetooth devices, to be used for future authentication[definition from Bluetooth Core Spec].
The Bonding procedure is done through the BlueZ?Adapter?interface.
Development warnings/recommendations
Just one Bonding request per time is allowed.
Bonding is not allowed if there is a discovery running.
Pending remote name is canceled if a CreateBonding message is received.
The Bonding takes some seconds, therefore it is recommended set a D-Bus callback to handle the message reply for this operation.
Passkey Agent
Currently, two types of passkey agents are supported:
Device Specific: handles all passkey requests for a given remote device
Default: Handles the remaining requests(not addressed by device specific agents)
For more information check the BlueZ D-Bus API and implementation references:
-
utils/daemon/passkey-agent.c is a passkey agent implementation able to handle device specific and default
-
bluez-gnome?passkey agent implementation distributed by the BlueZ community=
3、Bluez編程實現(xiàn)
3.1、藍(lán)牙開發(fā)關(guān)鍵技術(shù)剖析
3.1.1、連接機(jī)制分析
物理信道(physical channel)是藍(lán)牙系統(tǒng)的最底層結(jié)構(gòu),通過偽隨機(jī)跳頻序列、發(fā)送時槽定時、接入碼及幀頭編碼來表征。藍(lán)牙針對不同應(yīng)用定義了一系列物理信道,包括用于匹克網(wǎng)內(nèi)設(shè)通信的匹克網(wǎng)物理信道、用于尋呼設(shè)備的尋呼掃描物理信道和用于查找設(shè)備的查找掃描物理信道。兩臺設(shè)備必須采用相同的物理信道才能進(jìn)行通信。
主從設(shè)備建立連接的過程就是建立相同匹克網(wǎng)信道的過程,該過程確保主從設(shè)備以同樣的定時和次序進(jìn)行載波頻率的跳變,進(jìn)行數(shù)據(jù)傳輸,同時可以根據(jù)匹克網(wǎng)接入碼和幀頭編碼進(jìn)行數(shù)據(jù)過濾和解析,避免和其他設(shè)備在同一個頻段上的相撞。
尋呼掃描物理信道(page scan physical channel)用于主設(shè)備尋呼從設(shè)備,是設(shè)備建立連接的必經(jīng)階段。尋呼掃描跳頻序列和尋呼請求幀的設(shè)備接入碼(DAC)是由從設(shè)備物理地址運(yùn)算,處于可被連接模式的從設(shè)備以固定的周期(由page scan interval決定)在一個固定的時間窗(由page scan window決定)內(nèi)以某個跳頻頻率監(jiān)聽主設(shè)備的尋呼請求。
3.1.2、自動連接
為了方便用戶的使用,大多藍(lán)牙設(shè)備都實現(xiàn)了自動連接功能,根據(jù)以上對藍(lán)牙連接機(jī)制的分析,設(shè)計自動連接方案時必須考慮設(shè)備在不同的工作狀態(tài)下采用不同的物理信道和跳頻序列,而不能按照人為的邏輯隨意設(shè)置,否則會給用戶帶來不便。
以車載免提裝置為例,上電自動連接對駕駛員來說可以提高使用車載免提的自覺性,降低行車期間通話帶來的風(fēng)險。采用搜索方式判斷設(shè)備是否在有效范圍內(nèi),按照優(yōu)先級從低到高連接,最后一次連接的手機(jī)為優(yōu)先級最高的,然后按照配對列表的逆序而優(yōu)先級依次降低。鏈路丟失后的自動連接只針對最后一部手機(jī),這樣可以實現(xiàn)服務(wù)的連續(xù)性。
3.1.3、時鐘設(shè)計
藍(lán)牙核心規(guī)范要求時鐘頻率精度為±20ppm,如果載波頻率不穩(wěn)定,則會發(fā)生“過零點”錯誤(zero-cross)。載波頻率由本地時鐘(晶振)做為PLL的參考時鐘倍頻產(chǎn)生。一般說來,藍(lán)牙設(shè)備的時鐘設(shè)計便是指晶振電路的設(shè)計及微調(diào)。決定晶振工作精度的兩個重要參數(shù)是制造公差和溫度穩(wěn)定度,如果選擇有源晶振作為本地時鐘,需要滿足:制造公差+溫度穩(wěn)定度≤±20ppm,有源晶振內(nèi)部集成晶體和相應(yīng)的振蕩電路,匹配精確,頻率穩(wěn)定性高,而且抗干擾性能好,缺點是成本較高。§
圖1?????震蕩電路設(shè)計
本文自行設(shè)計的振蕩電路如圖1所示。
選擇溫度穩(wěn)定性高、制造公差低的高精度晶體,通過振蕩電路設(shè)計實現(xiàn)諧振頻率的精確調(diào)整,這是由晶體負(fù)載電容的匹配及可調(diào)實現(xiàn)的。負(fù)載電容是指CRY_IN和CRY_OUT兩端的電容值,在晶體的CRY_IN引腳上并聯(lián)一個可調(diào)電容,調(diào)整該電容便可以對諧振頻率進(jìn)行精密微調(diào)。晶體負(fù)載電容計算公式如下:
Cload=Cint+(Cin+Ctrim)×Cout/(Cin+Ctrim+Cout)
Cint包括IC內(nèi)部電容(一般為固定值)以及PCB雜散電容(3pF~5pF)。
3.1.4、配對列表管理
為了保證鏈路級的安全,藍(lán)牙通信要求設(shè)備在連接建立前進(jìn)行“雙向認(rèn)證”。認(rèn)證成功的前提是設(shè)備雙方存儲了相同的鏈路密鑰Kab,配對是產(chǎn)生初始密鑰Kint的階段,Kint由PIN碼、從設(shè)備藍(lán)牙地址和主設(shè)備發(fā)給從設(shè)備的一個隨機(jī)數(shù)由一套固定的算法計算出來,只要PIN碼一致,主從設(shè)備生成的的Kint也是一致的。鏈路密鑰的輸入是主從設(shè)備的藍(lán)牙地址和主從設(shè)備各一隨機(jī)數(shù),只要主從設(shè)備能互換隨機(jī)數(shù),便能得到一致的Kab。主設(shè)備將隨機(jī)數(shù)RandA與Kint異或的結(jié)果發(fā)給從設(shè)備,從設(shè)備只將該結(jié)果與Kint異或便得到RandA,即
§
配對列表的管理包括添加、替代及刪除,添加配對設(shè)備是在非易失性存儲中存儲該設(shè)備的藍(lán)牙地址及Kab。刪除配對設(shè)備需要謹(jǐn)慎處理,如果存在連接,需要先斷開連接然后刪除,因為如果刪掉該設(shè)備而有一種應(yīng)用的連接沒有斷開,會存在臨時密鑰用于當(dāng)前應(yīng)用,這時不經(jīng)配對也可以連接上其他應(yīng)用,違反了藍(lán)牙安全性要求
3.1.5、藍(lán)牙文件傳輸模式
文件傳輸?shù)哪康氖鞘箖蓚€終端之間的數(shù)據(jù)交換成為可能,傳輸時使用的協(xié)議如圖3.1所示,可傳送的文件有doc、jpg、ppt、xls、wav等文件,還包括遠(yuǎn)端文件夾瀏覽功能。傳輸文件的設(shè)備可歸結(jié)成C/S結(jié)構(gòu)。客戶可從服務(wù)器下載文件,或向服務(wù)器上傳文件。服務(wù)器是一種使用對象交換協(xié)議(OBEX)文件夾列表格式的遠(yuǎn)端藍(lán)牙設(shè)備,其支持目標(biāo)交換服務(wù)、文件夾瀏覽功能,還允許客戶修改、創(chuàng)建文件或文件夾。
3.2、hci層介紹
3.2.1、hci層介紹
Host Controller Interface(HCI)?就是用來溝通Host和Module。Host通常就是PC,?Module則是以各種物理連接形式(USB,serial,pc-card等)連接到PC上的bluetooth Dongle。HCI則比較特殊,它一部分在軟件中實現(xiàn),用來給上層協(xié)議和程序提供訪問接口(Bluez中,hci.c hci_usb.c,hci_sock.c等).另一部分也是在Firmware中實現(xiàn),用來將軟件部分的指令等用底層協(xié)議明白的方式傳遞給底層。
居于PC的上層程序與協(xié)議和居于Modules的下層協(xié)議之間通過HCI溝通,有4種不同形式的傳輸:Commands, Event, ACL Data, SCO/eSCO Data.。
HCI Command:HCI Command是Host向Modules發(fā)送命令的一種方式。
HCI Event:Modules向Host發(fā)送一些信息,使用HCI Event。
3.2.2、hci層編程
對本地dongle進(jìn)行操作:
得到Host上插入Dongle數(shù)目以及Dongle信息
實現(xiàn)步驟如下:
// 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.
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,得到對應(yīng)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的數(shù)據(jù)信息,如發(fā)送多少個ACL Packet,正確多少,錯誤多少,等等。
};
打開一個HCI Socket---int hci_open_dev(int dev_id)
這個function用來打開一個HCI Socket。它首先打開一個HCI protocol的Socket(房間),并將此Socket與device ID=參數(shù)dev_id的Dongle綁定起來。只有bind后,它才將Socket句柄與Dongle對應(yīng)起來。
注意,所有的HCI Command發(fā)送之前,都需要使用?hci_open_dev打開并綁定。
關(guān)閉一個HCI Socket
int hci_close_dev(int dd) //簡單的關(guān)閉使用hci_open_dev打開的Socket。
向HCI Socket(對應(yīng)一個Dongle)發(fā)送?request
int hci_send_req(int dd, struct hci_request *r, int to)
BlueZ提供這個function非常有用,它可以實現(xiàn)一切Host向Modules發(fā)送Command的功能。
參數(shù)1:HCI Socket。
參數(shù)2:Command內(nèi)容。
參數(shù)3:以milliseconds為單位的timeout.
下面詳細(xì)解釋此function和用法:
當(dāng)應(yīng)用程序需要向Dongle(對應(yīng)為一個bind后的Socket)發(fā)送Command時,調(diào)用此function.
其中,參數(shù)一dd對應(yīng)一個使用hci_open_dev()打開的Socket(Dongle)。
參數(shù)三to則為等待Dongle執(zhí)行并回復(fù)命令結(jié)果的timeout.以毫秒為單位。
參數(shù)二hci_request * r?最為重要,首先看它的結(jié)構(gòu):
struct hci_request {
uint16_t ogf; //Opcode Group
uint16_t ocf; //Opcode Command
int event; //此Command產(chǎn)生的Event類型。
void *cparam; //Command?參數(shù)
int clen; //Command參數(shù)長度
void *rparam; //Response?參數(shù)
int rlen; //Response?參數(shù)長度
};
ogf,ocf不用多說,對應(yīng)前面的圖就明白這是Group Code和Command Code。這兩項先確定下來,然后可以查HCI Spec。察看輸入?yún)?shù)(cparam)以及輸出參數(shù)(rparam)含義。至于他們的結(jié)構(gòu)以及參數(shù)長度,則在~/include/net/bluetooth/hci.h中有定義。
?
例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。所以輸入?yún)?shù)即為此連接Handle.
返回參數(shù)則包含3部分,status(Command是否順利執(zhí)行),?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輸入?yún)?shù)
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;
}
//如果返回值狀態(tài)不對
if (Response_Param.status) {
return -1;
}
//得到當(dāng)前policy
*policy = Response_Param.policy;
幾個更基礎(chǔ)的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?比較
得到指定Dongle BDAddr
int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
參數(shù)1:HCI Socket,使用hci_open_dev()打開的Socket(Dongle)。
參數(shù)2:輸出參數(shù),其中會放置bdaddr.
參數(shù)3:以milliseconds為單位的timeout.
讀寫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)
參數(shù)1:HCI Socket,使用hci_open_dev()打開的Socket(Dongle)。
參數(shù)2:讀取或設(shè)置Name。
參數(shù)3:以milliseconds為單位的timeout.
注意:這里的Name與IOCTL HCIGETDEVINFO?得到hci_dev_info中的name不同
得到HCI Version
int hci_read_local_version(int dd, struct hci_version *ver, int to)
得到已經(jīng)UP的Dongle BDaddr
int hci_devba(int dev_id, bdaddr_t *bdaddr);
dev_id: Dongle Device ID.
bdaddr:輸出參數(shù),指定Dongle如果UP, 則放置其BDAddr
得到BDADDR不等于參數(shù)bdaddr的Dongle Device ID
int hci_get_route(bdaddr_t *bdaddr)
?
查找Dongle,發(fā)現(xiàn)Dongle Bdaddr不等于參數(shù)bdaddr的第一個Dongle,則返回此Dongle Device ID。
?
所以,如果: int hci_get_route(NULL),則得到第一個可用的Dongle Device ID。
將BDADDR轉(zhuǎn)換為字符串
int ba2str(const bdaddr_t *ba, char *str)
對遠(yuǎn)程dongle進(jìn)行操作:
inquiry?遠(yuǎn)程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?傳遞回來。
參數(shù)1:dev_id:指定Dongle Device ID。如果此值小于0,則會使用第一個可用的Dongle。
參數(shù)2:len:?此次inquiry的時間長度(每增加1,則增加1.25秒時間)
參數(shù)3:nrsp:此次搜索最大搜索數(shù)量,如果給0。則此值會取255。
參數(shù)4:lap:BDADDR中LAP部分,Inquiry時這塊值缺省為0X9E8B33.通常使用NULL。則自動設(shè)置。
參數(shù)5:ii:存放搜索到Bluetooth Device的地方。給一個存放inquiry_info指針的地址,它會自動分配空間。并把那個空間頭地址放到其中。
參數(shù)6:flags:搜索flags.使用IREQ_CACHE_FLUSH,則會真正重新inquiry。否則可能會傳回上次的結(jié)果。
返回值是這次Inquiry到的Bluetooth Device?數(shù)目。
注意:如果*ii不是自己分配的,而是讓hci_inquiry()自己分配的,則需要調(diào)用bt_free()來幫它釋放空間。
得到指定BDAddr的reomte device Name
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)
參數(shù)1:使用hci_open_dev()打開的Socket。
參數(shù)2:對方BDAddr.
參數(shù)3:name?長度。
參數(shù)4:(out)放置name的位置。
參數(shù)5:等待時間。
讀取連接的信號強(qiáng)度
int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
注意,所有對連接的操作,都會有一個參數(shù),handle.這個參數(shù)是連接的Handle。?前面講過如何得到連接Handle的。
3.3、L2CAP層編程
3.3.1、L2CAP協(xié)議簡介
Logical Link Control and Adaptation Protocol(L2CAP)邏輯連接控制和適配協(xié)議為上層協(xié)議提供面向連接和無連接的數(shù)據(jù)服務(wù),并提供多協(xié)議功能和分割重組操作。L2CAP?充許上層協(xié)議和應(yīng)用軟件傳輸和接收最大長度為?64K?的L2CAP?數(shù)據(jù)包。
L2CAP?基于通道(channel)?的概念。 通道?(Channel)?是位于基帶?(baseband)?連接之上的邏輯連接。每個通道以多對一的方式綁定一個單一協(xié)議?(single protocol)。多個通道可以綁定同一個協(xié)議,但一個通道不可以綁定多個協(xié)議。 每個在通道里接收到的?L2CAP?數(shù)據(jù)包被傳到相應(yīng)的上層協(xié)議。 多個通道可共享同一個基帶連接。也就是說,所有L2CAP數(shù)據(jù)均通過HCI傳輸?shù)?span style="line-height:1.8;font-family:'DejaVu Serif', serif;">Remote Device。且上層協(xié)議的數(shù)據(jù),大都也通過L2CAP來傳送。L2CAP可以發(fā)送Command。例如連接,斷連等等。
3.3.2、L2CAP編程方法
L2CAP編程非常重要,它和HCI基本就是Linux Bluetooth編程的基礎(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 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對應(yīng),這一項很重要
bdaddr_t l2_bdaddr; //Remote Device BDADDR
unsigned short l2_cid;
};
4.?發(fā)送數(shù)據(jù)到Remote Device:
send()或write()都可以。
5.?接收數(shù)據(jù):
revc()?或read()
以下為實例:
注:在Bluetooth下,主動去連接的一端作為主機(jī)端。被動等別人連接的作為Client端。
背景知識1:Bluetooth設(shè)備的狀態(tài)
之前HCI編程時,是用?ioctl(HCIGETDEVINFO)得到某個Device Info(hci_dev_info).其中flags當(dāng)時解釋的很簡單。其實它存放著Bluetooth Device(例如:USB Bluetooth Dongle)的當(dāng)前狀態(tài):
其中,UP,Down狀態(tài)表示此Device是否啟動起來。可以使用ioctl(HCIDEVUP)等修改這些狀態(tài)。
另外:就是Inquiry Scan, PAGE Scan這些狀態(tài):Inquiry Scan狀態(tài)表示設(shè)備可被inquiry. Page Scan狀態(tài)表示設(shè)備可被連接。
參考:L2CAP編程,http://blog.csdn.net/baozhongchao/archive/2009/10/26/4728751.aspx
3.4、SDP協(xié)議簡介
Service Discovery Protocol(SDP)提供一種能力,讓應(yīng)用程序有方法發(fā)現(xiàn)哪種服務(wù)可用以及這種服務(wù)的特性。服務(wù)發(fā)現(xiàn)協(xié)議(SDP或Bluetooth SDP)在藍(lán)牙協(xié)議棧中對藍(lán)牙環(huán)境中的應(yīng)用程序有特殊的含意,發(fā)現(xiàn)哪個服務(wù)是可用的和確定這些可用服務(wù)的特征。SDP定義了bluetooth client發(fā)現(xiàn)可用bluetooth server服務(wù)和它們的特征的方法。這個協(xié)議定義了客戶如何能夠?qū)ふ一谔囟▽傩缘姆?wù)而不讓客戶知道可用服務(wù)的任何知識。SDP提供發(fā)現(xiàn)新服務(wù)的方法,在當(dāng)客戶登錄到正在操作的藍(lán)牙服務(wù)器的一個區(qū)域時是可用的時。
Service discovery機(jī)制提供client應(yīng)用程序偵測server應(yīng)用程序提供的服務(wù)的能力,并且能夠得到服務(wù)的特性。服務(wù)的品質(zhì)包含服務(wù)type或服務(wù)class。SDP也提供SDP server與SDP client之間的通訊。SDP server維護(hù)著一個服務(wù)條目(service record)列表.每個服務(wù)條目描述一個單獨(dú)的服務(wù)屬性。?SDP client可以通過發(fā)送SDP request來得到服務(wù)條目。如果一個client或者依附于client之上的應(yīng)用程序決定使用某個service.?它創(chuàng)建一個單獨(dú)的連接到service提供者。?SDP?只提供偵測Service的機(jī)制,但不提供如何利用這些Service的機(jī)制。這里其實是說:SDP只提供偵測Service的辦法,但如何用,SDP不管。每個Bluetooth Device最多只能擁有一個SDP Server。如果一個Bluetooth Device只擔(dān)任Client,那它不需要SDP Server。但一個Bluetooth Device可以同時擔(dān)當(dāng)SDP Server和SDP client.
Service Record(Service?條目):
一個service是一個實體為另一個實體提供信息,執(zhí)行動作或控制資源。一個service可以由軟件,硬件或軟硬件結(jié)合提供。所有的Service信息都包含于一個Service Record內(nèi)。一個Service Record?包含一個Service attribute(Service屬性) list。在一個SDP Server內(nèi),每個Service Record擁有一個32-bit的唯一性數(shù)據(jù)。通常,這個唯一性只是在每個SDP Server內(nèi)部。 如果SDP Server S1?和SDP Server S2擁有同樣的一個Service Record。那他們在不同SDP Sever內(nèi)的獨(dú)特數(shù)值并不一定相同。SDP在SDP Server增加或減少Service Record時,并不會通知SDP client.
Service Attribute(Service?屬性):
每個Service屬性描述servcie的特性.一個Service Attribute由2部分:
Attribute ID + Attribute Value。
Attribute ID:16-bit無符號整數(shù),用于區(qū)別一個Service Record內(nèi)的其它屬性。
Attribute Value:Attribute值。
Service Class:
每個Service?都是某個Service Class的實例. Service Class定義了Service Record中包含的Service?屬性。屬性ID,屬性值都被定義好了。每個Service Class也有一個獨(dú)特ID。這個Service Class標(biāo)識符包含在屬性值ServiceClassIDList屬性中。并描繪為UUID。自從Service Record中的屬性格式以及含義依賴于Service Class后,ServiceClassIDList屬性變得非常重要。
Searching For Service:
Service Search transaction(事務(wù)?)允許client得到Service Record Handle。一旦SDP Client得到Service Record Handle,它就可以請求這個Record內(nèi)具體屬性的值。
如果某個屬性值UUID,則可以通過查找UUID查到這個屬性。
UUID:?universally unique identifier.(唯一性標(biāo)識符)
SDP協(xié)議棧使用request/response模式工作,每個傳輸過程包括一個request protocol data unit(PDU)和一個response PDU. SDP使用L2CAP連接傳輸數(shù)據(jù)。在發(fā)送Request PDU但未收到Response PDU之前,不能向同一個server再發(fā)送Request PDU。
PDU:protocol Data unit
PDU ID:用來識別PDU。
TransactionID:
用來識別Request PUD以及Response PUD。并用來對比某個Response PUD是否對應(yīng)著Request PUD。
4、Openobex
4.1、Openobex簡介
Welcome to the Open OBEX project. The overall goal of this project is to make an open source implementation of the Object Exchange (OBEX) protocol. OBEX is a session protocol and can best be described as a binary HTTP protocol. OBEX is optimised for ad-hoc wireless links and can be used to exchange all kind of objects like files, pictures, calendar entries (vCal) and business cards (vCard).
OBEX was specified by the IrDA? (Infrared Data Association), and although the protocol is very good for Infrared connections, it is not limited to it. In fact OBEX does not specify the top or bottom API making it very flexible and can run over most transports like TCP/IP and Bluetooth. Therefore OBEX is also called IrOBEX when used over the Infrared medium. There are some transport modules for serial links (cable OBEX) too.
Today, OBEX is builtin in many devices e.g. PDA's like the?Palm§?Pilot, and mobile phones like theSony Ericsson§?R320, R520, T68, T610, T630, K700 and many later phones,?Siemens§?S25, S35, S45, S55, S65?Nokia§?NM207 and Nokia 9110 Communicator. The HP?Scanner?§?CapShare 920 can also talk OBEX in addition to JetSend.?Microsoft§?Windows2000 has also builtin OBEX support.
Link types currently supported by OpenOBEX are:
INFRARED DATA ASSOCIATION ? (IrDA)?http://www.irda.org§
USB-IF, Inc.?http://www.usb.org§
Bluetooth(TM)?http://www.bluetooth.com§
Support for TCP/IP links, file descriptors and custom transports is also provided.
OBEX全稱為Object Exchange,中文對象交換,所以稱之為對象交換協(xié)議。它在此軟件當(dāng)中有著核心地位,文件傳輸和IrMC同步都會使用到它。OBEX協(xié)議構(gòu)建在IrDA架構(gòu)的上層. OBEX協(xié)議通過簡單的使用“PUT”和“GET”命令實現(xiàn)在不同的設(shè)備、不同的平臺之間方便、高效的交換信息。支持的設(shè)備廣泛,例如PC,PDA,電話,攝像頭,自動答錄機(jī),計算器,數(shù)據(jù)采集器,手表等等。OBEX協(xié)議定義了一種柔性的概念——objects。也即是對象。這些對象可以包括文件,診斷信息,電子商務(wù)卡片,銀行的存款等等。Objects在這里沒有高級的技術(shù)含義,而是視你的應(yīng)用而定。OBEX協(xié)議小到可作“命令和控制”功能,例如對電視機(jī),錄像機(jī)等的操作。大道可以做很復(fù)雜的操作,例如數(shù)據(jù)庫的事務(wù)處理和同步。
OBEX能夠具有以下幾個特點:
1、??友好的應(yīng)用——可實現(xiàn)快速開發(fā)。
2、??緊縮——可用在資源有限的小型設(shè)備上。
3、??跨平臺
4、??柔性的數(shù)據(jù)支持。
5、??方便的作為其他Internet傳輸協(xié)議的上層協(xié)議。
6、??可擴(kuò)展性——提供了對未來需求的擴(kuò)充支持而不影響以存在的實現(xiàn)。例如可擴(kuò)展安全,數(shù)據(jù)壓縮等。
7、? 可測試可調(diào)試。
4.2、Openobex與bluez編程實現(xiàn)
如下表:基于OBEX和BlueZ的數(shù)據(jù)傳輸?shù)牟襟E
| 步驟 | 函數(shù) | 意義 |
| 1 | OBEX_Init() | 用于初始化一個obex instance handle; arg1:OBEX_TRANS_BLUETOOTH用于聲明傳輸協(xié)議為bluetooth; arg2:callback function; arg3:flag=OBEX_FL_KEEPSERVER,接收到請求后,服務(wù)器可以繼續(xù)接收其他客戶端的請求; |
| 2 | OBEX_SetUserData() | 設(shè)置用戶自己的變量,該函數(shù)的使用完全取決于用戶自己; |
| 3 | BtOBEX_ServerRegister() | 一個專用于藍(lán)牙協(xié)議的服務(wù)端函數(shù),用于監(jiān)聽客戶端發(fā)送的請求。該函數(shù)內(nèi)部創(chuàng)建了一個?socket(調(diào)用socket(AF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM)),bind該socket到本地藍(lán)牙地址,將該socket轉(zhuǎn)化為監(jiān)聽sockt,之后該socket才能夠監(jiān)聽端口上來自客戶端的連接請求; |
| 4 | OBEX_HandleInput() | 函數(shù)用于讀取并處理接收到的數(shù)據(jù),如果沒有數(shù)據(jù)到達(dá),該函數(shù)將會阻塞;該函數(shù)內(nèi)部調(diào)用了selet()函數(shù),向系統(tǒng)登記了參數(shù)handle的客戶端sockt與服務(wù)端sockt,讓系 ? 統(tǒng)監(jiān)聽socket上的事件,如果是服務(wù)端socket上有數(shù)據(jù)到達(dá),則調(diào)用accept()函數(shù)為客戶端創(chuàng)建一個新的sockt,如果OBEX_Init()的flag不是設(shè)置為OBEX_FL_KEEPSERVER,則關(guān)掉服務(wù)端socket,禁止其他客戶端的連接請求; |
| 5 | CALLBACK?: (OBEX_EV_ACCEPTHINT) | ? |
| 6 | OBEX_ServerAccept() | 該函數(shù)返回上述客戶端的socket。 函數(shù)內(nèi)部重新創(chuàng)建了一個obex instance handle,并將上述服務(wù)器handle的參數(shù)復(fù)制到該handle,獲得服務(wù)器handle的fd(accept為客戶端創(chuàng)建的socket)后,清除服務(wù)器本身的fd; 該函數(shù)同時也為新創(chuàng)建的obex instance handle設(shè)置callback function及Userdata;至此,已經(jīng)為客戶端創(chuàng)建了一個與服務(wù)端完全獨(dú)立的obex instance handle,此后該服務(wù)端的操作都由該handle標(biāo)識,而服務(wù)器的socket則繼續(xù)監(jiān)聽其他客戶端的連接請求。 |
| 7 | CALLBACK?: OBEX_EV_REQDONE | OBEX_CMD_CONNECT |
| 8 | CALLBACK?: OBEX_EV_REQHINT | 一個請求即將到來。 調(diào)用OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)設(shè)置響應(yīng)操作碼 |
| 9 | CALLBACK?: OBEX_EV_REQCHECK | 第一個接收到的請求包已經(jīng)被解析 |
| 10 | CALLBACK?: OBEX_EV_PROGRESS | 收到n個此事件,說明客戶端正在傳輸文件內(nèi)容 |
| 11 | CALLBACK?: OBEX_EV_REQ | OBEX_CMD_PUT OBEX_ObjectSetRsp(object,OBEX_RSP_CONTINUE,OBEX_RSP_SUCCESS)設(shè)置響應(yīng)操作碼;此時,客戶端文件傳輸完畢,需進(jìn)行處理: OBEX_ObjectGetNextHeader()分別取得文件的名稱與內(nèi)容; |
| 12 | CALLBACK?: OBEX_EV_REQDONE | OBEX_CMD_PUT |
| 13 | CALLBACK?: OBEX_EV_REQHINT | 一個請求即將到來 |
| 14 | CALLBACK?: OBEX_EV_REQ | OBEX_CMD_DISCONNECT OBEX_ObjectSetRsp(object,OBEX_RSP_SUCCESS,OBEX_RSP_SUCCESS)設(shè)置響應(yīng)操作碼 |
| 15 | CALLBACK?: OBEX_EV_REQDONE | OBEX_CMD_DISCONNECT; OBEX_TransportDisconnect(handle)斷開連接; 注意:該函數(shù)只是將socket關(guān)閉,并沒有釋放handle所占用的資源,所以在應(yīng)用時需要釋放handle占用的資源; |
| 16 | OBEX_Cleanup() | 關(guān)掉obex handle并釋放該handle占用的資源。 (該函數(shù)同樣關(guān)閉了客戶端及服務(wù)端socket) |
?
如下表:OpenOBEX的代碼說明:
?
| 文件編號 | 文件名 | 作用 |
| 1 | obex_connect.c/.h | 處理CONNECT PDU,打包和解包 |
| 2 | obex_header.c/.h | PDU處理的公共函數(shù)及數(shù)據(jù)類型定義 |
| 3 | obex_object.c/.h | 對象處理函數(shù),客戶端和服務(wù)器公用代碼 |
| 4 | obex_server.c/.h | 服務(wù)器端處理代碼 |
| 5 | obex_client.c/.h | 客戶端處理代碼 |
| 6 | obex_transport.c/.h | 傳輸接口的抽象 |
| 7 | irobex.c/.h | 基于紅外的傳輸方式 |
| 8 | usbobex.c/.h | 基于USB的傳輸方式 |
| 9 | inobex.c/.h | 基于TCP/IP的傳輸方式 |
| 10 | btobex.c/.h | 基于藍(lán)牙的傳輸方式 |
?
Obexftp
5.1、obexftp簡介
The main goal of this project is to make mobile devices that feature the OBEX protocol and that adhere to the OBEX FTP standard accessible using an open source implementation.
ObexFTP is a library bundling everything needed for OBEX transfers and exposing it via a simple interface. Quite a number of language bindings are provided using SWIG or other means. There is a sample command line client "obexftp" and a server "obexftpd" included. Besides FTP the ObexFTP library provides access to the PUSH, GOEP and SYNCH services. It runs on Linux, FreeBSD, NetBSD and Win32.
5.2、基于Obexftp的應(yīng)用程序開發(fā)
參考代碼
6、參考資料
D-bus官網(wǎng),http://dbus.freedesktop.org/doc/dbus-tutorial.html§
D-bus中文介紹,http://blog.sina.com.cn/s/blog_5412ede60100eml7.html§
Bluetooth官網(wǎng),www.bluetooth.com§
Openobex官網(wǎng),http://dev.zuckschwerdt.org/openobex/wiki/ObexIntroduction§
Openobex API,http://dev.zuckschwerdt.org/openobex/doxygen/
轉(zhuǎn)載于:https://my.oschina.net/u/994235/blog/113213
總結(jié)
以上是生活随笔為你收集整理的Linux下Bluez的编程实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: t2mk是什么文件
- 下一篇: 英特尔ARM开启芯片之战 消费者该何去何