bluez5.50源碼分析

    網上查找資料,沒有找到詳細的bluez協議棧源碼分析的文檔,只能自己硬着頭皮看bluez的源碼,這裏記下我自己對源碼的理解,供後來者參考。

1、bluez5源碼下載地址

   https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/

2、bluez5源代碼組織架構

(注:該部分是網上抄來的)

  BlueZ5源代碼中有很多目錄和文件:

   android/ - 用於替代android中bluedroid的android版本bluez源碼。

   attrib/ - 包含gatttool 源碼以及與gatt attribute相關的代碼,gatttool程序入口爲gatttool.c。   

   btio/ - 通過的標準socket接口與BlueZ5 kernel模塊通信?。

   client/ - bluetoothctl源碼,程序入口爲main.c。

   doc/ - BlueZ5 API文檔。

   emulator/ - 與bluetooth虛擬controller工具相關的代碼?。

   gdbus/ - BlueZ5自帶的內部gdbus庫源碼。

   gobex/ - Blue5自帶的內部gobex庫源碼。

   lib/ - libbluetooth.so 源碼,提供BlueZ4 API,用來支持某些第三方應用。

   monitor/ - btmon源碼, 程序入口爲main.c。

   obexd/ - obexd源碼,程序入口爲src/main.c。

   peripheral/ - 與藍牙ble的GATT相關的代碼?。

   plugins/ - BlueZ5插件源碼(neard,autopair等插件)。

   profiles/ - BlueZ5藍牙上層協議(a2dp,hid等)源碼。

   src/ - bluetoothd源碼,程序入口爲main.c。

   test/ - Bluez5測試腳本。

   tools/ - Bluez5測試工具集源碼。

   unit/ - PTS測試相關的一些代碼?。

   README / INSTALL - 配置,編譯,安裝Bluez5的說明。

   Makefile.obexd - 定義obexd編譯規則,此文件被include於Makefile.am中。

   Makefile.plugins - 定義BlueZ5的plugins(neard,autopair等)的編譯規則, 此文件被include於Makefile.am中。

   Makefile.tools - 定義BlueZ5測試工具集的編譯規則,此文件被include於 Makefile.am中。

   Makefile.am - 定義了Bluez5的編譯規則。用於automake工具,生成 Makefile.in文件。

   Makefile.in - 用於configure腳本,生成最終的Makefile文件。

   configure.ac - 用於autoconf工具,生成configure腳本。

   configure - 配置編譯選項,生成最終的Makefile文件,以及config.h文件

3、bluez核心代碼分析

  bluez核心代碼在src目錄下,入口函數是main.c,bluez5編譯後會生成bluetoothd可執行文件,該可執行文件在linux系統啓動時自動加載,加載配置文件放在/etc/init/bluetooth.conf,下面進入main.c函數開始分析(注:bluez源碼很多地方是異步操作的,看源碼時經常需要根據關鍵詞到處搜索註冊的回調函數在哪裏被觸發)

(1)bluez5.50/src/main.c/main函數分析

int main(int argc, char *argv[])

{

         ……

         //glib庫函數創建一個死循環線程用於處理事件

         event_loop = g_main_loop_new(NULL, FALSE);

         //註冊信號接收處理ctrl+c等中斷信號

         signal = setup_signalfd();

         ……

         //gbus總線註冊bluez服務端,該函數很重要,後面詳細分析

         if (connect_dbus() < 0) {

                   error("Unable to get on D-Bus");

                   exit(1);

         }

         ……

         //bluez控制器初始化,該函數很重要,後面詳細分析

         if (adapter_init() < 0) {

                   error("Adapter handling initialization failed");

                   exit(1);

         }

         //bluez設備及應用層協議註冊,具體用處我沒有看

         btd_device_init();

         btd_agent_init();

         btd_profile_init();

         ……

         //開始死循環並處理事件

         g_main_loop_run(event_loop);

         ……

}

(2)bluez5.50/src/main.c/connect_dbus函數分析

static int connect_dbus(void)

