低功耗蓝牙ble开发(二)——bluez5源码分析

3、bluetoothctl工具代码分析

Bluetoothctl工具的入口程序client/main.c中的main函数,现在跳到main函数开始分析

(1)client/main.c/main函数分析
c 复制代码
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)client/client.c/g_dbus_client_new_full函数分析
c 复制代码
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)client/client.c/message_filter函数分析
c 复制代码
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)client/client.c/get_managed_objects函数分析
c 复制代码
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)gdbus/object.c/get_objects函数分析
c 复制代码
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)gdbus/object.c/append_property函数分析
c 复制代码
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)gdbus/client.c/get_managed_objects_reply函数分析
c 复制代码
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)gdbus/client.c/parse_properties函数分析
c 复制代码
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)client/main.c/proxy_added函数分析
c 复制代码
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);
}

4、bluetoothctl工具部分命令实现分析

(1)client/main.c/ cmd_list函数分析
c 复制代码
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)client/main.c/ cmd_scan函数分析
c 复制代码
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)src/adapter.c/start_discovery函数分析

static DBusMessage *start_discovery(DBusConnection *conn,

                                           DBusMessage *msg, void *user_data)

{

c 复制代码
static DBusMessage *start_discovery(DBusConnection *conn,
                                            DBusMessage *msg, void *user_data)
{    
	......
     //触发开始扫描设备,设置扫描超时时间,该函数里调用trigger_start_discovery函数,当超时时间到了会调用start_discovery_timeout函数
     err = update_discovery_filter(adapter);
     ......
}
(4)src/adapter.c/start_discovery_timeout函数分析
c 复制代码
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)src/adapter.c/ device_found_callback函数分析
c 复制代码
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)src/adapter.c/ update_found_devices函数分析
c 复制代码
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)src/adapter.c/ adapter_create_device函数分析
c 复制代码
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)src/device.c/ device_new函数分析
c 复制代码
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函数分析

c 复制代码
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);
     ......
}
相关推荐
奋斗的小花生1 小时前
c++ 多态性
开发语言·c++
闲晨1 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
UestcXiye2 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
霁月风4 小时前
设计模式——适配器模式
c++·适配器模式
vfbox33984 小时前
C-DTL698电表 转 IEC61850 协议项目案例
物联网·iec61850·vfbox·协议转换工业网关·c-dtl698
jrrz08284 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i4 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1074 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客4 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
爱吃喵的鲤鱼5 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++