BLE 技術(六)--- GATT Profile + ATT protocol + L2CAP(Core_v5.2)

2020-09-28 09:09:58

前言

前篇博文Generic Access Profile介紹了藍芽裝置之間是如何發現彼此、建立連線、實現配對與繫結的,同時描述了裝置如何實現無需連線的資料傳輸、如何實現等時同步資料傳輸、如何建立ACL和CIS 連線、如何加密認證通訊鏈路等。

本篇文章主要介紹藍芽裝置建立連線後,如何提供或響應服務、如何發現或請求服務,這就要靠GATT(Generic Attribute Profile)來定義了,GATT 為藍芽裝置定義了Server role 和Client role 兩種角色(角色並不固定在裝置上,同一裝置不同時刻充當的角色可能不同):

  • Server role:接收來自使用者端的服務請求,並向用戶端返回響應資料。GATT 中的角色與GAP 中的角色相互獨立又相互相容,GAP 中的Peripheral role 或Central role 都可以充當GATT 中的Server role;
  • Client role:向伺服器傳送服務請求,並接收來自伺服器的響應資料。GATT 中的角色與GAP 中的角色相互獨立又相互相容,GAP 中的Peripheral role 或Central role 都可以充當GATT 中的Client role。

An example of a light state machine
Server 根據設計需求會實現並向外公開一個或多個服務,Client 則根據需要發現Server 公開的部分或全部服務。Client 發現Server 提供的服務後,就可以向其請求相應的服務資料(既可以讀取伺服器狀態資訊比如溫度感測器的值,又可以改變伺服器的狀態比如控制LED燈的亮滅),Server 接收到服務請求後會向Client 響應對應的服務資料(Server 可能需要Client 通過認證與授權)。

BLE 採用「Server – Client」 架構,Server 專注於定義如何使用一個或多個屬性來實現某種特定的服務(包括可以提供的狀態資訊、可以執行的狀態切換等)以及如何存取並使用這些服務(比如使用者端請求可讀取/可寫入狀態資訊、伺服器可通知狀態更新資訊、存取服務是否需要加密/認證/授權等);Client 專注於定義如何使用一個或多個服務來滿足某種特定的應用需求(比如可以使用光強感應服務、人體感應服務、照明服務等協同實現智慧照明服務)。

Server 定義的每個單獨服務十分簡單、而且是原子化的(表示一種服務只執行不可分割的特定操作),可以讓每個服務的行為更簡單明確,同時讓不同服務之間的組合更豐富多樣。Client 使用不同的原子服務組合,可以滿足豐富多樣的場景需求,相同的服務可以在不同應用中複用,滿足高內聚、低耦合的設計原則。

為何說明Client 請求服務、Server 響應服務的工作原理,下面擴充套件前篇博文談到的使用者場景:

使用者購買的心率帶裝置為了安全傳輸心率資料,是可以被發現並連線的,對外公開的主要有心率服務、電池電量服務、裝置名服務等。使用者的智慧手機通過GAP 規範發現並連線了心率帶裝置(可以通過公開的裝置名服務從裝置列表中選出目標裝置),由於使用者需求是獲得心率值,心率帶對外提供該服務,因此心率帶處於Server role,智慧手機處於Client role。

Client role 與Server role 建立連線後,Client role 首先需要知道Server role 能提供哪些服務,然後再根據應用規範去使用這些服務。Client role 發現Server role 公開服務的過程主要有四個:Primary Service Discovery(可以發現所有主要服務或者某一特定主要服務)、Relationship Discovery(該服務參照的其它服務)、Characteristic Discovery(可以發現所有特性或者某一特定特性)、Characteristic Descriptor Discovery。 智慧手機先發現心率帶公開的心率服務和電池電量服務,再發現心率服務參照的時間服務(Secondary Service),接著發現心率服務包含的HRM Characteristic(Heart Rate Measurement) 和BSL Characteristic(Body Sensor Location),最後發現HRM Characteristic 包含的CCCD(Client Characteristic Configuration Descriptor)。Heart Rate Service、Characteristic、Descriptor 之間的關係如下圖示,每一行四個欄位Handle、UUID、Permissions、Value 共同構成一個屬性,Characteristic 與Descriptor 都是由一個或多個屬性構成的。