{

         ……

         //dbus是linux多進程通信的一種方式,bluez在dbus守護進程上註冊了一個連接,用於接收其它bluez用戶進程的消息,處理後並返回應答消息。具體的dbus實現原理需要同志們自己找資料理解,因爲bluez源碼依賴dbus編程。

         //清除dbus錯誤標誌

         dbus_error_init(&err);

         //向總線型dbus守護進程申請名爲BLUEZ_NAME ("org.bluez")的連接,其它進程可以使用dbus的dbus_message_new_method_call函數發消息給這個名爲BLUEZ_NAME的連接。該函數很重要,後面詳細分析

         conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);

         ……

         //申請的連接的指針賦值給全局的連接指針

         set_dbus_connection(conn);

         //向dbus總線註冊監聽連接斷開的信號,當連接斷開時守護進程會發信號給這個已經註冊的監聽信號,然後回調disconnected_dbus函數處理斷開連接

         g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL);

         //向dbus總線註冊bluez的管理對象,該對象的對象路徑是”/”,用戶可以編程調用該接口下面的方法獲取bluez創建的所有對象以及每個對象下面的所有方法,該函數很重要,後面詳細分析

         g_dbus_attach_object_manager(conn);

         ……

}

(3)bluez5.50/gdbus/mainloop.c/g_dbus_setup_bus函數分析

DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,

                                                                 DBusError *error)

{

         ……

         //向dbus總線申請一個連接

         conn = dbus_bus_get(type, error);

         ……

         //向dbus總線申請給該連接起一個名字,其它進程就可以向改名字的連接發送dbus消息了。另外註冊了一些dbus的觀察函數,具體用處不是特別清楚

         if (setup_bus(conn, name, error) == FALSE) {

         ……

}

(4)bluez5.50/gdbus/object.c/g_dbus_attach_object_manager函數分析

gboolean g_dbus_attach_object_manager(DBusConnection *connection)

{

         ……

         //向dbus總線的”/”路徑的對象註冊一個包含該對象的所有數據存放地址的指針,便於程序在其它地方使用dbus_connection_get_object_path_data函數通過”/”路徑獲取指針。該函數很重要,後面詳細分析

         data = object_path_ref(connection, "/");

         ……

         //該對象添加名爲DBUS_INTERFACE_OBJECT_MANAGER宏定義的接口,接口註冊了一個方法和一個信號,當其它進程使用dbus_message_new_method_call函數發消息給該對象的該接口方法時,generic_table裏的generic_message會被調用,然後manager_methods會被調用,具體的調用流程後面會詳細介紹

         add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,

                                               manager_methods, manager_signals,

                                               NULL, data, NULL);

     ……

}

(5)bluez5.50/gdbus/object.c/object_path_ref函數分析

static struct generic_data *object_path_ref(DBusConnection *connection,

                                                                 const char *path)

