1.1 GATT Manager
GATT MGR模塊管理所有的GATT服務,同時也是連接GATT模塊與GATT ServiceS模塊的橋梁。
1.1.1 主要功能模塊
先來看一張該模塊的API關系圖,sink_gatt_manager.c里面定義的接口主要供ApplicationLayer調用和回調,如用戶(BLE Server)調用sinkGattManagerStartAdvertising()開始進行廣播,用戶(BLE Client)調用sinkGattManagerStartConnection()開始進行BLE連接。該該部分同時負責接收和處理來自ApplicationLayer的消息——GATT_MANAGER_DISCONNECT_IND,GATT_MANAGER_REGISTER_WITH_GATT_CFM等GATT_MANAGER_XXX系列消息。這些消息主要用來處理注冊連接確認,斷開連接請求等事件。
Gatt_nanager_handler.c主要定義和實現了內部回調函數gattManagerMessageHandler(),該接口主要接收來自GATT模塊的GATT_XXX系列消息,以及GATT MGR內部的消息GATT_MANAGER_INTERNAL_MSG_XXX。處理這些消息的函數接口主要分布在gatt_manager_server.c和gatt_manager_client.c兩個函數內部。
根據BLE工作的角色不同,BLE分別定義了兩套接口,分別完成客戶和服務的角色的任務。
作為BLE客戶主要完成的任務有:
1.連接到服務器。這里包括主導發起連接至服務器,以及接收到服務器連接請求的確認。即:GattManagerConnectToRemoteServer()用來主動發起連接請求,在接收到GATT_CONNECT_CFM消息后,調用gattManagerClientRemoteServerConnected ()接口用於確認連接,完成連接的三次握手過程。
2. GATT支持notification和indication操作,因此客戶端需要有着兩種請求操作對應的接口。gattManagerClientRemoteServerNotification()和gattManagerClientRemoteServerIndication()就是為了實現這兩種操作巍峨設計的。
3. 獲取服務器的服務。根據GATT標准,獲取服務的服務主要有:獲取服務器主要GATT服務,獲取某個GATT服務的所有特征值定義,獲取某個GATT服務的特征值描述符,讀/寫某個特征值等。
作為BLE服務器主要完成的任務有:
1.廣播,以及處理客戶端的連接請求。gattManagerServerAdvertising()開啟廣播,GattManagerWaitForRemoteClient()等待遠程客戶發起連接,gattManagerServerConnectInd()處理連接請求。gattManagerServerRemoteClientConnected()處理連接請求確認(三次握手最后一次)消息。
2.注冊GATT service。GattManagerRegisterServer()向GATT MGR模塊注冊一個GATT service。
3.處理來自客戶端的GATT操作,如access,indication,notification等。其中indication,notification操作都是由服務器主動對客戶端發起的操作,因此客戶端和服務器端處理的消息剛好是互為匹配的,即服務器處理的消息有(GATT_ACCESS_IND, GATT_NOTIFICATION_CFM, GATT_INDICATION_CFM),客戶端對應的處理消息有(ATT_ACCESS_RSP, GATT_NOTIFICATION_IND, GATT_INDICATION_IND)。
1.1.2 GATT數據庫
作為GATT的服務器,通常需要存儲比較多的數據來支撐多個不同的GATT服務,因此在GATT服務器端,建立一個數據庫是必要的。在CSR的BlueCore和CSRμEnergy軟件開發工具包(SDK)中使用了一種特殊的數據庫目標語言。該數據庫通過GATT數據庫生成器(CSR提供的gattdbgen.exe)自動生成。這就允許應用程序開發者以一種簡單易讀,便於維護的方式來創建一個數據庫,避免采用諸如SDP這種用復雜的二進制形式表示方式。
數據庫的生成
GATT數據庫生成器輸入一個用jeason語言描述的GATT服務文件foobar.db,輸出一個foobar.c和foobar.h文件,這兩個文件可以作為ADK工程的一部分進行編譯和鏈接。可見數據庫的生產是在整個工程編譯前,准確的說是在預編譯階段就已經完成了。顯然這樣生產的數據是一個准常量的數據庫,不能往里面新增記錄,即不能動態的添加GATT服務,所有的服務必須在編譯前就已經確定好。
例如,在ADK的sink例程中,sink_gatt_db.db作為數據庫生成器的輸入文件,輸出sink_gatt_db.c和sink_gatt_db.h。
其中xxx_db.c文件有const uint16 gattDatabase[] = {}存儲數據庫記錄。並提供兩個接口工外部操作數據庫,這兩個接口分別為:
uint16 *GattGetDatabase(uint16 *len); /*獲取數據庫存儲區首地址*/
uint16 GattGetDatabaseSize(void); /*獲取數據庫大小*/
在xxx_db.h文件中,除了GattGetDatabase()和GattGetDatabaseSize()接口的聲明外,還有數據庫中所有ATT記錄的句柄(UUID)的宏定義,通常以HANDLE_GAIA_XXX的形式定義。通過該宏,可以快速索引某條ATT記錄。
BLE在初始化GATT Manager模塊時,對GATT database進行了初始化:
initialiseGattWithServers()->
GattManagerRegisterConstDB(&gattDatabase[0], GattGetDatabaseSize());
例如某個gaia_db.db內容為:
primary_service {
uuid : 0x01,
name : "GAIA_SERVICE",
characteristic {
uuid : 0x02,
name : "GAIA_COMMAND_ENDPOINT",
flags : [ FLAG_IRQ, FLAG_DYNLEN, FLAG_ENCR_W ],
properties : [ write ],
value : 0x0
}
},
則其生成的gaia_db.c文件如下:
/* Static GATT database */
const uint16 gattDatabase[] = {
/* 0001: Primary Service 0001 */
0x0002, 0x0100,
/* 0002: Characteristic Declaration 0002 */
0x3005, 0x0803, 0x0002, 0x0000,
/* 0003: . */
0xdd01, 0x0000,
};
uint16 *GattGetDatabase(uint16 *len)
{
uint16 *rc = PanicUnlessMalloc(sizeof(gattDatabase));
*len = sizeof(gattDatabase);
memmove(rc, gattDatabase, sizeof(gattDatabase));
return rc;
}
uint16 GattGetDatabaseSize(void)
{
return sizeof(gattDatabase);
}
其生成的gaia_db.h文件如下:
#define HANDLE_GAIA_SERVICE (0x0001)
#define HANDLE_GAIA_SERVICE_END (0xffff)
#define HANDLE_GAIA_COMMAND_ENDPOINT (0x0003)
uint16 *GattGetDatabase(uint16 *len);
uint16 GattGetDatabaseSize(void);
數據庫維護
GATT數據庫的數據單元是ATT記錄,通過某條ATT記錄的句柄(handler)可以快速和唯一地訪問該條記錄,例如,如果希望訪問電池服務的電量特征值,該條特征值定義封裝在一條句柄為0x0003的ATT記錄中,通過句柄0x0003找到該條ATT記錄,ATT.value即對應着電池電量值。