Client role 已經發現了Server role 公開的所有Primary Service及其相關的Characteristic 和Descriptor,接下來看Client role 如何使用Server role 提供的服務。智慧手機可通過特定APP向心率帶發起服務請求讀取心率值,心率帶將當前採集到的心率值返回給智慧手機(也可以設定為允許心率帶主動通知智慧手機當前的心率值),如果再為心率帶增加時間服務,智慧手機APP 就可以藉助心率服務和時間服務為使用者繪製心電圖,並給出心率健康評估結果。Handle 0x0021 屬性是Heart Rate Service 的declaration(宣告服務型別為心率服務),Handle 0x0024 和0x002A 屬性分別是HRM Characteristic 和BSL Characteristic 的declaration(宣告特性性質為唯讀,特性數值屬性控制程式碼分別為0x0027和0x002C),Handle 0x0027 和0x002C 屬性分別是HRM Characteristic 和BSL Characteristic 的Value(描述特性數值的表示格式,心率測量值單位bpm – beats per minute,身體測量位置為手指),Handle 0x0028 屬性是HRM Characteristic 的Descriptor(使用者端特性設定為啟用通知,允許伺服器端主動通知使用者端更新後的心率值)。

GATT Heart Rate Service

一、Generic Attribute Profile

GATT 使用屬性協定定義了服務架構,方便Client 與Server 之間實現基於服務請求/響應的通訊,簡化了藍芽裝置應用程式的開發。一個服務通常包含多個屬性,GATT 定義瞭如何使用屬性協定來發現、讀取、寫入這些屬性資料。
Protocol model
最上層的Application 應用層在Server 伺服器端主要是基於屬性協定定義各種服務的GATT 規範,在Client 使用者端主要是定義如何發現並使用這些服務來滿足某種特定的應用需求或實現某種特定的業務邏輯,本文主要介紹如何定義、發現、使用服務的GATT 規範。Attribute protocol 相當於GATT 的承載層,GATT 定義的服務是由多個屬性組成的,Attribute protocol 定義了屬性的報文格式和支援的操作方法。L2CAP 邏輯鏈路控制適配協定定義了報文的分片重組、重傳流控,上層GATT 或Attribute protocol 層資料封裝為SDU(Service Data Unit),下層Controller 資料封裝為PDU(Protocol Data Unit)。

1.1 UUID and Attributes

在繼續介紹GATT Profile 之前,先介紹一個藍芽協定中比較重要的兩個概念UUID(Universally Unique Identifier) 和Attributes。

1.1.1 UUID

UUID(Universally Unique Identifier) 通用唯一識別碼是一個128位元(16位元組)的數位,可以保證在所有空間和所有時間都是唯一的。除了藍芽之外,UUID 還用於許多協定和應用中(特別是分散式計算領域),它們的格式、用法在IETF(Internet Engineering Task Force)公佈的標準 RFC 4122 中定義(技術上等同於 ITU-T Rec. X.667 | ISO / IEC 9834-8:2005 規範)。

完整的UUID 需要佔用16位元組,這對於鏈路層27位元組的有效資料載荷長度算是不小的負擔,為了減輕儲存、傳輸128位元UUID 的負擔,BLE 規範新增了兩個附加的UUID 格式:16位元UUID 和32位元UUID。這些16位元或32位元的UUID 可以看作是基於Bluetooth Base UUID (00000000-0000-1000-8000-00805F9B34FB) 的偏移量,因此只能與Bluetooth 規範中定義的UUID 一起使用,也可以稱為標準Bluetooth UUID。這些標準Bluetooth UUID 可以藉助Bluetooth Base UUID 重建為完整的128位元UUID,只需要經過如下的運算即可:

128_bit_value = 16_bit_value * 296 + Bluetooth_Base_UUID
128_bit_value = 32_bit_value * 296 + Bluetooth_Base_UUID

16位元UUID 可以通過高位補零轉換為32位元UUID 格式,當UUID 包含在ATT PDU 中時,所有32位元UUID 都應轉換為128位元UUID。

16位元UUID 雖然已經比較簡短了,為了增加可讀性,通常不直接使用數值,而是起一個名稱並加上書名號來表示,比如用《Include》 來表示數值為0x2802 的UUID。UUID 並沒有定義自身的用法,為了增加人工偵錯時的可讀性,BLE 常用的那部分UUID 被分為以下幾組:

  • 0x1800 ~ 0x26FF 用作 Service UUIDs
  • 0x2700 ~ 0x27FF 用於標識計量單位 Units
  • 0x2800 ~ 0x28FF 用於區分 Attribute Types
  • 0x2900 ~ 0x29FF 用作 Characteristic Descriptors
  • 0x2A00 ~ 0x7FFF 用於區分 Characteristic Types

