2016年11月9日 星期三

轉貼from http://thinkingiot.blogspot.tw/2015/03/bluetooth-low-energy-developers_19.html
(主要為備份參考用,此文在BLE架構分析上相當清楚細膩)

Bluetooth Low Energy: The Developer's Handbook 讀書筆記 (3)

http://www.amazon.com/Bluetooth-Low-Energy-Developers-Handbook/dp/013288836X

這本書是市面上 BLE Protocol 介紹的書裡我覺得最好的, 內容很深入, 算是介於入門書跟SEPC之間的地位, 最近也出了大陸簡體中文版, 懶得看英文的可以買來看, 百度也有線上版可以看, 但我強烈建議看英文版就是了, 畢竟 SPEC 是寫英文...

本篇使用的圖片, 有些來自網路電子書, 有些來自網路, 若有疑慮請通知我, 我會把它移除!
(Some figures in this article are captured from internet or e-book. If there is a violation of copyright. consideration. Please tell me and I i will remove it immediately)

==============

Part 1 : Overview

Chapter 3: BLE 架構

BLE 架構很簡單如下圖, 會分 Application, Host, Controller 三塊:

  • Controller: 負責 PHY/LL 的部分, 也就是收送無線電訊號 (Radio Signals), 以及將訊號轉換成封包資料的部分
  • Host: 就是軟體 Protocol 本身, 管理 devices 之間的連線, 以及如何將多個 services 資料由 radio 送出, 或是將收到的資料解成多個 services
  • Application: 使用 Host 來達到使用的案例

這個章節就會對下圖這幾個方塊做一個概念性的介紹