{

         ……

         //判斷”/”路徑的對象有沒有已經申請存放該對象的存儲空間,如果已經申請了,返回的data指針不爲空

         if (dbus_connection_get_object_path_data(connection, path,

                                                        (void *) &data) == TRUE) {

         ……

         //向dbus總線註冊該對象,當其它進程使用dbus_message_new_method_call函數發消息給該對象時,generic_table裏的generic_message會被調用

         if (!dbus_connection_register_object_path(connection, path,

                                                        &generic_table, data)) {

         ……

         //把對象添加到對象鏈表data->objects裏,便於程序在其它地方查找所有對象

         invalidate_parent_data(connection, path);

         ……

}

(6)bluez5.50/gdbus/object.c/generic_message函數分析

static DBusHandlerResult generic_message(DBusConnection *connection,

                                               DBusMessage *message, void *user_data)

{

         ……

         Dbus接收從其他進程發來的消息,過濾出消息的接口名字

         interface = dbus_message_get_interface(message);

         //根據接口名字查找之前已經註冊的對應接口

         iface = find_interface(data->interfaces, interface);

         ……

         //檢查完參數後開始處理消息

         return process_message(connection, message, method,

                                                                 iface->user_data);

}

(7)bluez5.50/gdbus/object.c/process_message函數分析

static DBusHandlerResult process_message(DBusConnection *connection,

                            DBusMessage *message, const GDBusMethodTable *method,

                                                                 void *iface_user_data)

{

         ……

         //回調已經註冊的接口對應的方法,具體使用例子在後面詳細介紹

         reply = method->function(connection, message, iface_user_data);

         ……

         //發送應答消息

         g_dbus_send_message(connection, reply);

         ……

}

(8)bluez5.50/src/adapter.c/adapter_init函數分析

int adapter_init(void)

{

         ……

         //構造訪問hci接口的結構體,該函數很重要,後面詳細分析

         mgmt_master = mgmt_new_default();

……

//hci接口向內核註冊的藍牙控制器發送命令,收到對應返回命令時調用read_version_complete回調函數進行處理。

if (mgmt_send(mgmt_master, MGMT_OP_READ_VERSION,

                                     MGMT_INDEX_NONE, 0, NULL,

                                     read_version_complete, NULL, NULL) > 0)

}

(9)bluez5.50/src/shared/mgmt.c/mgmt_new_default函數分析

struct mgmt *mgmt_new_default(void)

{

         ……

         //使用socket打開hci驅動接口,和open訪問驅動設備類似。明白這個需要自己去了解linux藍牙驅動的相關知識,這裏不介紹

         fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,

                                                                           BTPROTO_HCI);

         ……

         //構造發送hci命令的結構體。該函數很重要,後面詳細分析

         mgmt = mgmt_new(fd);

}

(10)bluez5.50/src/shared/mgmt.c/mgmt_new函數分析

struct mgmt *mgmt_new(int fd)

{

         ……

         //使用glib庫的io註冊功能把fd描述符添加監控列表裏,詳情看bluez5.50/src/shared/io-glib.c/io_new函數的實現

         mgmt->io = io_new(fd);

         ……

         //使用glib庫的io功能添加監控fd讀變化的功能,當hci接口有應答數據時,fd讀操作狀態改變,can_read_data回調函數被調用。詳情看bluez5.50/src/shared/io-glib.c/ io_set_read_handler函數的實現

         if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {

}

(11)bluez5.50/src/shared/mgmt.c/can_read_data函數分析

static bool can_read_data(struct io *io, void *user_data)

{

         ……

         //根據hci接口返回的應答數據的事件類型進行不同處理

         switch (event) {

         ……

         //當返回的事件類型是命令完成時執行如下函數

         request_complete(mgmt, cc->status, opcode, index, length - 3,

                                                        mgmt->buf + MGMT_HDR_SIZE + 3);

         ……

         //當返回的事件類型是命令狀態時執行如下函數

         request_complete(mgmt, cs->status, opcode, index, 0, NULL);

         ……

         //其它事件時執行如下的通知函數,作用是通知已經註冊到mgmt->notify_list鏈表的通知操作

         process_notify(mgmt, event, index, length,

                                                        mgmt->buf + MGMT_HDR_SIZE);

}

(12)bluez5.50/src/shared/mgmt.c/request_complete函數分析

static void request_complete(struct mgmt *mgmt, uint8_t status,

                                               uint16_t opcode, uint16_t index,

                                               uint16_t length, const void *param)

{

         ……

         //執行在hci接口發送命令時已經註冊的命令完成回調函數,例如read_version_complete就是一個註冊的回調函數

         request->callback(status, length, param,

                                                                 request->user_data);

         ……

         //使用glib的io功能把fd的寫功能添加進去,當fd能寫時,把需要發送的hci命令發送給驅動

         wakeup_writer(mgmt);

}

(13)bluez5.50/src/adapter.c/read_version_complete函數分析

static void read_version_complete(uint8_t status, uint16_t length,

                                               const void *param, void *user_data)

{

         ……

         //註冊藍牙控制器個數增加時的通知信號,當驅動發現藍牙控制器被插入時,發送事件通知命令,process_notify函數會被執行,在該函數中會調用已經註冊的index_added函數申請新的適配器存儲空間

         mgmt_register(mgmt_master, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,

                                                        index_added, NULL, NULL);

         ……

         //發送hci命令獲取藍牙適配器(藍牙控制器)的個數,收到響應命令時回調read_index_list_complete函數

         if (mgmt_send(mgmt_master, MGMT_OP_READ_INDEX_LIST,

                                     MGMT_INDEX_NONE, 0, NULL,

                                     read_index_list_complete, NULL, NULL) > 0)

         ……

}

(14)bluez5.50/src/adapter.c/read_index_list_complete函數分析

static void read_index_list_complete(uint8_t status, uint16_t length,

                                               const void *param, void *user_data)

{

         ……

         //獲取適配器的個數,然後根據index值註冊所有的適配器

         index_added(index, 0, NULL, NULL);

         ……

}

(15)bluez5.50/src/adapter.c/index_added函數分析

static void index_added(uint16_t index, uint16_t length, const void *param,

                                                                 void *user_data)

{

         ……

         //查找適配器鏈表adapter_list裏有沒有編號爲index的適配器存在

         adapter = btd_adapter_lookup(index);

         ……

         //根據index創建新的適配器

         adapter = btd_adapter_new(index);

         ……

         //把新創建的適配器指針添加到適配器鏈表裏,方便其它地方查詢

         adapter_list = g_list_append(adapter_list, adapter);

         ……

         //發送hci接口命令給驅動層,獲取index對應的適配器的詳細信息,讀取到結果後會回調read_info_complete函數

         if (mgmt_send(mgmt_master, MGMT_OP_READ_INFO, index, 0, NULL,

                                               read_info_complete, adapter, NULL) > 0)

         ……

}

(16)bluez5.50/src/adapter.c/read_info_complete函數分析

static void read_info_complete(uint8_t status, uint16_t length,

                                               const void *param, void *user_data)

{

         ……

         //設置適配器的bdaddr等,然後爲適配器註冊dbus對象,對象路徑爲"/org/bluez/hci%d",對象接口名爲"org.bluez.Adapter1",接口方法爲adapter_methods

         err = adapter_register(adapter);

         ……

         //註冊設備發現函數,當驅動層掃描到新的藍牙設備時,發送通知給應用層,應用層會觸發can_read_data函數,然後調用到process_notify函數,然後調用到mgmt->notify_list鏈表,而device_found_callback函數是添加在mgmt->notify_list鏈表上的,所以會被執行

         mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FOUND,

                                                        adapter->dev_id,

                                                        device_found_callback,

                                                        adapter, NULL);

         ……

}

(17)bluez5.50/src/adapter.c/adapter_register函數分析

static int adapter_register(struct btd_adapter *adapter)

{

         ……

         //指定適配器註冊到dbus總線上的對象的路徑

         adapter->path = g_strdup_printf("/org/bluez/hci%d", adapter->dev_id);

         //爲適配器註冊dbus對象,對象路徑爲"/org/bluez/hci%d",對象接口名爲"org.bluez.Adapter1",接口方法爲adapter_methods,接口屬性爲adapter_properties

         if (!g_dbus_register_interface(dbus_conn,

                                               adapter->path, ADAPTER_INTERFACE,

                                               adapter_methods, NULL,

                                               adapter_properties, adapter,

                                               adapter_free)) {

         ……

         //把當前適配器添加到adapters鏈表

         adapters = g_slist_append(adapters, adapter);

         ……

}

4、bluetoothctl工具代碼分析

Bluetoothctl工具的入口程序是bluez5.50/client/main.c中的main函數,現在跳到main函數開始分析

(1)bluez5.50/client/main.c/main函數分析

int main(int argc, char *argv[])

{

         ……

         //命令行輸入初始化,該函數裏面調用rl_init函數,這是linux的命令行功能,裏面的rl_handler函數在鍵盤輸入字符是會被調用,從而解析出命令

         bt_shell_init(argc, argv, &opt);

         ……

         //向dbus系統總線註冊一個連接

         dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);

         //綁定管理對象,這個好像沒什麼用

         g_dbus_attach_object_manager(dbus_conn);

         //申請一個client結構體存儲空間,並註冊接收其它dbus總線發來的信號的回調函數,g_dbus_client_new裏面調用了g_dbus_client_new_full函數,後面直接分析該函數

         client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");

         ……

         //註冊代理,bluez核心代碼裏向dbus系統總線註冊了多個對象,例如”/”、” /org/bluez/hci%d”對象等,每個對象裏有多個接口,例如” /org/bluez/hci%d”對象裏提供了名字爲” org.bluez.Adapter1”的接口,該接口下面有"StartDiscovery"方法等。每個接口放在一個代理存儲空間裏,代理裏存放了接口的名字,接口有哪些方法,有哪些屬性,接口的對象路徑地址等,這樣便於程序通過代理鏈表查找。該函數裏面調用了get_managed_objects函數,後面直接分析該函數

         g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,

                                                                 property_changed, NULL);

         ……

}

(2)bluez5.50/client/client.c/g_dbus_client_new_full函數分析

GDBusClient *g_dbus_client_new_full(DBusConnection *connection,

                                                                 const char *service,

                                                                 const char *path,

                                                                 const char *root_path)

{

         ……

         //dbus總線的註冊方法,當其它進程廣播dbus信號時,message_filter函數會被調用

if (dbus_connection_add_filter(connection, message_filter,

                                                        client, NULL) == FALSE) {

         ……

}

(3)bluez5.50/client/client.c/message_filter函數分析

static DBusHandlerResult message_filter(DBusConnection *connection,

                                               DBusMessage *message, void *user_data)

{

         ……

         //用g_dbus_client_set_signal_watch函數註冊的信號的回調函數會被調用

         client->signal_func(connection, message, client->signal_data);

}

(4)bluez5.50/client/client.c/get_managed_objects函數分析

static void get_managed_objects(GDBusClient *client)

{

         ……

         //使用dbus庫函數向bluez核心代碼發送獲取所有被管理的對象。調用時傳入的參數是bluez核心進程dbus連接名稱”"org.bluez",對象名稱是”/”,接口方法是DBUS_INTERFACE_OBJECT_MANAGER的宏定義名稱,接口方法的名字是"GetManagedObjects"。我們在bluez5.50/src/mian.c/main函數中調用connect_dbus->g_dbus_attach_object_manager->add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,manager_methods, manager_signals,NULL, data, NULL);在這個函數中註冊了manager_methods方法。當bluez核心進程收到當前進程的GetManagedObjects方法時,manager_methods方法列表中的get_objects函數會被調用

         msg = dbus_message_new_method_call(client->service_name,

                                                        client->root_path,

                                                        DBUS_INTERFACE_OBJECT_MANAGER,

                                                        "GetManagedObjects");

         ……

         //當收到bluez核心進程的應答消息是改註冊函數裏的get_managed_objects_reply方法會被調用

         dbus_pending_call_set_notify(client->get_objects_call,

                                                        get_managed_objects_reply,

                                                        client, NULL);

}

(5)bluez5.50/gdbus/object.c/get_objects函數分析

static DBusMessage *get_objects(DBusConnection *connection,

                                     DBusMessage *message, void *user_data)

{

         ……

         //搜索data->objects鏈表上所有的dbus對象,對每個對象調用append_object回調函數處理。例如創建的根目錄對象”/”和藍牙適配器對象"/org/bluez/hci%d"都添加在data->objects鏈表上。data->objects鏈表中的數據是註冊dbus對象時調用invalidate_parent_data函數添加到鏈表上的。append_object函數裏面調用append_interfaces函數,接着調用append_interface,接着調用append_properties,接着調用append_property

         g_slist_foreach(data->objects, append_object, &array);

}

(6)bluez5.50/gdbus/object.c/append_property函數分析

static void append_property(struct interface_data *iface,

                            const GDBusPropertyTable *p, DBusMessageIter *dict)

{

         ……

         //適配器註冊dbus時添加了adapter_properties,所以p->get會逐個調用adapter_properties列表裏的property_get_address函數,一直調用到property_get_modalias函數,也就是適配器的屬性信息都將被讀取並返回給用戶。當掃描到藍牙設備後,藍牙設備註冊到dbus裏的device_properties列表下的get函數也會被調用

         p->get(p, &value, iface->user_data);

         ……

}

(7)bluez5.50/gdbus/client.c/get_managed_objects_reply函數分析

static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)

{

         ……

         //解析所有的管理對象及對象下所有的接口,給每個對象申請一個代理。該函數裏調用parse_managed_objects->parse_interfaces->parse_properties

         parse_managed_objects(client, reply);

         ……

         //獲取每個接口下的所有屬性

         refresh_properties(client->proxy_list);

         ……

}

(8)bluez5.50/gdbus/client.c/parse_properties函數分析

static void parse_properties(GDBusClient *client, const char *path,

                                     const char *interface, DBusMessageIter *iter)

{

         ……

         //查看proxy_list鏈表裏有沒有名字爲interface的接口代理,初始化時默認是沒有的

         proxy = g_dbus_proxy_lookup(client->proxy_list, NULL,

                                                        path, interface);

         ……

         //申請接口代理,並把代理添加到proxy_list鏈表上

         proxy = proxy_new(client, path, interface);

         ……

         //獲取每個接口的屬性信息,並把第一個適配器接口設置爲默認適配器,第一個掃描到的設備作爲默認設備

         update_properties(proxy, iter, FALSE);

         //根據接口類型把接口代理進行分類。例如接口是適配器接口,就把接口代理添加到適配器鏈表,設備接口就把接口代理添加到掃描它的適配器鏈表下的設備鏈表上。該函數裏調用client->proxy_added(proxy, client->user_data),又client->proxy_added=proxy_added

         proxy_added(client, proxy);

}

(9)bluez5.50/client/main.c/proxy_added函數分析

static void proxy_added(GDBusProxy *proxy, void *user_data)

{

         ……

         //獲取代理的接口名

         interface = g_dbus_proxy_get_interface(proxy);

         ……

         //接口名字是"org.bluez.Device1"時,把該代理添加到掃描它的適配器的設備鏈表上

         device_added(proxy);

         ……

         //接口名字是"org.bluez.Adapter1"時,把代理添加到適配器鏈表

         adapter_added(proxy);

}

5、bluetoothctl工具部分命令實現分析

(1)bluez5.50/client/main.c/ cmd_list函數分析

static void cmd_list(int argc, char *argv[])

{

         ……

         // 打印所有的適配器地址,ctrl_list鏈表存放所有適配器代理,在adapter_new函數中把適配器加入ctrl_list鏈表

         for (list = g_list_first(ctrl_list); list; list = g_list_next(list))

}

(2)bluez5.50/client/main.c/ cmd_scan函數分析

static void cmd_scan(int argc, char *argv[])

{

         ……

         //發送設置掃描過濾消息給bluez核心進程,主要設置掃描到有效設備的信號強度閾值等

         set_discovery_filter();

         ……

         //發送啓動掃描設備消息給bluez核心進程,bluez核心進程會調用bluez5.50/src/adapter.c/start_discovery函數,當調用收到響應消息後start_discovery_reply函數會被調用

         if (g_dbus_proxy_method_call(default_ctrl->proxy, method,

                                     NULL, start_discovery_reply,

                                     GUINT_TO_POINTER(enable), NULL) == FALSE) {

}

(3)bluez5.50/src/adapter.c/start_discovery函數分析

static DBusMessage *start_discovery(DBusConnection *conn,

                                               DBusMessage *msg, void *user_data)

{

         ……

         //觸發開始掃描設備,設置掃描超時時間,該函數裏調用trigger_start_discovery函數,當超時時間到了會調用start_discovery_timeout函數

         err = update_discovery_filter(adapter);

         ……

}

(4)bluez5.50/src/adapter.c/start_discovery_timeout函數分析

static gboolean start_discovery_timeout(gpointer user_data)

{

         ……

         //調用hci接口發送啓動掃描命令給指定藍牙適配器,當適配器啓動掃描設備時,會通過hci接口發送響應命令,這時bluez5.50/src/mgmt.c/can_read_data被調用,裏面調用request_complete,裏面調用request->callback,而start_discovery_complete函數就註冊在request->callback裏。start_discovery_complete-> g_dbus_emit_property_changed-> g_dbus_emit_property_changed_full-> add_pending。該函數裏註冊了 process_changes函數,在主循環空閒時,process_changes函數被調用。process_changes函數裏調用emit_interfaces_added函數,裏面調用dbus_message_new_signal(root->path,

                                               DBUS_INTERFACE_OBJECT_MANAGER,

                                               "InterfacesAdded");這個函數把增加的接口通過dbus信號廣播給bluetoothctl進程。

         mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,

                                     adapter->dev_id, sizeof(cp), &cp,

                                     start_discovery_complete, adapter, NULL);

}