1.1.2 Attributes

Attribute 是ATT(Attribute protocol)定義的最小資料實體,也是構成GATT 中Service、Characteristic、Descriptor 的基本元素。每個Attribute 都包含有關屬性本身的資訊和實際資料,使用者端與伺服器之間進行的服務互動,最終都是對這些屬性的操作存取。從前文GATT Heart Rate Service 圖示瞭解到,Attribute 由Handle、UUID、Permissions、Value 四部分構成,其邏輯表示如下:
Logical attribute representation

  • Attribute handle:屬性控制程式碼是特定GATT伺服器上每個屬性的唯一16位元識別符號,便於區分並定址某個屬性。有效的控制程式碼範圍是0x0001 ~ 0xFFFF,可以把屬性控制程式碼看作該屬性資源的記憶體地址或者指向該屬性資源的指標;
  • Attribute type:使用UUID 區分並標識每種屬性的型別,比如溫度、壓強、體積、距離、功率、時間、充電狀態、開關狀態等可公開資料的型別,屬性協定為每種型別都分配了一個UUID(16位元標準Bluetooth UUID 或完整的128位元UUID);
  • Attribute value:表示裝置公開的狀態資訊,可以是固定長度或可變長度的八位位元組陣列,最大長度為512位元組。屬性值欄位不僅可以表示狀態資訊,還對上層的GATT Profile 層級結構的定義有重要作用,比如定義Service 宣告的屬性值為Service UUID、Characteristic 宣告的屬性值為<Characteristic Properties | Characteristic Value Attribute Handle | Characteristic UUID>;
  • Attribute permissions:表示每個屬性的存取限制或許可,屬性限制可分為四個型別:Readable / writable 的存取許可權、Encryption / No encryption required 的加密許可權、Authentication / No Authentication required 的認證許可權、Authorization / No Authorization required 的授權許可權等,也可以使用上述多個型別的組合許可權。

下面給出某BLE 裝置屬性伺服器定義的部分屬性供參考,支援的服務屬性包括:GAP Service、GATT Service、Battery State Service、Thermometer Humidity Service、Weight Service、Position Service、Alert Service、Manufacturer Service和Vendor Specific Service等(下圖用《》括起來的名字是UUID 的另一種表示方式,Attribute Value 中被{ } 括起來的部分表示該屬性值由多個欄位組成,被「 」 括起來的部分表示字串)。
Examples of attribute server attributes

1.2 GATT Profile hierarchy

GATT 中定義了的Profile 由一個或多個Service 組成,每個Service 可以看作是完成特定功能或特性的資料和相關行為的集合,由一個或多個Characteristic 構成。每個Characteristic 一般都包含一個資料或者一個公開行為,以及該資料的單位或者公開行為的單位。GATT Profile 的資料層級結構如下圖示:
GATT Profile hierarchy

  • Service:完成特定功能或特性的資料和相關行為的集合,由一個或多個Characteristic 構成。可分為Primary Service 和Secondary Service 兩種型別:Primary Service 是公開此裝置主要可用功能的服務(主要服務型別的16位元UUID 值0x2800);Secondary Service 是為裝置提供額外輔助資訊,但跟裝置公開的功能無關的服務,一般被包含在主要服務或另一個輔助服務中(輔助服務型別的16位元UUID 值0x2801)。Service declaration 屬性存取許可權為唯讀且不需要認證授權,屬性值為具體的服務種類,比如Heart Rate Service 的16為UUID 值為0x180D;
    Service declaration
  • Include Service:將伺服器上存在的另一種服務定義參照到要定義的服務中,是一種重用其它服務的方法。當某一個服務被包含到當前服務中,當前服務就可以使用被包含服務定義的資料和行為,但不能更改被包含服務的資料和行為。Include declaration 的屬性值包含三個欄位:被包含服務宣告的屬性控制程式碼、被包含服務屬性組合中的最後一個屬性控制程式碼、被包含服務的UUID,比如前面給出的範例屬性列表中的《Include》屬性值為{0x0500, 0x0504, «Manufacturer Service»};
    Include declaration
  • Characteristic:包含服務中使用的資料資訊,包括如何存取該資料的屬性方法、如何表示或顯示該資料的格式資訊等。Characteristic declaration 屬性值也包括三個欄位:特徵存取方法、特徵值屬性控制程式碼、特徵的UUID,比如本文開頭Heart Rate Service 中的HRM Characteristic declaration 的屬性值為{Notify, 0x0027, 0x2A37},其中0x2A37 是Heart Rate Measurement 的16位元UUID;
    Characteristic declaration
  • Characteristic Properties:包含當前Characteristic 支援的存取方法資訊,支援的特徵存取方法有Broadcast、Read、Write、Notify、Indicate、Authenticated Signed Writes等,比如本文開頭Heart Rate Service 中的HRM Characteristic Properties 為Notify,BSL Characteristic Properties 為Read;
    Characteristic Properties bit field
  • Characteristic Value:包含當前Characteristic 定義的資料數值資訊,比如本文開頭Heart Rate Service 中的HRM Characteristic Value declaration 的屬性型別為Heart Rate Measurement 的UUID也即0x2A37,屬性值為實際測得的心率值;
    Characteristic Value declaration
  • Characteristic Descriptor:包含當前Characteristic 的描述資訊、設定資訊、表示格式資訊等,比如本文開頭Heart Rate Service 中的HRM Characteristic 包含了一個CCCD(Client Characteristic Configuration Descriptor),該CCCD declaration 的屬性存取許可權為可讀寫,屬性值為0x0001,也即Bit Number 0 位Notification被置 1,表示為HRM Characteristic 啟用通知功能,Server 心率帶測量到的心率值以通知形式主動傳送給Client 智慧手機;
    Characteristic descriptor declarations