(Figure from: https://developer.bluetooth.org/TechnologyOverview/Pages/BLE.aspx)


[Controller]

一般被稱作是 Bluetooth radio 或是 Bluetooth chip, 它包含了 RF 處理 analog / digital 的部分, 包含硬體的部分, 負責 TX/RX (傳送/接收) Bluetooth 封包
  • Physical Layer
    • 負責在 2.4 GHz 下做 radio TX/RX 的工作
    • Modulation scheme (我不會翻, 調解方案?) 是使用 Gaussian Frequency Shift Keying (GFSK)
    • Shift Keying 意思就是 0/1 是利用頻率的上下移位來表示
    • 然後利用 Gaussian filter 來讓頻率不會位移的太嚴重, 形成 Gaussian curve 的波形
    • 在利用展頻的技術, 當離中心點 > 185kHz 就當作 1, < 185kHz 就當 0 
    • BLE PHY 的能力是 1us 傳輸 1 bit
    • 註: 這邊真的有講跟沒講差不多, 我對通訊也沒到很熟, 真的要講可能之後章節有比較多的圖來解釋會比較易懂, 這邊 PHY 的部分看看就好..
  • Direct Test Mode
    • 用來測試 PHY 的功能, 大多的無線通訊技術其實對策是 PHY 並沒有一個標準, 所以有的公司會自己做自己的測試 PHY 的方式, 增加 Cost 的考量
    • 利用這個 Direct Test Mode 可以直接控制 PHY 做 TX/RX 資料並分析判斷 PHY 行為正確性
    • Direct Test Mode 也可以用來量測 RF 參數, 並可以被用來做產線的測試以及 RF 的校準
  • Link Layer
    • 主要功能是處理廣播 (advertising), 掃描 (scanning), 以及建立和保持連線 (connections), 並確保資料有正確的封包格式, 並執行 CRC check 以及處理加密, 這些工作由以下三的部分組成
      • Channels
        • advertising channels
          • 還沒連線時使用來廣播連線訊息, 會有 3 個 advertising channels, 然後 scanner 也會去掃描這些 channels, 發現裝置並執行連線的動作
        • data channels
          • 當連線建立後, 就使用 data channels 來收跟傳資料, 會有 37 個 data channels, 並透過 adaptive frequency-hopping (選擇性跳頻) 來達到傳輸的穩定性 (避開干擾)
          • data channel 還有其他功能如 ACK (acknowledgement), RETX (重傳) 以及針對每個封包為單位作加密
      • Packets
        • 封包 (packet) 就是一些資料依據格式做的封裝單位, 目的是可以在很短時間內收送, 所以長度很短, advertising/data channel 的封包格式都市長一樣的, 可以參考下方的 LL 封包格式圖
          • 8-bit Preamble: 讓接收端做 time sync 以及 power 量測用的
          • 32-bit access address: 對於 advertising channel 是固定值但 data channel 則是 random/private 的
          • PDU (2-39 bytes): LL 的要處理的資料
            • 會包含 2 bytes PDU header, 主要是 PDU type 以及一些 flags 還有 PDU payload length
            • 以及包含 0-37 bytes PDU payload
          • 24-bit CRC: 用來檢查此 packet 是否有 error 發生, 確保 packet 內容的正確性
      • Procedures
        • Procedures 包含一些如建立連線, Pairing, Bonding, 加密等等的步驟, 之後細講 LL 才會帶到
  • Host/Controller Interface
    • 簡稱 HCI, 是 Host 用來跟 Controller 溝通的標準介面, 因為在傳統 BT 的架構下, 有 60% 的 Controller 是透過 HCI 來控制的 (也就是 Controller 是一顆獨立的 Chip 而 Host/App 再另一顆 Chip, 智慧手機就是一個例子)
    • HCI 會分成 logical interface 以及 physical interface
      • Logical Interface
        • 定義了 commands (指令) 以及 events (事件), 以及當收到指令或是發生事件對應的行為是什麼
        • 此 interface 可以透過實體方式傳送資料 (commands & events) 或是透過定義 API 的方式, 讓 Host 可以使用 (host/controller 同一個 chip 的情況下)
      • Physical Interface
        • 定義了 commands/events/data 如何傳輸 (with Host)
        • 常見的有如 SDIO / USB / UART 等介面
        • 一般 controller 會支援 1-2 種介面, 而 USB 因為是比較耗電且 cost 比較高的介面, 所以通常 single mode chip (BLE only) 不會支援 USB
        • HCI 要同時存在於 controller 以及 host, 這樣才有辦法溝通, 所以我們會把 controller 的部分稱為 lower-host controller interface 而 host 的部分稱為 upper-host controller interface

(Figure from BLE SPEC, LL 的封包格式)















[Host]

Host 包做的事情有 Multiplexing (L2CAP), Authentication (SM), expose state data (Attribute
Protocol), 定義 Attribute Protocol 如何重複利用 services 來讓裝置的 characteristics 可以被存取到 (Generic Attribute Profile), 以及定義裝置如何找到其他裝置並做連線 (Generic Access Profile).

註: Host 上面並沒有定義 interface (不向 HCI), 所以 API 是由各廠商自己訂的.


  • Logical Link Control and Adaptation Protocol (L2CAP)
    • BLE 的 multiplexing Layer (簡單來說就是將資料用正確的格式分出去 or 聚集起來, 中文是多工 & 解多工)
    • L2CAP channel
      • 一條雙向的資料通道 (裝置之間的 L2CAP)
      • 每一條 L2CAP channel 都是獨立的, 有各自的 flow control 以及設定
      • BLE 使用三條固定的 channel
        • 一條是 signaling channel (做控制用的)
        • 一條是和 SM 之間
        • 一條是和 Attribute Protocol 之間
      • 封包格式稱為 B-frame (最下圖可以看到), 簡化設計 (傳統 BT 格式會比較多種, 但 BLE 都只用 B-Frame Type)
        • 2 bytes 資料長度
        • 2 bytes Channel ID
        • 最後就是 LL2CAP PDU Payload
    • L2CAP signaling commands
      • 即在 signaling channel 上傳輸的 command, 之後會在比較細節的討論到
  • Security Manager Protocol
    • 負責 pairing 以及 key distribution
    • pairing 就是 device 之間互相取得信任的程序, pairing 後就會用加密的連線做 key distribution. (pairing 過程中 SM 會產生 hash, confirm value, short term key 等等)
    • key distribution 就是 slave 把加密資訊送給 master, 所以重新連線後, 就可以用這個加密資訊來加密資料做安全性的傳輸
  • Attribute Protocol
    • 定義了存取 peer device (對方裝置) 資料的"規則"
    • Server 的資料存放在 Attribute 中, Client 可以送需求 (Request) 給 Server 並藉由 Server 回復 (Response) 得知 Server 的 Attributes 並做 Attributes 讀寫, Server 其實也可以自己送 Message 給 Client, 端看 Message 的類型
    • Attribute Protocol 定義六種 Message 類型:
      • 1) Request (Client -> Server)
      • 2) Response (Server -> Client, 回覆 Request 用)
      • 3) Commands (Client -> Server, 不用 Response)
      • 4) Notifications (Server -> Client, 不用 Confirm)
      • 5) Indications (Server -> Client, 需要 Confirm)
      • 6) Confirmations (Client -> Server, 為了回覆 Indication)
    • 每一個 Attribute 就是一個有位址有標籤的資料
      • 會有一個 unique handle 來辨識此 Attribute
      • 此 Attribute 會有個 Type 來敘述此 Attribute 存放什麼資料, 例如溫度資料, 例如心跳資料
    • Attribute Protocol 也定義了 Attributes 的權限 (Permissions)
      • Read
      • Write
      • Authenticated Read
      • Authenticated Write
      • 註: 對於需要 Authentication 的部分, Client 若沒有正確的 Authenticated, 發出去的需求只會得到 Error, Client 並不會知道到底真正的權限是什麼
    • Attribute Protocol 為 stateless (無狀態的), 每一個動作 (transaction), 例如 Read/Write, 狀態都不會被記住 (可以想成每一個 transaction 都是獨立的), 所以 Attribute Protocol可以節省記憶體, 唯一一個需要記的prepare and write request 這個動作, 要先把要 write 的資料都先存下, 再靠一個 execute all 的 transaction 逐一的把資料寫入
  • Generic Attribute Profile
    • 位於 Attribute Protocol 的上層 (所以是 based on Attribute Protocol), 定義了 Attributes 的 Type 以及他們該如何被使用
    • 提出了幾個"概念": “characteristics,” “services, services 之間的 “include” 關聯性 , 以及characteristic “descriptors.”
    • 並定義了一些 procedures 來發現 services, characteristics, descriptors 並對 characteristics 做讀寫
    • Service
      • "immutable" "encapsulation" of some "atomic" "behavior" of a device
      • 以下一一講解這些文字的意義
      • Immutable
        • 不可變的, 一旦此 Service 公開, 就不可以被改變
        • 有此特性後 Service 才可以被重複使用 (reuse), 因為行為不會被改變了
        • 假如 Service 可以改變, 就得靠一直存在的連線來做交換訊息, 但就跟 BLE 的 connectionless 有衝突了, 所以這邊定義 Services 是不能變的
      • Encapsulation
        • 封裝, 把相關的 attributes 合起來就變成一個 Service, 也就是說 Service 是一個 attributes 集合
      • Atomic
        • 簡單來說就是小單元的
        • 為何需要小單元是因為 Service 本身有 include 的功能, 我們希望 service 可以盡量被 reuse, 所以 service 的定義最好越單元性越容易被 reuse, 假如定義了一個複雜的 Service 那就很難被 reuse 或 include 了
      • Behavior
        • 在某種情況下, 所做的反應或動作稱作 Behavior (行為)
        • 對於 Service 來說, Behavior 就是當 Attribute 被讀寫時的行為, 或是什麼事件讓 attribute 會 notify client (notification)
        • Behavior 必須很明確被定義, 才不會造成和不同 Client 連線有不同行為, 喪失協同性 (interoperability)
    • Service relationships
      • 裝置假如有複雜的 Behavior 就可以用到這個特性, 一個複雜的 Behavior 可以有不只一個 Services
      • 例如一個裝置有溫度 Service 可以量環境溫度, 有個 Battery Service 可以量電量 (他們都是 Primary Service)
      • 又因為 Battery 中其實又有溫度 sensor, 所以 Battery Service 其實又可以參考到另一個溫度 service 的實體用來取得電池溫度 (電池溫度 Service 為 Secondary Service)
  • Generic Access Profile
    • 定義了裝置如何成為 discoverable, connectable, bondable
    • 定義了裝置如何利用程序 (procedures) 來 discover, connect, read device name, bond 其他裝置
    • 另外也涉及隱私 (Privacy) 的概念, 利用 Resolvable private addresses (可知來源的私人位址), Privacy 可用在一直 broadcast 資訊並被連線的裝置上, 他可以一直改變 random address 來達到 privacy 需求. 就可以避開別人透過一直聽 address 來 track 裝置, 但是對於信任的人, 這個裝置 (一直換 address 的傢伙) 又必須是可被連線的
    • 所以 Generic Access Profile 定義了如何讓 private address 是 Resolvable (可知來源的), 也定義了如何去對 private address 裝置做連線