(5)bluez5.50/src/adapter.c/ device_found_callback函數分析

static void device_found_callback(uint16_t index, uint16_t length,

                                               const void *param, void *user_data)

{

         ……

         //啓動掃描設備後,當適配器掃描到設備後,驅動程序會通過hci接口發設備信息響應包給bluez核心進程。Bluez核心進程裏can_read_data會被觸發,最後調用到device_found_callback函數。該函數目的是爲掃描到的設備在dbus總線上添加一個設備對象,並把該設備所有的信息綁定到給設備對象上,方便其它進程通過dbus消息獲取設備信息

         update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,

                                               ev->rssi, confirm_name, legacy,

                                               flags & MGMT_DEV_FOUND_NOT_CONNECTABLE,

                                               eir, eir_len);

}

(6)bluez5.50/src/adapter.c/ update_found_devices函數分析

static void update_found_devices(struct btd_adapter *adapter,

                                               const bdaddr_t *bdaddr,

                                               uint8_t bdaddr_type, int8_t rssi,

                                               bool confirm, bool legacy,

                                               bool not_connectable,

                                               const uint8_t *data, uint8_t data_len)

{

         ……

         //查看當前適配器的設備鏈表adapter->devices上有沒有設備地址是bdaddr的設備存在,默認是不存在該設備的

         dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);

         ……

         //把新設備作爲一個新的dbus對象添加到dbus總線上

         dev = adapter_create_device(adapter, bdaddr, bdaddr_type);

         ……

}