上述介紹的構成GATT Profile 的Service、Include Service、Characteristic、Characteristic Descriptor 等元素的屬性彙總如下:
Summary of GATT Profile attribute types

1.3 GATT feature and procedure

前面介紹瞭如何使用Attribute 構成GATT Profile 資料層級結構,這裡介紹如何使用Attribute protocol 來完成GATT Profile 定義的各種功能。GATT Profile 主要定義瞭如下11 個功能(feature),每個功能都對映到過程(procedure)和子過程(sub-procedure),這些過程和子過程描述瞭如何使用Attribute protocol 來完成相應的功能(下表中的M:Mandatory,O:Optional )。
GATT feature mapping to procedures

  • Server Configuration:Client 使用Exchange MTU (Maximum Transmission Unit) 過程與Server 交換雙方支援的MTU,僅當Client 或Server 可以處理的MTU 超過預設值23 位元組(LE 鏈路層支援的普通報文payload 為27 位元組,L2CAP Header 佔用4 位元組,所以ATT_MTU 預設為23 位元組)時才會使用該過程(該過程對連線雙方都是可選的);
    Exchange MTU
  • Primary / Include Service Discovery:Client 使用Discover All Primary Services 或Discover Primary Services By Service UUID 子過程發現Server 公開的主要服務(可以發現所有主要服務或通過UUID 發現某個特定主要服務)。待Client 發現主要服務後,可以通過Find Included Services 過程發現該主要服務所包含的其它服務,兩個過程可以讓Client 瞭解Server 公開的所有主要服務與輔助服務;
    Discover All Primary and Include Service
  • Characteristic / Descriptor Discovery:Client 使用Discover All Characteristic of a Service 或Discover Characteristic by UUID 子過程發現Server 公開的某服務的Characteristic(可以發現某服務包含的所有Characteristic或者通過UUID 發現某個特定的Characteristic,通過UUID 發現某個特徵實際上是發現所有特徵後丟棄掉與UUID 不匹配的結果)。待Client 發現Characteristic 後,可以通過Discover All Characteristic Descriptors 過程發現該Characteristic 包含的所有Descriptors。Characteristic 和Characteristic Descriptors 是構成Service 的基本元素,也是讀寫Server 公開資料或狀態資訊的載體;
    Discover All Characteristic and Characteristic Descriptors
  • Characteristic Value Read:當Client 知道想要讀取的某個Characteristic 的特徵值屬性控制程式碼時,使用Read Characteristic Value 子過程從Server 讀取該特徵值,如果Server 響應的特徵值長度大於(ATT_MTU - 1)位元組則只能返回特徵值的前半部分,Client 可以使用Read Long Characteristic Values 子過程讀取完整的特徵值。當Client 知道想要讀取的一組特徵值控制程式碼集時,可以使用Read Multiple Characteristic Values 子過程讀取該組的多個特徵值。當Client 只知道想要讀取的特徵型別UUID 而不知道特徵值控制程式碼時,可以使用Read Using Characteristic UUID 子過程讀取已發現該特徵型別的所有特徵值;
    Read Characteristic Value
  • Characteristic Value Write:當Client 知道想要寫入的某個Characteristic 的特徵值屬性控制程式碼時,使用Write Characteristic Value 子過程向Server 寫入一個特徵值,如果要寫入的特徵值長度大於 (ATT_MTU – 3) 位元組則應使用Write Long Characteristic Values 子過程向Server 寫入一個比較長的完整特徵值。當Client 不需要確認寫入是否成功執行時,可以使用Write Without Response 子過程向Server 寫入特徵值,該過程可以即時傳送而不受流控約束,若Server 無法處理則靜默丟棄。當Client 需要在單個操作中按順序寫入多個值時,可以使用Characteristic Value Reliable Writes 子過程向Server 傳輸要準備寫入的多個特徵值,待Client 驗證伺服器響應的資料無誤後,對已傳輸到Server 上且經過檢驗的全部特徵值執行寫入操作;
    Write Characteristic Value
  • Characteristic Descriptor Value Read / Write:Client 讀取或寫入Characteristic Descriptor Value 跟前面介紹的讀取或寫入Characteristic Value 類似,當Client 知道想要讀取或寫入的Characteristic Descriptor 屬性控制程式碼時,使用Read Characteristic Descriptors 或Write Characteristic Descriptors 子過程從Server 讀取或向Server 寫入特徵描述符。如果特徵描述符的長度大於單個請求報文容納的上限,Client 可以使用Read Long Characteristic Descriptors 或Write Long Characteristic Descriptors 子過程從Server 讀取或向Server 寫入比較長的完整特徵描述符;
    Characteristic Descriptor Value Read / Write
  • Characteristic Value Notifications:當Server 被設定為向Client 通知特徵值,且不需要確認是否成功接收到通知時,使用Notifications 子過程向Client 通知特定屬性控制程式碼及其對應的特徵值。當Server 被設定為向Client 通知多個特徵值時,可以使用Multiple Variable Length Notifications 子過程;
    Characteristic Value Notifications
  • Characteristic Value Indication:當Server 被設定為向Client 指示特徵值,並期望屬性協定層確認已成功接收到指示時,使用Indications 子過程向Client 指示特定屬性控制程式碼及其對應的特徵值,後續會受到來自Client 的確認資訊。
    Characteristic Value Indication