(Figure from BLE SPEC, L2CAP 封包格式)









[Application]

Application Layer 在最上層, 定義了三種規範: characteristic, service, and profile
每一個規範都是基於 Generic Attribute Profile (GAP) 所定義的, GAP 會定義多個 attributes groups 來表示 services 和 characteristics, 而 Applications 定義了如何去使用這些 attribute groups
  • Characteristics
    • 已知格式的資料, 伴隨一個 Universally Unique Identifier (UUID)
    • 可以重複使用 (reusable), 所以沒有行為的定義 (no behavior), 因為一但有了行為的定義, 就不容易重複使用了
    • computer readable format, 需要透過 computer 協助顯示給 user
  • Services
    • 他是 human readable format 的資料
    • 他包含了多個 characteristics 以及對應的行為 (behavior), 所以 characteristic 到了一個 service 內才被賦予了行為 (behavior)
    • Service 只對定義 characteristics  在 Server 的行為 (GATT Server), Client 的行為則不會定義 (Client 行為定義在 Profile)
      • 註: 所以我們講 Service 的時候, 就是只會提到 Server 的行為而已
    • Service 可以包含 (include) 其他 services, 但是他只能定義 included services 之間如何互動, 但不能改變他們的 characteristics 或改變這些 services 的行為
    • Service 不是敘述裝置如何連線並找到和使用 service, 以及如何找到 characteristics, 而是敘述當 Server 的 characteristics 被讀寫 (以及 notified / indicated) 的時候, 會發生什麼事
    • Service 有兩種, Primary Service 以及 Secondary Service
      • Primary: 用來體現裝置的目的, 簡單來說就是使用者利用 Primary Service 來知道這裝置是幹嘛用的
      • Secondary: 用來輔助 Primary Service 或其他 Secondary Services
      • 例如 Battery Service 是 Primary Service 用來取得電池相關資訊, 那會有一個 Temperature Service 為 Secondary Service 輔助 Battery Service 讓整個電池的資訊更加完整
  • Profiles
    • Profile 就是應用程式本身, 或是使用案例的一個體現
    • Profile 會敘述 2 到多個裝置, 每個裝置會有多個 services
    • Profile 會敘述裝置如何成為 discoverable 以及 connectable (間接定義了)
    • Profile 會敘述 Client 如何找到 Service, 找到 Characteristics, 以及如何去使用這些 Services 以達到目標的功能
    • Profiles 和 Services 是多對多對應關係的, 如下圖所示
      • 不同的 Profiles 可以使用到同一種 Service 來達到裝置要的行為 (如 immediate Alert Service)
      • 一個 Profile 也可以同時有用到多個 Services (如 Proximity Profile) 
      • 另外補充, 同種類的 Service 在不同 Profile 間是獨立的 (會各自有個實體 instance)


[Stack 劃分]

上面提到的 BLE 架構, 不一定得全都在同一個 Chip, 是可以做劃分的, 如下圖每個方塊都可以當作一個 Chip, 所以可以看到有各種組合的可能, 而 Chip 之間都有 interface 可以做 interaction, 這些不同的 solutions 可以用在不同的需要上.
  • Single Chip Solution
    • Application / Host / Controller 都跑在同一顆 Chip, 屬於最 Low Cost 的方案
    • 常用於 low-power 裝置, 例如 BLE 燈泡, BLE 電源開關, BLE 飛機...等等通常都會使用這種 SoC 的 BLE Chip 來做, 常見的 Chip 如 TI CC2541
    • 缺點就是, 他真的只適合用在小型裝置, 因為這種 Chip 都是資源非常限制的裝置 (大多會是 8-bit MCU 和很小的 Memory), 所以開發的 Application 就被這個給侷限了, 能做的事情有限
  • Two Chips Solution
    • 簡單來說就是分成兩顆 Chips, 有兩種分法
      • Application/Host + Controller, 滿常見的用途是智慧手機, 因為手機本來就有一個 AP 負責 Applications, 所以他只需要透過 HCI (host controler interface)來控制支援 BLE 的 Controller Chip 就可以了, 強大的 AP Core 則會跑 Application + BLE Protocol Software (Host)
      • Application + Host/Controller, 這個比較常見的用途是 Arduino + BLE module, 兩個 chip 之間會用客製化方式溝通 (例如 UART), 優點就是讓某平台可以藉由加入 Module 方式變成支援 BLE 功能, 且也不會因為 BLE 的加入讓 Application chip 變的受限制 (因為 host & controller 都跑在另一顆 chip 沒什麼太大的 overhead)
  • Three Chips Solution
    • 市面上場品幾乎都不會做這種劃分, 比較可能是在開發初期的實驗階段才會這樣做
    • 一個是因為太複雜, 每顆 Chip 都需要 interface 做溝通, 
    • 再來就是太貴了, 三顆 Chip 的價錢以及所占的面積其實都會比較大

(Figure created by Jackal Chen)




2015年1月26日 星期一

Android藍芽協議棧BlueDroid分析(3)-a2dp初始化(使能) (轉)

歡迎轉載opendevkit文章, 更好排版和效果, 請關注文章原始地址: http://www.opendevkit.com/?e=113


