LMP 全稱是Link Manager Protocol,我們還是要一張圖,說明LMP 在哪里?
他是在HCI 以下,baseband 以上,實現在藍牙控制器中。
按照協議規范,我們分幾個部分來分別介紹LMP
- introduction
- general rules
- device features
- procedure rules
下面我們先來看看 整體的介紹部分:
Introduction
LMP是用來控制和協商兩個設備連接行為的協議,涉及的方面包括邏輯傳輸連接的建立和控制,以及對於物理鏈路的控制等等,它是兩個設備的LMP模塊之前的交流,其消息是傳輸在ACL-C 的邏輯鏈路上,對於這一點,我們在baseband的概述中已經提過:
這里需要注意的一點是,LMP 是實現在控制器里面,其消息並不會經過HCI 接口傳輸到HOST 端,其架構如下圖:
我們可以看到其是 兩個設備的LMP 模塊之前的通信。
General rules
2.1 Message Transport
這里描述的就是三個點:
- LMP的消息的交互是傳輸在ACL-C 邏輯鏈路上的,他和ACL-U的區別是通過LLID 來區分的。
- ACL-C有更高的優先級,一般控制信號都會有較高的優先級
- LMP消息本身沒有攜帶額外的數據錯誤檢測的信息
2.2 Synchronization
LMP 傳輸在ACL-C的邏輯鏈路上面,但這並不能保證傳輸的時間的以及對方ack的及時性。
2.3Packet Format
LMP 的數據包的格式有兩種,一種是7bit的opcode,另外一種是15bit的opcode
我們發現除了opcode,在最低位還有一個TID,他是transtransaction ID,當它是0 的時候,說明這個LMP message是 master 發起的,當它是1的是時候說明是slave 發起的。
上圖中是 header的最低位是0,代表是Transaction ID Initiated by master。
2.4Transactions
transaction 就是一次完整的打成某種目的的傳輸。所有屬於同一個transaction 的單次傳輸的transaction ID 都是想同的。
比如下面:
LMP version exchange 的Transaction ID 都是 Initiated by slave
另外LMP_setup_complete 是一個 獨立PDU,它自己形成一個transaction,
上面兩個OpCode LMP_setup_complete 的transaction ID 是不同的,他們是獨立的PDU
LMP response的timeout 時間是30s
2.5ERROR HANDLING
關於出錯處理,每一個錯誤都分配了一個錯誤碼,常見的錯誤如下:
- Unknown opcode(0x19)
- Invalid parameters(0x1E)
- PDU is not allowed(0x24)
- PDU not supported(0x1A)
- Transaction Collision / LL Procedure Collision(0x23)
- Different Transaction Collision(0x2A)
2.6GENERAL RESPONSE MESSAGES
general response 在很多的procedure中,被用來對於各種流程的應答消息,如果opcode是7bit,那么應該使用前兩種 PDU,如果opcode 是15bit,那么應答的PDU 應該使用后兩種。
DEVICE FEATURES
device feature,顧名思義,這里面定義了很多的設備的特性,並且這些特定是可以用特定的LMP message 來獲取的。那么這些feature 是如何傳輸的呢?其實就是定義了一些bit 位來代表相應的feature,當這些位被設置為1的時候,就說明支持該feature,這個我們在程序代碼實現中經常見到這樣的技巧。
這些feature 定義在兩個page 上面,page1 里面使用8個字節,64bit 定義了64個feature,page2用2個字節定義了12個feature,其中包括2個reserved 位。
這里簡單列舉幾個feature:
PROCEDURE RULES
這里包含了很多的基本流程,主要有如下的項目:
-
Connection control
- Information Requests
- Role Switch
- Modes of Operation
- Logical Transports
我們下面分別看看這幾個流程:
4.1Connection control
這里每一個流程都有很多的子流程,這里只簡單介紹下Connection establishment和Detach的流程:
下面先看一下Connection establishment的流程圖
在經過了page的流程之后,可能的行為是請求時鍾的偏移量,LMP的版本,對端支持的feature請求,對端的name請求,以及可能的deteach行為。
之后如果要建立連接的話,paging的設備就會發送LMP_host_connection_req,對端如果同意建立連接就會回復LMP_accepted 否則就是LMP_not_accepted
后續可能走得關於認證和加密的流程,最后是LMP_setup_complete,下面我們來看一個例子:
下面的建立連接的PDU:
下面我們看看detach 的流程:
我們發現關於detach 是沒有LMP response 的消息的過來的,從實際的air log 中,也的確沒有detach response 回復,對端設置只會簡單的ack 該 消息:
下面我們看看 從設備的host 端 去斷開對端設備的 LMP 與HCI 的message 流向:
我們發現是 A設備再收到了對端的ACK 之后就會向host 端上傳斷開完成事件。
關於 驗證和加密的部分,會在分析配對的文章中專門講述。
下面我們我們看看
4.2Information Requests
這一類的message 交互都是 信息請求類型的,具體的有如下幾種:
-
Timing accuracy
- Clock offset
- LMP version
- Supported features
- Name request
這幾個流程也是非常的類型,我們也是挑選其中一個來 看一下:
我們看看lmp version 的流程:
其交互流程很簡單,發起端發起一個LMP_version_req,對端回應一個LMP_version_res
涉及到的PDU的包如下:
這里我們發現,發起這個LMP version請求的時候,還會攜帶本端的LMP version,Company ID 以及Sub Version Number,這個Sub Version Number 應該是公司內部的編號了,那這樣一來,一個request,一個response,就完成了LMP 相關版本信息的交互。我們看看air log 關於這交互的消息:
response 如下:
這里需要提一點就是LMP 數據包的payload header 里面也是有流控相關的,但是這個流控對於LMP 是不起作用的。其應該是主要針對ACL-U的數據。
這里再簡單名字的獲取的流程,當沒有建立物理link的時候,獲取名字和已經建立了link的獲取的名字的流程有點區別。我們先下有link的時候 流程:
有link的時候,很簡單,直接是在LMP 層進行LMP_name_req的交互,收到對方的應答之后,通過HCI接口上報的到host端,如果是沒有link的話,那么是要先建立一個臨時的link,其流程見下圖:
我們可以看到兩個設備經過page 之后,會先進行LMP feature exchange以及extend feature exchange,然后上報到host 端,接下來才進行LMP_name_req 的交互,最后是detach 這個臨時的link,
我們看看 air log 中實際的交互情況:
實際情況和上面的流程也是非常的吻合,最后會把這個 臨時建立起來的獲取名字的link斷開。
下面我們看看role switch 相關的
4.3Role switch
關於role switch ,因為我們平常在debug 中會遇到,值得仔細講一下:
role switch的執行需要符合幾個條件:
- 設備要處於active mode
- 加密的流程被終止或者已經關閉
- 在當前的link上面,沒有sco數據在傳輸
- 需要停掉當前link所有的ACL-U 數據流
role switch 從字面含義上來看,非常的簡單,就是交互角色,我們知道兩個設備連接之后,一開始發起配對的那個設備在建立連接之后就是master,這樣一種默認模式不能總是符合應用場景,所以經常在兩個設備之后需要交互角色。
role switch 是master 和slave 都可以發起的,我們先看一下,master 發起的角色交換的流程:
LMP 層面的交互很簡單,就三個指令。下面我們詳細講一下LMP_switch_req和LMP_slot_offset的 作用。
我們看出 LMP_slot_offset 攜帶兩個關鍵的信息,一個slot offset,另外一個 藍牙設備地址。這個地址是當前 slave 地址,也是role switch 之后master的地址。這個slot offset 是干嘛的呢?
這個在specification中原話是“The slot offset shall be the time in microseconds between the start of a master transmission in the current piconet to the start of the next following master transmission in the piconet where the BD_ADDR device (normally the slave) is master at the time that the request is interpreted by the BD_ADDR device.”
其含義就是,當前網絡的master的開始傳輸的時機(時鍾上升沿)到交換后的網絡master 進行傳輸的時機(時鍾上升沿)的時間間隔,次時間是用ms 來描述的。
從這里,我們能夠覺悟出,slot offset 應該都是由當前網絡的slave(role switch 之后的master) 來發送,而不管是 哪一端的設備先發起的,下面我們看看slave 端發起的role switch的交互流程:
我們發現,slot offset,的確是由slave端送出,之后才送出 LMP_switch_req,現在我們看看一下LMP_switch_req這個命令:
我們可以設想一下,藍牙設備之間的交互是要保持時鍾同步的,否則就會“失去聯系”,那么我們在進行role swtich 之后還是要保持時鍾同步才能繼續通信,那在什么樣的時機進行role switch呢?
我們先看下LMP_switch_req的參數:
我們發現其有一個參數是 switch instant ,這就是 描述 具體在哪一個時間點進行TDD switch。
這個instant 應該是一個未來的時間,如果是過去的時間,那么當接受者受到這個instant的時候,並不會接受這樣的參數,會回復 LMP_not_accepted PDU with the error code Instant Passed (0x28)。
當role switch 完成的時候,兩個設備都要向各自的host 上報HCI_Role_Change event,如下圖:
Modes of Operation
hold mode 很少遇到,這里主要講一下 sniff mode ,
sniff mode 就是呼吸模式,主要是為了省電而應用的,比如一些藍牙設備,比如鍵盤鼠標,當他們在active mode的時候,每一個master to slave slot,設備都要去監聽master是否有數據發過來。而當他們不使用的時候,如果還是一直處於active mode 的話,那么這段時間的消耗的電量就浪費了,而進入到sniff mode 可以使得設備進入了類似睡眠的狀態,只是定期去監聽master 的封包,這樣可以節省電量。
我們先看看 進入sniff mode 的流程圖:
我們這里主要着眼於LMP層面,當host端有進入sniff mode的命令下來,那么控制器 將向對方發出LMP_sniff_req的請求,對方如果同意,那么回復LMP_accepted。因為這是一個協商的過程,上圖只顯示了一次協商的過程,其實可能存在有發多次的LMP_sniff_req的情況。現在我們看看 LMP_sniff_req的參數:
Dsniff = Clock_Value (bits 26-1) mod Tsniff
Tsniff 是 sniff interval,就是每次起來監聽的時間間隔
Tsniff 就是 相鄰的兩個sniff anchor point的時間間隔。
sniff attempt 是 每次起來之后 嘗試去監聽的次數,如果監聽了sniff attempt 次數之后發現master 沒有發數據過來,那么再次進入到睡眠的狀態。
sniff timeout 是 在slave 去監聽master 的封包的時候,如果有封包發過來,那么就要繼續監聽額外的sniff timeout 個master to slave slot。
我們這里說一下Sniff 第一個錨點的計算方法:Dsniff = Clock_Value (bits 26-1) mod Tsniff
這里Dsniff 和Tsniff 是參數已經給出的,那么就是根據上面的公式去反推第一個錨點的clk的值。我們這里舉一個例子來說明:
其當前的clk值是Clock[27-0] 0x04301E02 這里的Dsniff 的值是0,
bit27 = 0,我們先看看當前的clk是0x04301E02,那么下一個錨點一定是 大於當前值而小於(0x04301E02+800*2)
我們先看看當前的Clock_Value (bits 26-1) mod Tsniff = (0x04301E02>>1)mod(800) = 737
那么下一個錨點的clk = ((800-737)+ (0x04301E02>>1))*2 = 0x4301E80
那么關於LMP 的概述就先介紹到這里。