上述介紹的每個GATT 功能及其子過程到ATT 屬性協定操作指令之間的對映關係彙總如下(屬性協定操作指令將會在下文介紹):
GATT procedure mapping to ATT protocol opcodes

二、Attribute protocol

前面已經介紹了Attribute 的邏輯結構,這裡主要介紹Attribute 的報文格式和支援的操作指令或方法。

2.1 Attribute PDU Format

Attribute PDU 包含三個部分:1 位元組長度的指令操作碼、可變長度的屬性引數、可選的12 位元組長度認證簽名資訊,屬性PDU 格式如下:
Attribute protocol PDU
Attribute PDU 中的Attribute Parameters 欄位通常包含前面介紹的屬性邏輯結構中Attribute Handle、Attribute Type、Attribute Value、Attribute Permissions 四個欄位中的零個或多個,只有Server 與Client 之間需要互動的欄位資訊才會包含在Attribute PDU 中。

2.2 Attribute protocol methods

GATT Profile 的Procedure 描述瞭如何使用Attribute protocol 來完成相應的Feature,Client 通過Attribute protocol 可以發現並獲取或更改屬性伺服器上的屬性。按照Client 與Server 之間的互動方式劃分,Attribute protocol 由以下六種基本操作構成(也即Attribute PDU 有六種基本型別,由字尾區分):
Attribute PDUs

  • Request / Response:Client 向Server 傳送ATT Request,以要求伺服器執行相關操作並返回ATT Response。Client 一次只能傳送一條Request,也即只能在收到上一條請求的Response 後才能繼續傳送下一條Request,這降低了構建屬性伺服器的複雜度。Server 返回給Client 的響應可能是與請求直接相關的響應,也可能是說明請求為何失敗的錯誤響應;
    An Attribute Protocol request
  • Command:Client 向Server 傳送ATT Command,讓伺服器執行某種操作,但不需要Server 返回響應。Client 可以連續向Server 傳送多條命令而不受流控約束,當Server 無法響應命令時可直接忽略,Client 不知道Command的執行結果也不會收到錯誤響應,因此ATT Command 的執行是不可靠的;
    An Attribute Protocol command
  • Indication / Confirmation:Server 向Client 傳送ATT Indication,讓使用者端獲知某屬性的數值,Client 需要返回給Server 一個ATT Confirmation。Server 一次也只能傳送一條Indication,也即只能在收到上一條指示的Confirmation 後才能傳送下一條Indication;
    An Attribute Protocol indication
  • Notification:Server 向Client 傳送ATT Notification,以告知使用者端某屬性的數值,但不需要Client 返回確認資訊。Server 也可以連續向Client 傳送多條命令而不受流控約束,Client 來不及處理的通知也將直接丟棄,Server 不知道Notification 的執行結果也不會收到錯誤資訊,因此ATT Notification 的執行也是不可靠的。
    An Attribute Protocol notification

