低功耗蓝牙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

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来