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

3、bluetoothctl工具代码分析

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

(1)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)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)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)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)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_propertyg_slist_foreach(data->objects, append_object, &array);
}
(6)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)gdbus/client.c/get_managed_objects_reply函数分析
static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
{     ……//解析所有的管理对象及对象下所有的接口,给每个对象申请一个代理。该函数里调用parse_managed_objects->parse_interfaces->parse_propertiesparse_managed_objects(client, reply);……//获取每个接口下的所有属性refresh_properties(client->proxy_list);……
}
(8)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_addedproxy_added(client, proxy);
}
(9)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);
}

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

(1)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)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)src/adapter.c/start_discovery函数分析

static DBusMessage *start_discovery(DBusConnection *conn,

                                           DBusMessage *msg, void *user_data)

{

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函数分析
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函数分析
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函数分析
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函数分析
static struct btd_device *adapter_create_device(struct btd_adapter *adapter,const bdaddr_t *bdaddr,   uint8_t bdaddr_type)
{     ……//创建新的设备dbus对象,device_create-> device_newdevice = device_create(adapter, bdaddr, bdaddr_type);……//把设备添加到适配器的设备链表adapter->devices上adapter->devices = g_slist_append(adapter->devices, device);
}
(8)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_propertiesif (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);……
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/852626.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析 Spring Cloud Sentinel:分布式系统流量控制与熔断降级的全面指南

📢📢📢 深入解析 Spring Cloud Sentinel:分布式系统流量控制与熔断降级的全面指南 Spring Cloud Sentinel 是阿里巴巴开源的一款强大的分布式系统流量防卫组件,专为微服务架构设计,提供流量控制、熔断降级…

watcher学习小结

架构 主要是watcher-api,watcher-applier,watcher-decision-engine watcher-applier watcher-decision-engine 将DecisionEngineManager和DecisionEngineSchedulingService封装到oslo_service,然后调service的launch_service,实…

NetSuite ERP项目中非批次物料—批次物料数据转换流程

最近在刚结束的项目上也再次碰到了非批次物料转换为批次物料的操作,因此也想把我们在处理数据流程中的心得写出来,以便复盘与总结,也分享给各位。 整体的步骤我们可分为准备工作,调整工作以及检查工作: 准备工作 主…

抖店退款退货率太高,平台也不帮助商家,快做不下去了怎么办?

我是王路飞。 现在很多商家对抖店的评价是:比拼多多还狠,动不动就扣保证金,退款率太高,而平台一边倒站买家,要是再遇到个别发疯的买家,商家真的很无助。 其实关于抖店退款退货率高、平台也不站在商家这一…

【运维项目经历|031】GitLab自动化运维管理平台项目

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 🏅阿里云ACE认证高级工程师 🏅阿里云开发者社区专家博主 💊交流社区:CSDN云计算交流社区欢迎您的加入! 目…

【Flutter】路由组件的应用 (学习记录)

前言 在 Flutter 中,路由用于管理应用程序中不同页面之间的导航和跳转。Flutter 提供了多种方式来实现路由管理,包括基本的静态路由、动态路由、命名路由以及使用第三方库(如 GetX、Provider 等)来管理路由。 一、 静态路由&#…

编译结果处理的shell脚本

#!/bin/bash WEB"web" DIST"dist" RED\033[0:31m GREEN\033[0;32m NC\033[0m #生产打包传参 BUILD"b" if [ -e ${WEB} ];then#删历史文件rm -r ${WEB}rm ${WEB}.zip fi #编辑文件 npm run build #检查构建是否成功 if[ -e ${DIST} ];then#改名mv…

分布式事务的八种方案解析(1)

针对不同的分布式场景业界常见的解决方案有2PC、TCC、可靠消息最终一致性、最大努力通知等方案,以下总结8 种常见的解决方案,帮助大家在实际的分布式系统中更好地运用事务。 1.2PC 二阶段提交协议(Two-phase commit protocol)&…

好用的视频压缩软件

在当今数字化时代,视频已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐,视频都扮演着重要的角色。视频的存储空间也越来越大,这给我们的设备存储带来了不小的挑战。因此,学习如何将视频压缩小点成为了一项实用的…

Qt QMake指南(如何写pro文件)

QMake相关介绍 QMake是一个通过编译Pro文件自动生成Makefile文件的工具。 扩展知识 这里是关于make、makefile、cmake、CMakeLists.txt、qmake等工具和文件之间关系的详细解释: 起始:当我们开始学习基础的helloworld项目时,一般使用gcc命令…

微信营销在使用时应该注意什么?

(一)注重个人品牌的打造 微信好友的信任是开展个人微信营销成功的基础,因此,平时应利用朋友圈和微信群发布一些有助于提升个人形象的信息。 (二)发送营销信息不可过滥 如果一个人的朋友圈内容充斥营销信息&…

叁[3],VM二次开发异常处理

1,开发环境 VS2022/WPF/.NetFramework4.8 VM4.2 2,"模块状态0,错误码10100005,错误信息:模块与平台不匹配" 现象描述: 1,WPF/NetFramework项目中打开方案,对工具做模板&#xff0c…

开源可视化表单可以用在哪些行业中?

很多客户朋友会询问我们,什么样的行业可以使用低代码技术平台及开源可视化表单?其实,随着社会的进步和发展,很多中小型企业都希望通过低代码技术平台能够让企业实现提质增效的目的,也想借助它的优势特点进入流程化办公…

MySql通过 Procedure 循环删除数据

一、问题描述 在日常使用运维中,一些特殊情况需要批量删除陈旧或异常数据。 如果通过 delete from 【表名】 where 【条件】 直接删除,可能会由于数据量过大,事务执行时间过长,造成死锁。 二、解决方案 通过 Procedure 使用循环…

怎么提升机器人外呼的转化效率

在某些情况下,如市场调查、产品推广等,语音机器人可以高效地完成大量的呼叫任务,并能通过预设的语音脚本和智能识别功能,初步筛选和分类潜在客户。此时,不转人工可能更为高效和经济。 然而,在一些需要深度沟…

jenkins使用注意问题

1.在编写流水线时并不知道当前处在哪个目录,导致名使用不当,以及文件位置不清楚 流水线任务默认路径是,test4_mvn为jenkins任务名 [Pipeline] sh (hide)pwd /var/jenkins_home/workspace/test4_mvn maven任务也是,看来是一样的…

前端相关面试题--html

html是什么 html是超文本标记语言,与js和css一样,是由W3C(万维网联盟)制定的一套语言,超文本 指的是连接一个网站内或多个网站的网页的链接 标记是 html使用各种标记 来注明文本、图片、链接等 以便在浏览器这种进行 …

自动驾驶基础一车辆模型

模型概述: 自行车动力学模型通常用于研究自行车在骑行过程中的行为,如稳定性、操控性和速度等。模型可以基于不同的简化假设和复杂度,从简单的二维模型到复杂的三维模型,甚至包括骑行者的动态。力学方程: 基础物理学方…

【数据结构与算法】广度优先遍历

前序/中序/后序遍历属于 深度优先遍历 广度优先遍历实现 节点定义 /*** Definition for a binary tree node.* function TreeNode(val) {* this.val val;* this.left this.right null;* }*/对应lc 102题 注意js里面 push pop unshift shift之间的区别和关系 // 递…

【SpringBoot整合系列】SpringBoot整合kinfe4j

目录 kinfe4j与Swagger的区别 SpringBoot2.x整合kinfe4j1.添加依赖2.启动类注解3.创建Knife4J配置类4.实体类5.接口admin访问 api访问 常用注解汇总SpringBoot3.x整合Kinfe4j启动报错解决1.更换依赖2.启动类3.配置4.配置类5.参数实体类6.接口admin访问 api访问 各版本注解参照 …