Attribute protocol 支援的操作指令彙總如下(對照GATT procedure 到ATT protocol opcodes 的對映關係更容易理解):
Attribute protocol summary

  • Error handling:指令ATT_ERROR_RSP 用於返回請求執行失敗的原因,返回的屬性引數包括產生此錯誤的請求的屬性操作碼、產生該錯誤的原始請求中的屬性控制程式碼、表示產生此錯誤的原因的錯誤碼等;
  • MTU exchange:在BLE 連線中,屬性協定預設的MTU 長度是23 位元組,如果裝置想傳送更大的封包,可以通過ATT_EXCHANGE_MTU_REQ 和ATT_EXCHANGE_MTU_RSP 指令協商一個更長的MTU。一般由Client 發起交換MTU 的請求,協商結果為Client / Server 二者可接收的MTU 長度中的較小值;
  • Find information:查詢資訊請求有兩種:一種是Client 傳送ATT_FIND_INFORMATION_REQ 指令,請求給定屬性控制程式碼集合內的所有屬性資訊,比如前面介紹的Discover All Characteristic Descriptors 過程;另一種是Client 傳送ATT_FIND_BY_TYPE_VALUE_REQ 指令,請求符合特性屬性型別UUID 的所有屬性資訊,比如前面介紹的Discover Primary Services By Service UUID 過程;
  • Reading attributes:讀取屬性值請求有五種:一、ATT_READ_BY_TYPE_REQ 指令按屬性型別UUID 讀取屬性值;二、ATT_READ_REQ 指令按屬性控制程式碼Handle 讀取屬性值;三、ATT_READ_BLOB_REQ 指令按屬性控制程式碼Handle 配合偏移量Offset 讀取長屬性值;四、ATT_READ_MULTIPLE_REQ 和ATT_READ_MULTIPLE_VARIABLE_REQ 指令按屬性控制程式碼集合Set Of Handles 讀取多個屬性值;五、ATT_READ_BY_GROUP_TYPE_REQ 指令按屬性組型別UUID 讀取一組屬性值;
  • Writing attributes:普通的寫入屬性值請求有兩種:一、ATT_WRITE_REQ 指令按屬性控制程式碼Handle 寫入屬性值,需要接收到響應資訊;二、ATT_WRITE_CMD 和ATT_SIGNED_WRITE_CMD 指令也是按屬性控制程式碼Handle 寫入屬性值,但不需要返回任何響應資訊;
  • Queued writes:排隊寫入屬性值請求分為兩個階段:第一階段是準備寫入請求,也即Client 通過ATT_PREPARE_WRITE_REQ 指令將要寫入的一個或多個屬性值傳送到Server,並通過ATT_PREPARE_WRITE_RSP 指令確認Server 接收到的屬性值無誤;第二階段是執行寫入請求,也即Client 通過ATT_EXECUTE_WRITE_REQ 指令將之前傳送給Server 並經確認無誤的屬性值立即寫入到對應的屬性中。比如前面介紹的Write Long Characteristic Values 或Characteristic Value Reliable Writes 過程;
  • Server initiated:伺服器啟動更新請求實際上是從Server 向Client 傳送屬性資訊的方式,伺服器上屬性值有更新時可以及時將更新後的屬性值傳送給使用者端,這樣使用者端就不需要定期輪詢屬性值,從而節省了功率和頻寬。伺服器啟動更新過程也有兩種方式:一、Server 傳送ATT_HANDLE_VALUE_IND 指令,讓Client 獲知特定屬性控制程式碼及其對應的屬性值,且需要使用者端返回ATT_HANDLE_VALUE_CFM 確認指令;二、Server 傳送ATT_HANDLE_VALUE_NTF 指令,讓Client 獲知特定屬性控制程式碼及其對應的屬性值,但不需要使用者端返回確認資訊,Server 還可以通過傳送ATT_MULTIPLE_HANDLE_VALUE_NTF 指令,讓Client 一次性獲知多個屬性控制程式碼的屬性值。