(7)bluez5.50/src/adapter.c/ adapter_create_device函數分析

static struct btd_device *adapter_create_device(struct btd_adapter *adapter,

                                                        const bdaddr_t *bdaddr,

                                                        uint8_t bdaddr_type)

{

         ……

         //創建新的設備dbus對象,device_create-> device_new

         device = device_create(adapter, bdaddr, bdaddr_type);

         ……

         //把設備添加到適配器的設備鏈表adapter->devices上

         adapter->devices = g_slist_append(adapter->devices, device);

}

(8)bluez5.50/src/device.c/ device_new函數分析

static struct btd_device *device_new(struct btd_adapter *adapter,

                                     const char *address)

{

         ……

         //給設備賦值一個dbus對象的路徑

         device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);

         ……

         //給設備註冊dbus對象,對象路徑是device->path,對象下面註冊一個接口名爲DEVICE_INTERFACE,接口提供了方法集合device_methods,屬性集合device_properties

         if (g_dbus_register_interface(dbus_conn,

                                               device->path, DEVICE_INTERFACE,

                                               device_methods, NULL,

                                               device_properties, device,

                                               device_free) == FALSE)

         ……

}

(9)bluez5.50/gdbus/client.c/ g_dbus_client_new_full函數分析

GDBusClient *g_dbus_client_new_full(DBusConnection *connection,

                                                                 const char *service,

                                                                 const char *path,

                                                                 const char *root_path)

{

         ……

         //在main初始化過程中在這給函數中註冊了一個接口增加回調函數,當bluez核心進程發送"InterfacesAdded"該信號時,bluetoothctl進程會觸發調用interfaces_added函數。interfaces_added-> parse_interfaces-> parse_properties-> proxy_added,最後把新增的接口根據類型添加到適配器列表、設備列表或其他列表

         client->added_watch = g_dbus_add_signal_watch(connection, service,

                                                        client->root_path,

                                                        DBUS_INTERFACE_OBJECT_MANAGER,

                                                        "InterfacesAdded",

                                                        interfaces_added,

                                                        client, NULL);

         ……

}

到這裏,終於解釋完了cmd_scan

(10)bluez5.50/client/main.c/ cmd_connect函數分析

static void cmd_connect(int argc, char *argv[])

{

         ……

         //發送連接設備消息給bluez核心進程,bluez核心進程收到消息後會調用bluez5.50/src/device.c/dev_connect函數。dev_connect->device_connect_le->bt_io_connect->l2cap_connect->connect最終發起設備連接

         if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,

                                                                 proxy, NULL) == FALSE) {

         ……

}

到此結束,其它的還沒看明白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章