低功耗蓝牙ble开发(一)——bluez介绍及源码分析

2024-06-15 04:44

本文主要是介绍低功耗蓝牙ble开发(一)——bluez介绍及源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

bluez-5.56源码

https://mirrors.edge.kernel.org/pub/linux/bluetooth/bluez-5.64.tar.xz

0、bluez5开发

​ bluez5主要提供基于HCI和基于DBUS的接口,基于HCI的接口主要用于更细致控制蓝牙硬件模块,而基于DBUS的接口提供大量的蓝牙上层协议,能更好的管理蓝牙。

不使用glib、dbus开发的话,想要开发周边设备可以借鉴btmgmt.c和btgatt-server.c、btgatt-client.c

使用glib、dbus开发的话,可以参考gatt-service.c

user space
APP:上层应⽤程序
Pluseaudio/pipewire:A2DP的组件
Bluetoothd: 蓝⽛守护进程
Bluez: 包括Bluez tool跟Bluez lib

kernel space
内核代码包含以下⼏部分
driver/bluetooth
net/bluetooth
include/net/bluetooth

在应用层,bluetoothd进程管理蓝牙,bluetooctl提供用户操作命令,通过dbus守护进程进行IPC通信,使用基于dbus的bluez开发主要就是调用bluez提供的dbus接口方法以及属性。

1、bluez5源代码组织架构

BlueZ5源代码中有很多目录和文件:

android/ - 用于替代android中bluedroid的android版本bluez源码。

attrib/ - 包含gatttool 源码以及与gatt attribute相关的代码,gatttool程序入口为gatttool.c。

btio/ - 通过的标准socket接口与BlueZ5 kernel模块通信?。

client/ - bluetoothctl源码,用于与 bluetoothd 交互,程序入口为main.c。

doc/ - BlueZ5 API文档。

emulator/ - 与bluetooth虚拟controller工具相关的代码。从字面意思可以推测出它是用来仿真的,通过阅读代码发现它是仿真controller的,通过btdev_create函数来创建一个虚拟的蓝牙controller设备。 kernel也是支持的,"/dev/vhci"这个设备被虚拟为一个虚拟的蓝牙设备。

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等插件)。包含 BlueZ 插件的源代码,这些插件扩展了 BlueZ 的功能,例如支持不同的蓝牙协议和配置文件。

profiles/ - BlueZ5蓝牙上层协议(a2dp,hid等)源码。每个子目录实现一个特定的蓝牙配置文件。

src/ - bluetoothd源码,程序入口为main.c,包含核心 BlueZ 守护进程 bluetoothd 的源代码。这里是主要的实现逻辑,包括设备管理、服务发现、配置和连接管理等。

​ src/shared/ - 包含在多个模块中共享的源代码。

​ src/adapter - 包含适配器管理相关的代码。

test/ - 测试蓝牙功能的py脚本,通过dbus接口和bluetoothd进行通信。 dbus提供支持python和c语言的接口。

tools/ - Bluez5测试工具集源码。包含各种命令行工具的源代码,例如 hcitoolgatttoolbtmgmt 等。这些工具用于与蓝牙设备进行交互和调试。也算是使用实列代码,可以参照工具的功能参考源码。

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文件。

2、bluez5核心代码分析

​ bluez核心代码在src目录下,入口函数是main.c,bluez5编译后会生成bluetoothd可执行文件,加载配置文件bluetooth.conf,下面进入main.c函数开始分析(注:bluez源码很多地方是异步操作的,看源码时经常需要根据关键词到处搜索注册的回调函数在哪里被触发)

(1)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)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)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)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)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)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)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)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)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)/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)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)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)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)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)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_methodserr = 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)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_propertiesif (!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);……
}

这篇关于低功耗蓝牙ble开发(一)——bluez介绍及源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062457

相关文章

揭秘未来艺术:AI绘画工具全面介绍

📑前言 随着科技的飞速发展,人工智能(AI)已经逐渐渗透到我们生活的方方面面。在艺术创作领域,AI技术同样展现出了其独特的魅力。今天,我们就来一起探索这个神秘而引人入胜的领域,深入了解AI绘画工具的奥秘及其为艺术创作带来的革命性变革。 一、AI绘画工具的崛起 1.1 颠覆传统绘画模式 在过去,绘画是艺术家们通过手中的画笔,蘸取颜料,在画布上自由挥洒的创造性过程。然而,随着AI绘画工

20.Spring5注解介绍

1.配置组件 Configure Components 注解名称说明@Configuration把一个类作为一个loC容 器 ,它的某个方法头上如果注册7@Bean , 就会作为这个Spring容器中的Bean@ComponentScan在配置类上添加@ComponentScan注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan>@Sc

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

高度内卷下,企业如何通过VOC(客户之声)做好竞争分析?

VOC,即客户之声,是一种通过收集和分析客户反馈、需求和期望,来洞察市场趋势和竞争对手动态的方法。在高度内卷的市场环境下,VOC不仅能够帮助企业了解客户的真实需求,还能为企业提供宝贵的竞争情报,助力企业在竞争中占据有利地位。 那么,企业该如何通过VOC(客户之声)做好竞争分析呢?深圳天行健企业管理咨询公司解析如下: 首先,要建立完善的VOC收集机制。这包括通过线上渠道(如社交媒体、官网留言

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W+,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码+数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人  Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全:1000个热