三、L2CAP Protocol

3.1 L2CAP Overview

本文開頭已經介紹了在Attribute protocol 與LE Controller 之間還有一層L2CAP(Logical Link Control and Adaptation Protocol and Adaptation Protocol) 協定層,L2CAP 協定層的功能架構圖如下(可以將L2CAP 協定與TCP 協定對比理解):
L2CAP architectural blocks

  • Protocol/channel multiplexing:L2CAP 上層不止承載一個協定,比如ATT 屬性協定、SMP 安全管理協定等報文都需要經L2CAP 路由到LE Controller,這裡L2CAP 起到協定多路複用器的作用,L2CAP 為上層每個協定分配一個邏輯通道,多個邏輯通道可以複用下層LE Controller 的同一個物理通道,來傳輸不同的協定報文;
  • Segmentation and reassembly:L2CAP 上層的應用封包或服務封包SDU(Service Data Unit) 通常比較大(最大可達到64 KB),下層LE Controller 可接受的報文PDU(Protocol Data Unit)容量較小(有效負載最大為27 Bytes,擴充套件報文最大為 251 Bytes),中間的L2CAP 可以起到封包分段重組的作用,將上層Application 較大的SDU 分段為較小的PDU 後傳送給LE Controller,或將從LE Controller 接收到的多個PDU 重組為完整的SDU 再遞交給上層Application 處理;
  • Flow control per L2CAP channel:為了避免空中傳輸鏈路堵塞,導致資料傳輸錯誤或丟失,L2CAP 協定為每個L2CAP channel 提供了基於視窗的流量控制機制;
  • Error control and retransmissions:空中傳輸鏈路容易受到干擾,導致資料傳輸錯誤或丟失,L2CAP 協定為傳輸的資料提供錯誤校驗和重新傳輸機制,當發現傳輸的封包錯誤時,請求對方重新傳輸錯誤的封包,跟流量控制機制一起保證藍芽無線傳輸的可靠;
  • Support for Streaming:藍芽音訊服務是一個很重要的應用場景,像音訊這類資料流傳輸對時延比較敏感,對偶爾出現的少量錯誤是可以容忍的,L2CAP 協定為Streaming 流資料傳輸提供了相應的L2CAP channel,而且不對其使用流量控制和錯誤重傳機制,儘可能保證流資料傳輸的及時同步。

3.2 L2CAP PDU Format

L2CAP 既然起到多協定複用器的作用,可以將多個邏輯通道對映到同一個物理通道,為了便於區分不同的邏輯通道,L2CAP 報文需要包含Channel ID 欄位,下面給出L2CAP 的基本報文格式如下(為了實現更多上述功能,L2CAP 還有更復雜的報文格式):
L2CAP PDU format in Basic L2CAP mode
BLE 協定棧中L2CAP 支援的CID (Channel ID) 型別如下:
CID name space on LE-U logical link
上述CID 主要分為兩類:一類是固定通道,比如為Attribute protocol 分配的CID 是0x0004、為L2CAP LE Signaling channel 分配的CID 是0x0005、為Security Manager protocol 分配的CID 是0x0006;另一類是使用過程中動態分配的可變通道。下文將分別對BLE 支援的三個L2CAP 固定通道報文及指令做簡單介紹。

前面介紹的只是Basic L2CAP Mode下的報文格式,L2CAP 為Flow Control Mode、(Enhanced) Retransmission Mode、Streaming Mode、LE (Enhanced) Credit Based Flow Control Mode 提供了更復雜的報文結構如下(本文不展開介紹了,可以參考Core_v5.2 手冊的[Vol 3] Part A, Section 3 ):
L2CAP PDU formats in other modes

3.2.1 L2CAP PDU format on a signaling channel