a2dp作為分析的實例很合適,包括l2cap連接的使用, sdp註冊等,完整的使用了 btif, bta等模組!

0. 背景


藍芽有個特點是,不同種類profile可以在同一時刻在兩個設備間存在連結!所以分析一個profile的過程是可以的,不必考慮會影響其他profile!

android上藍芽操作,搜索和連接是分開的,也就是初始化和連接只需要拿到對應的設備即可。

基本框架:

bt interface (有自己的狀態機,傳遞一個回調給) -> bta (有狀態機,每個狀態都有跳轉表) , bta的請求都是以事件的方式發給btu_task,再根據模組調用回調,回調裡再跳轉到對應的處理函數) -> bta向下直接調用stack的接口 -> bta處理完成後,會調用bt interface的接口傳遞狀態!

1. a2dp profile初始化


(1) A2dpStateMachine類構造的時候,調用了initNative

(2) com_android_bluetooth_a2dp.cpp的initNative獲取了a2dp bt interface

  158     if ( (sBluetoothA2dpInterface = (btav_interface_t *)
  159           btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID))

接着調用了   sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks))
初始化a2dp bt interface!

(3) btif_av.c的init操作

  814 static const btav_interface_t bt_av_interface = {
  815     sizeof(btav_interface_t),
  816     init,                 
  692 bt_status_t btif_av_init(void)
  693 {
  694     if (btif_av_cb.sm_handle == NULL)
  695     {
  696         if (btif_a2dp_start_media_task() != GKI_SUCCESS) // 創建media task
  697             return BT_STATUS_FAIL;       
  698
  699         btif_enable_service(BTA_A2DP_SERVICE_ID); // 使能a2dp service
  700
  701         /* Initialize the AVRC CB */ 
  702         btif_rc_init();
  703                                                                                                                                        
  704         /* Also initialize the AV state machine */
  705         btif_av_cb.sm_handle = btif_sm_init((const btif_sm_handler_t*)btif_av_state_handlers, BTIF_AV_STATE_IDLE); // 創建a2dp bt interface狀態機
  706  
  707         btif_a2dp_on_init();
  708
  709         return BT_STATUS_SUCCESS;                                                                                                      
  710     }                 
  711
  712     return BT_STATUS_DONE;
  713 }

[1] btif_enable_service(BTA_A2DP_SERVICE_ID); 使能服務

btif_enable_service -> btif_transfer_context -> btif_dm_execute_service_request -> btif_in_execute_service_request -> btif_av_execute_service
btif_av_execute_service分別調用:BTA_AvEnable和BTA_AvRegister完成使能和服務註冊。

[2] 創建a2dp bt interface狀態機

btif_av_cb.sm_handle = btif_sm_init((const btif_sm_handler_t*)btif_av_state_handlers, BTIF_AV_STATE_IDLE); // 創建a2dp bt interface狀態機


創建一個狀態機賦值狀態回調和初始化狀態,以後就可以調用類似btif_sm_dispatch(btif_av_cb.sm_handle, event, (void*)p_param);這個接口來事件驅動狀態機了!

Android藍芽協議棧BlueDroid分析(1)-幾個獨立任務 (轉)

歡迎轉載opendevkit文章, 更好排版和效果, 請關注文章原始地址:http://www.opendevkit.com/?e=110

通過分析獨立任務,我們能更清楚的弄明白,協議棧部分到底有多少獨立執行線。


任務個數和作用


grep GKI_create_task發現,

./main/bte_main.c:204:    GKI_create_task((TASKPTR)btu_task, BTU_TASK, BTE_BTU_TASK_STR,

主要任務,處理上層請求,處理下層事件,承上啟下!

./btif/src/btif_core.c:463:    status = GKI_create_task(btif_task, BTIF_TASK, BTIF_TASK_STR,

bt interface任務。

./btif/src/btif_media_task.c:699:    retval =GKI_create_task((TASKPTR)btif_media_task, A2DP_MEDIA_TASK,

媒體任務,處理多媒體事件。

實際上, GKI_create_task也是封裝的pthread_create

grep pthread_create發現,

./gki/ulinux/gki_ulinux.c:806:    if (pthread_create( &timer_thread_id,

gki層提供的timer線程,為各種timer提供支持。

./hci/src/btsnoop.c:589:    if (pthread_create(&thread_id, NULL,

嗅探任務,負責hci的數據包嗅探。

./hci/src/bt_hci_bdroid.c:161:    if (pthread_create(&hc_cb.worker_thread, &thread_attr, \

對hci uart寫隊列處理的任務,包括hci的cmd,sco的數據包。

./hci/src/userial.c:363:    if (pthread_create(&(userial_cb.read_thread), &thread_attr, \

讀hci uart,讀後分發!

./btif/src/btif_sock_thread.c:170:    if( pthread_create(&thread_id, &thread_attr, start_routine, arg)!=0 )

為app層提供操作rfcomm的簡便方法,也就是使用socket通信!android的藍芽操作主要是玩rfcomm目前!

./btif/src/btif_hl.c:5171:    if ( pthread_create(&thread_id, &thread_attr, start_routine, arg)!=0 )

hl profile自己的任務,需要才打開!

./btif/co/bta_hh_co.c:138:    if ( pthread_create(&thread_id, &thread_attr, start_routine, arg)!=0 )

hh profile自己的任務,需要才打開!

./udrv/ulinux/uipc.c:573:    if (pthread_create(&uipc_main.tid, (const pthread_attr_t *) NULL, (void*)uipc_read_task, 
NULL) < 0)


處理a2dp_audio_hw中out_write接口發過來的數據,也就是sco數據處理!

Android藍芽協議棧BlueDroid分析(0)-初始化和enable (轉)

歡迎轉載opendevkit文章, 更好排版和效果, 請關注文章原始地址: http://www.opendevkit.com/?e=111

瞭解初始化,方便從頭捋順整個流程,做到心有框架。

1. java層初始化,引起jni和協議棧部分初始化


(1) AdapterService.java的static塊會在AdapterService整個類初始化的時候調用,static塊裡調用的是classInitNative。


(2) com_android_bluetooth_btservice_AdapterService.cpp的classInitNative接口關鍵部分是:


[1] 拿到java層類接口的回調,以便將來有通知好回調

......
  474     method_discoveryStateChangeCallback = env->GetMethodID(jniCallbackClass,
  475                                                            "discoveryStateChangeCallback", "(I)V");
  476
  477     method_devicePropertyChangedCallback = env->GetMethodID(jniCallbackClass,
  478                                                             "devicePropertyChangedCallback",
  479                                                             "([B[I[[B)V");
  480     method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
  481     method_pinRequestCallback = env->GetMethodID(jniCallbackClass, "pinRequestCallback",
  482                                                  "([B[BI)V");
  483     method_sspRequestCallback = env->GetMethodID(jniCallbackClass, "sspRequestCallback",
  484                                                  "([B[BIII)V");
......

[2] 加載stack庫,後去stack接口

  494     const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);                                               
  495
  496     err = hw_get_module(id, (hw_module_t const**)&module);
  497
  498     if (err == 0) {
  499         hw_device_t* abstraction;
  500         err = module->methods->open(module, id, &abstraction);
  501         if (err == 0) {
  502             bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
  503             sBluetoothInterface = btStack->get_bluetooth_interface();

(3) AdapterService這個服務初始化的時候,onCreate被調用,onCreate調用了initNative,initNative也是com_android_bluetooth_btservice_AdapterService.cpp的接口:


[1] 根據(2)裡拿到的interface,初始化interface,傳遞了jni層給interface層的回調,也就是interface層有通知會回調這些接口:
  517     if (sBluetoothInterface) {
  518         int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
  519         if (ret != BT_STATUS_SUCCESS) {
  520             ALOGE("Error while setting the callbacks \n");                                                                             
  521             sBluetoothInterface = NULL;
  522             return JNI_FALSE;
  523         }
[2] 通過bt interface獲取socket interface,這個socket interface是上層使用rfcom通信的關鍵對象。
  524         if ( (sBluetoothSocketInterface = (btsock_interface_t *)
  525                   sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
  526                 ALOGE("Error getting socket interface");
  527         }

(4)  (3).[1]調用了,bt interface的init接口,正是bluetooth.c文件中,bluetoothInterface結構的init成員:


  346 static const bt_interface_t bluetoothInterface = {
  347     sizeof(bt_interface_t),
  348     init,                                                                                                                              
  349     enable,
  350     disable,
  351     cleanup,
這個接口定義在bluetooth.c中,分別:
[1] 保存jni層傳遞下來的回調
bt_hal_cbacks = callbacks;
[2] 調用btif_init_bluetooth();初始化bt interface層;簡單初始化bte, 這個簡單初始化bte實際上有個關鍵的部分:獲取hc接口,下面enable的時候要用到; 獲取本地藍芽地址;創建了一個task,用於bt interface層。
  455     btif_config_init();
   456     bte_main_boot_entry();會調用bt_hc_if = (bt_hc_interface_t *) bt_hc_get_interface()
   457
   458     /* As part of the init, fetch the local BD ADDR */
   459     memset(&btif_local_bd_addr, 0, sizeof(bt_bdaddr_t));
   460     btif_fetch_local_bdaddr(&btif_local_bd_addr);
   461
   462     /* start btif task */
   463     status = GKI_create_task(btif_task, BTIF_TASK, BTIF_TASK_STR,
   464                 (UINT16 *) ((UINT8 *)btif_task_stack + BTIF_TASK_STACK_SIZE),
   465                 sizeof(btif_task_stack));
經過以上步驟,java層帶動jni層初始化了協議棧bt interface接口層,創建了一個任務btif_task。java層有jni層的interface,jni層有java層的回調。jni層有協議棧bt interface,協議棧有jni層的回調。

2. bt使能引起的初始化


(1) bt使能調用過程


bt使能由java層發起,自然要發生在1中的java層和jni層初始化之後。
路線是:BluetoothAdapter.getDefaultAdapter().enable (BluetoothAdapter.java)
-> enable (AdapterService.java)
-> mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON); (AdapterService.java) 
-> mAdapterService.processStart(); (AdapterState.java)
-> mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED)); (AdapterService.java)
-> boolean ret = mAdapterService.enableNative(); (AdapterState.java)
-> enableNative (AdapterService.java)
-> enableNative (android_bluetooth_btservice_AdapterService.cpp)
-> bluetoothInterface->enable (bluetooth.c)
  346 static const bt_interface_t bluetoothInterface = {
  347     sizeof(bt_interface_t),
  348     init,
  349     enable,
bt interface的enable接口,被java經過jni調用下來。

(2)  bluetoothInterface的enable接口


調用btif_enable_bluetooth -> bte_main_enable比較關鍵:
  166     /* Initialize BTE control block */
  167     BTE_Init();
  168
  169     lpm_enabled = FALSE;
  170
  171     if (bt_hc_if)
  172     {
  173         int result = bt_hc_if->init(&hc_callbacks, local_addr); 初始化hc interface,也會同時初始化libbt-vendor.so,也就是hci是libbt-vendor.so相關的。
  174         APPL_TRACE_EVENT1("libbt-hci init returns %d", result);
  175
  176         assert(result == BT_HC_STATUS_SUCCESS);
  177
  178         if (hci_logging_enabled == TRUE)
  179             bt_hc_if->logging(BT_HC_LOGGING_ON, hci_logfile);
  180
  181 #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)
  182         APPL_TRACE_DEBUG1("%s  Not Turninig Off the BT before Turninig ON", __FUNCTION__);
  183                                                                                                                                        
  184         /* Do not power off the chip before powering on  if BT_CLEAN_TURN_ON_DISABLED flag
  185          is defined and set to TRUE to avoid below mentioned issue.
  186
  187          Wingray kernel driver maintains a combined  counter to keep track of
  188          BT-Wifi state. Invoking  set_power(BT_HC_CHIP_PWR_OFF) when the BT is already
  189          in OFF state causes this counter to be incorrectly decremented and results in undesired
  190          behavior of the chip.
  191
  192          This is only a workaround and when the issue is fixed in the kernel this work around
  193          should be removed. */
  194 #else
  195         /* toggle chip power to ensure we will reset chip in case
  196            a previous stack shutdown wasn't completed gracefully */
  197         bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);
  198 #endif
  199         bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);此處比較關鍵,chip已經上電了!
  200
  201         bt_hc_if->preload(NULL);
  202     }
  203
  204     GKI_create_task((TASKPTR)btu_task, BTU_TASK, BTE_BTU_TASK_STR,
  205                     (UINT16 *) ((UINT8 *)bte_btu_stack + BTE_BTU_STACK_SIZE),
  206                     sizeof(bte_btu_stack));
此處又創建了一個任務,btu_task,負責處理bte btu的事件等任務。

(3) btu_task任務初始化部分


  170 #if (defined(HCISU_H4_INCLUDED) && HCISU_H4_INCLUDED == TRUE)
  171     /* wait an event that HCISU is ready */
  172     GKI_wait(0xFFFF, 0);
  173 #endif
  174     /* Initialize the mandatory core stack control blocks
  175        (BTU, BTM, L2CAP, and SDP)
  176      */
  177     btu_init_core();   核心協議棧初始化  -> btm_init,l2c_init,sdp_init
  178
  179     /* Initialize any optional stack components */
  180     BTE_InitStack();    擴展協議初始化RFCOMM_Init,SPP_Init
  181
  182 #if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE)
  183     bta_sys_init();     bta sys初始化這裡比較關鍵的是bta_sys_cb.task_id = GKI_get_taskid(),我們會看到bta sys這裡保存的是btu_task這個任務的id,也就是將來bta_sys_sendmsg發送的都是發到btu_task裡的!
  184 #endif
  185
  186     /* Initialise platform trace levels at this point as BTE_InitStack() and bta_sys_init()
  187      * reset the control blocks and preset the trace level with XXX_INITIAL_TRACE_LEVEL
  188      */
  189 #if ( BT_USE_TRACES==TRUE )
  190     BTE_InitTraceLevels();
  191 #endif
  192
  193     /* Send a startup evt message to BTIF_TASK to kickstart the init procedure */
  194     GKI_send_event(BTIF_TASK, BT_EVT_TRIGGER_STACK_INIT);給bt interface task發事件BT_EVT_TRIGGER_STACK_INIT,告訴bt interface task協議棧已初始化
  195
  196     raise_priority_a2dp(TASK_HIGH_BTU);

(4) 真正的協議棧使能操作'


bt interface task也就是btif_task處理BT_EVT_TRIGGER_STACK_INIT事件:
btif_task -> BTA_EnableBluetooth接口, 這個接口主要工作是:
    84         p_msg->hdr.event = BTA_DM_API_ENABLE_EVT;
    85         p_msg->p_sec_cback = p_cback;
    86         bta_sys_sendmsg(p_msg);
向btu_task發送BTA_DM_API_ENABLE_EVT事件!
btu_task用下面的代碼:
  517         if (event & TASK_MBOX_2_EVT_MASK)
  518         {
  519             while ((p_msg = (BT_HDR *) GKI_read_mbox(TASK_MBOX_2)) != NULL)
  520             {
  521                 bta_sys_event(p_msg);                                                                                                  
  522             }
  523         }
調用bta_sys_event來處理,bta_sys_event中:
 490 BTA_API void bta_sys_event(BT_HDR *p_msg)
  491 {
  492     UINT8       id;   
  493     BOOLEAN     freebuf = TRUE;  
  494
  495     APPL_TRACE_EVENT1("BTA got event 0x%x", p_msg->event);
  496
  497     /* get subsystem id from event */
  498     id = (UINT8) (p_msg->event >> 8);
  499
  500     /* verify id and call subsystem event handler */
  501     if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL))
  502     {
  503         freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);

實際就是,先取得事件對應的子系統id,之後回調對應子系統的回調,而子系統是用類似:

bta_sys_register( BTA_ID_SYS,  &bta_sys_hw_reg);這樣的接口註冊的!

#define BT_EVT_TRIGGER_STACK_INIT   EVENT_MASK(APPL_EVT_0)
#define APPL_EVT_0          8
#define EVENT_MASK(evt)       ((UINT16)(0x0001 << (evt)))
BT_EVT_TRIGGER_STACK_INIT事件對應的子系統就是1,也就是#define BTA_ID_DM           1

搜索BTA_ID_DM是在BTA_EnableBluetooth接口中,bta_sys_register (BTA_ID_DM, &bta_dm_reg );註冊的。也就是:

bta_dm_reg -> bta_dm_sm_execute -> bta_dm_action[0] -> bta_dm_enable:
bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback ); 註冊回調
sys_enable_event->hdr.event = BTA_SYS_API_ENABLE_EVT;發送給btu_task任務事件,經過上邊分析,我們知道這個事件會被註冊的bta_sys_register( BTA_ID_SYS,  &bta_sys_hw_reg);的bta_sys_hw_reg->bta_sys_sm_execute處理,
bta_sys_sm_execute -> bta_sys_action -> bta_sys_hw_api_enable -> bta_sys_hw_co_enable -> bta_sys_hw_ci_enabled( module );這個接口又發送 p_msg->hdr.event = BTA_SYS_EVT_ENABLED_EVT;事件。
bta_sys_sm_execute -> bta_sys_action -> bta_sys_hw_evt_enabled -> BTM_DeviceReset( NULL ); -> btm_dev_reset (); -> btsnd_hcic_reset (LOCAL_BR_EDR_CONTROLLER_ID); -> btu_hcif_send_cmd (local_controller_id,  p); -> HCI_CMD_TO_LOWER(p_buf); -> bte_main_hci_send -> bt_hc_if->transmit_buf(( -> 被bt_hc_worker_thread線程調用p_hci_if->send(sending_msg_que[i]);調用hci_h4_send_msg -> bytes_sent = userial_write(event,(uint8_t *) p,bytes_to_send); 最終寫到了uart上了!

事情實際上還沒有完,chip雖然reset了,但是上邊還是需要處理reset的狀態!


不過初始化部分就算結束了!

Android藍芽協議棧BlueDroid分析(2)-stack結構介紹 (轉)

歡迎轉載opendevkit文章, 更好排版和效果, 請關注文章原始地址: http://www.opendevkit.com/?e=112

瞭解源碼結構,方便快速閲讀學習!

external/bluetooth/bluedroid目錄

  ---------------- audio_a2dp_hw/ 
  a2dp的audio hal,使用藍芽輸出音頻時候,調用這個hal的out_write接口,這裡的輸出並不直接寫串口,而是傳給了stack裡的 task來做的,因為編碼pcm到sbc。跟它通信的是後邊的udrv的內容!
  ---------------- bta/
相對於stack的概念,在stack上抽出一層application用的接口,取名bta,其實並不直接給app調用的,app層調用的是btif,bta是使用stack的接口。也就是被btif調用,當然邏輯並不是唯一確定的,也可能越過bta調用下層接口。主要手段是發送消息,處理事件!
ag/
Android.mk
ar/
av/
dm/
fs/
gatt/
hh/
hl/
include/
jv/
pan/
pb/
sys/  --- 這個是bta控制的和行,其他都是具體實現!
 ---------------- btif/
與android的Bluetooth apk的jni層通信的接口,真正的為app提供interface的接口。
co/ - callout,bt interface 調用 bta,bta調用co的內容,實際就是承接stack的接口。
include/
src/ - 各種profile的interface,給jni使用,並且調用jni回調
  ---------------- conf/
bt配置文件!
  ---------------- embdrv/
不清楚emb取什麼意思,實際的代碼作用是進行sbc編碼和處理,可能embdrv的意思是針對嵌入式環境的編碼驅動?
---------------- gki/
general kernel interface?不知道什麼意思,不過看代碼是針對os的移植層,包括多任務和timer實現,實際就是為stack代碼提供一個抽象的多任務和時間控制環境,達到可移植的目的!
---------------- hci/
host control interface,實現hci的功能!
---------------- include/
頭文件!
---------------- main/
main意味着入口,包括對各個模組的整合,最終目標的定義,各個模組的初始化等!
---------------- stack/
協議棧代碼,
a2dp/
avct/
avdt/                                                                                                                                        
avrc/
bnep/
btm/
btu/
gatt/
hcic/
hid/
include/
l2cap/
mcap/
pan/
rfcomm/
sdp/
smp/
各種profile!
---------------- test/
測試程序目錄,實際上是調用bt interface的接口,實現回調,測試測試!
---------------- tools/
忽略!
---------------- udrv/
userspace driver?實際的代碼作用是跟a2dp端進行socket通信,處理命令和a2dp數據pcm流!media task調用這裡的接口!實際就是跟audio_a2dp_hw 的audio hal通信!

---------------- utils/
just utils!

2015年1月21日 星期三

Bluetooth L2CAP介绍 (轉) (規格詳細說明)

邏輯鏈路控制和適配協議(Logical Link Control and Adaptation Protocol),是藍芽系統中的核心協議 
相應的規範位於Core Version 4.1的vol 3:Part A
111 
L2CAP負責適配基帶中的上層協議。它同LM並行工作,向上層協議提供面向連接和無連接的數據服務,並提供多路復用,分段和重組操作 

允許高層次的協議和應用能夠以64KB的長度發送和接收數據包(L2CAP Serveice Data Units, SDU)。
L2CAP提供了邏輯通道,名為L2CAP Channels,即在一個或多個邏輯鏈路上進行多路復用。
L2CAP可分為兩個部件 
~1 Channel Manager 
~2 Resource Manager
l2cap
總的來說,L2CAP提供了如下功能 
~1 協議/通道多路復用 
~2 分段和重組 
~3 服務質量
tip: 
L2CAP只支持ACL,而不支持SCO/eSCO(用預留寬頻進行實時語音傳輸)   
L2CAP不支持可靠的廣播通道

1. 通用操作

1. L2CAP Channel

L2CAP基於通道的概念,通道的每一個端點被稱為通道標識符(CID) 
不同設備間CID可復用,但本地設備CID不可復用
以下是CID ACL-U和AMP-U鏈路的name space(LE-U未列出)
CIDDescriptionLogical Link Supported
0x0000Null identifier 
0x0001L2CAP Signalling ChannelACL-U
0x0002Connectionless ChannelACL-U
0x0003AMP Manager ProtocolACL-U
0x0004~0x003EReservedACL-U
0x003FAMP Test ManagerACL-U
0x0040~0xFFFFDynamically allocatedACL-U, AMP-U

2. 設備間操作

2323
上圖說明了CID在不同設備對等L2CAP實體間通信中的使用方式。
面向連接的數據通道提供了兩設備間的連接,綁定邏輯鏈路的CID則用於標識通道的每一端。
對於無連接的數據通道,當用於廣播傳輸時限制了傳輸的方向;當用於單播傳輸時則沒有限制(?)
部分通道都保留用做特殊目的,具體如下圖 
如0x0001表示Signalling Channel,用於創建和建立面向連接的數據通道,並可對這些通道的特性變化進行協商(ACL-U) 
1111 

3. 層間操作

121212

4. 操作模式

L2CAP Channels可運行在以下模式之一(~1是默認模式) 
~1 基本L2CAP模式(Basic L2CAP Mode) 
~2 流量控制模式(Flow Control Mode) 
~3 重傳模式(Retransmission Mode) 
~4 加強版重傳模式(Enhanced Retransmission Mode) 
~5 流模式(Streaming Mode) 
~6 LE Credit Based Flow Control Mode

2. 數據包格式(Data Packet Format)

Data Packet Format
L2CAP有以下幾種連接類型: 
~1 Connection-oriented Channels in Basic L2CAP mode 
~2 Connectionless Data Channel in Basic L2CAP mode 
~3 Connection-oriented Channel in Retransmission/Flow Control/Streaming Mode
~4 Connection-oriented Channels in LE Credit Based Flow Control Mode
對於不同的連接類型,數據包格式是不同的 
且Information payload是基於Little Endian byte order

1. B-Frame

bf
Length: 2 bytes,Information payload的位元組數(0~65535) 
Channel ID: 2 bytes, 對端目的通道 
Information payload: 0~65535 bytes

2. G-Frame

gf
Length: 2 bytes,Information payload和PSM的位元組數(0~65535) 
Channel ID: 2 bytes, 對於無連接傳輸使用固定值0x0002 
PSM: >= 2 bytes, Protocol/Servece Multiplexer(具體指參考Channel Identifiers
psm 
Information payload: 0~65535 bytes

3. S-Frame/I-Frame

I-Frame用於在L2CAP實體間進行信息傳輸 
S-Frame則用於確認I-Frame和I-Frame的重傳請求
slf
Length: 2 bytes,除Basic L2CAP外的總位元組數  
Channel ID: 2 bytes, 對端目的通道 
L2CAP SDU Length: 2 bytes, 只出現在Start I-Frame(SAR=0x01)中,表示總的SDU長度 
FCS: 2 bytes, Frame Check Sequence
Control Field有三種模式 
~1 Standard Control Field: 用於Retransmission mode and Flow Control mode 
~2 Enhanced Control Field: 用於Enhanced Retransmission mode and Streaming mode 
~3 Extended Control Field: 用於Enhanced Retransmission mode and Streaming mode
這三種Control Mode格式如下
111  
222 
333
SAR: (2bits)Segmentation and Reassembly,指明該L2CAP是否是分段過,定義如下 
a1 
TxSeq: (6/14bits)Send Sequence Number,對發送的I-Frame計數,用於分段和重組。 
ReqSeq: (6/14bits)Receive Sequence Number,接收方用於應答I-Frame和請求重傳 
R: (1bits)Retransmission Disable Bit,用來實現Flow Control. 
S: (2bits)Supervisory function,表示S-Frame的type,定義如下 
b1
P: (1bits)Poll, 置1表示從接收方徵求相應 
F: (1bits)Final,相應P置1的S-Frame

4. LE-Frame

lef
欄位含義與上類似

3. 信號包格式(Signaling Packet Format)

信號包格式(Signaling Packet Format)
這裡介紹的是在對端設備上兩個L2CAP實體間傳遞的信號命令(Signaling Commands) 
這些信號命令通過Signaling Channel來傳輸 
對於ACL-U邏輯鏈路應該使用CID 0x0001, 而對於LE-U則應該使用CID 0x0005
通用的信號包格式如下
image
Field類似B-Frame,不詳述 
值得一說的是payload長度 
image
另需要注意: 
一個C-Frame通過0x0001通道可以傳遞多個命令 
而一個C-Frame通過0x0005通道則只能傳遞一個命令
image
上圖顯示了信號命令的通用格式 
Code: 1 byte, 指定Command的類別 
Identifier: 1 byte, 用於標識一個Request和Response匹配對 
Length: 2 byte, data欄位的長度 
Data: 0~N byte, Code欄位來決定其格式
下圖顯示了規範所定義的Code類型,至於data的格式請參考規範vol 3-Part A-4
Command_list

4. 參數配置選項(Configuration Parameter Options)

(原文晦澀難懂,哥不甚理解)
image
Type: 1 byte, 定義需要被配置的參數,若不能識別則由最高位決定其行為  
         0表示必須識別該選項,若無法識別則拒絶配置請求  
         1表示可以跳過該選項 
Length: 1 byte, 選項數據的位元組數,若選項數據為空則為0 
Option Data: 由Type決定其內容(不詳述,見規範vol 3-Part A-5)
Type欄位具體含義 
1) Maximum Transmission Unit(MTU),           Type=0x01 
2) Flush Timeout Option,                            Type=0x02 
3) Quality of Service(Qos) Option,               Type=0x03 
4) Retransmission and Flow Control Option,   Type=0x04 
5) Frame Check Sequence(FCS) Option,        Type=0x05 
6) Extended Flow Specification Option,          Type=0x06 
7) Extended Window Size Option,                 Type=0x07

5. 狀態機(State Machine)

這裡指的是面向連接通道(Connection-oriented Channel)狀態機,適用於雙向CID 
介紹了狀態(state), 引起狀態變化的事件(event)及事件相對應的動作(action)

1. 狀態機

如下圖所示,發起請求的一方是客戶機,服務器接收請求,應用層的客戶既可以發起也可以接收請求 
image 
命令規則為: 
兩層之間的界面上(垂直方向)用下層的縮寫名作首碼,為上層提供服務,如L2CA 
兩個同層實體之間的接口(水平方向)則使用協議縮寫作為首碼,如L2CAP。 
來自上層的事件稱作請求Request(Req), 相應的答覆稱為確認Confirm(Cfm) 
來自低層的事件稱為指示Indication(Ind), 相應的答覆稱為相應Response(Rsp)

2. 事件

在L2CAP層中,只有超時事件是由本層產生
事件分為5類: 
來自下層的指示(Indication)和確認(Confirm) 
來自上層的請求(Request)和相應(Response) 
來自對等層的數據 
來自對等層的請求和相應 
超時事件

3. 動作

動作可分為5類: 
對上層的確認(Confirm)和指示(Indication) 
對下層的請求(Request)和相應(Response) 
發給對等層實體的數據傳輸 
發給對等層的請求和相應 
計時器設置

4. 通道操作狀態

1) CLOSED 
2) WAIT_CONNECT 
3) WAIT_CONNECT_RSP 
4) CONFIG 
5) OPEN 
6) WAIT_DISCONNECT 
7) WAIT_CREATE 
8) WAIT_CREATE_RSP 
9) WAIT_MOVE 
10) WAIT_MOVE_RSP 
11) WAIT_MOVE_CONFIRM_RSP 
12) WAIT_CONFIRM_RSP
更多內容見最下方參考網址