從L2CAP architectural blocks 圖中可以看到兩種資料流:紅色的Controls frame 和黑色的Data/packet flow,其中Controls frame 用於L2CAP 通道管理,傳輸Controls frame 通道為signaling channel,在signaling channel 上傳輸的Controls frame(C-frame) 格式如下:
L2CAP PDU format on a signaling channel and Command format
對於BLE 協定,在signaling channel 上傳輸的Controls frame 的CID 為0x0005(對於BR/EDR 協定,C-frame 的CID 為0x0001)。Controls frame 的Information payload 部分是signaling command format,包括信令操作碼Code、信令識別符號Identifier、資料長度Length、資料資訊data 四個部分,這裡只列出BLE 協定常用的信令操作碼及其描述如下(這裡刪去了BR/EDR支援而BLE不支援的信令操作碼):
Signaling command codes

  • Command Reject:當裝置收到了不支援或不理解的命令時,或者接收到的命令超過MTU 而溢位時,可以返回L2CAP_COMMAND_REJECT_RSP 報文告知對方拒絕執行命令的原因;
  • Create an L2CAP channel:通過傳送L2CAP_LE_CREDIT_BASED_CONNECTION_REQ 或L2CAP_CREDIT_BASED_CONNECTION_REQ 命令,在兩個裝置之間建立並設定L2CAP Channel;
  • Connection Parameters Update:在BLE 連線中,如果Slave 希望修改當前的連線引數可以使用L2CAP_CONNECTION_PARAMETER_UPDATE_REQ 命令向Master 發起連線引數更新請求。Master 可以接受或者拒絕Slave 請求的連線引數,並通過相應的響應報文告知Slave 結果;
  • Reconfigure MTU or MPS:當裝置支援的MTU(Maximum Transmission Unit) 或MPS(Maximum PDU Payload Size) 值變化時,可以傳送L2CAP_CREDIT_BASED_RECONFIGURE_REQ 命令重新設定MTU 或MPS 的值;
  • Flow Control:當裝置還能接收Credit-based frame (K-frame)時,通過傳送L2CAP_FLOW_CONTROL_CREDIT_IND 告知對方可以繼續傳送的K-frame 數量,用於流量控制;
  • Terminating an L2CAP channel:裝置可以傳送L2CAP_DISCONNECTION_REQ 命令來終止特定的L2CAP Channel。

3.2.2 L2CAP PDU format on Security Manager protocol

前篇博文介紹了Security Manager protocol 的配對過程,兩個裝置之間要完成配對也需要雙方互動相關的指令或報文,SMP 配對和安全管理相關的指令或報文格式如下:
L2CAP PDU format on Security Manager protocol
L2CAP 為Security Manager protocol 分配的Channel ID 為0x0006,SMP command Information payload 包含指令操作碼Code 和資料Data 兩部分,這裡列出BLE 支援的SMP 指令操作碼如下:
SMP command codes
下面依然按照BLE 配對過程的三個階段簡單介紹SMP 指令:

  • Phase 1(Pairing Feature Exchange):Master 使用Pairing Request 報文開始配對功能交換過程,Slave 也可以使用Security Request 命令請求Master 開始配對過程。如果配對過程中出現問題(包括下面兩個階段中出現的問題),則通過Pairing Failed 報文告知對方配對失敗的原因;
    Slave security request, master initiated pairing
  • Phase 2(Long Term Key Generation):雙方成功完成配對功能交換後,通過Pairing Public Key 報文交換本裝置的Public Key(如果Slave 有鍵盤輸入能力,可通過Keypress Notification 報文通知Master 輸入的配對碼),通過Pairing Confirm 和Pairing Random 報文交換Confirm value 和Random value, 然後開始計算並生成LTK。LTK 計算完成後,雙方通過Pairing DHKey Check 報文生成並相互驗證DHKey value,如果驗證成功則表示生成LTK 的過程沒有問題;
    Long Term Key Generation
  • Phase 3(Transport Specific Key Distribution):雙方建立加密連線後,開始生成並分發一些專用的金鑰。比如使用Encryption Information 報文分發LTK(用於加密對談資訊),使用Master Identification 分發EDIV(Encrypted Diversifier) 和Rand(用於計算並生成LTK),使用Identity Information 報文分發IRK(Identity Resolving Key,用於生成或解析裝置的resolvable private address),使用Identity Address Information 報文分發裝置的public device address 或static random address,使用Signing Information 報文分發CSRK(Connection Signature Resolving Key,用於對資料進行簽名或驗證接收資料的簽名資訊)。
    Transport specific key distribution

3.2.3 L2CAP PDU format on Attribute protocol

Attribute protocol 的報文格式前面已經介紹過了,在L2CAP 層加上一個L2CAP Header,其中Channel ID 為0x0004,報文格式如下:
L2CAP PDU format on Attribute protocol
Attribute protocol 支援的操作指令前面已經介紹過了,這裡不再贅述。

更多文章: