当前位置: 首页 > backend >正文

【Bluedroid】蓝牙启动之gatt_init 流程源码解析

本文围绕Android蓝牙协议栈中 GATT(通用属性配置文件)模块的初始化函数gatt_init展开,深入解析其核心实现逻辑与关键步骤。通过分析gatt_init及其关联子函数(如L2CA_RegisterFixedChannelgatt_profile_db_initEattExtension::Start等),以及相关数据结构(如tGATT_CB控制块、tL2CAP_FIXED_CHNL_REG注册结构体)的作用,阐明 GATT 模块如何完成状态初始化、底层协议交互(L2CAP 通道注册)、服务数据库构建及扩展功能(EATT)激活,为 GATT 服务发现、属性操作等核心功能奠定基础。

一、概述

GATT(Generic Attribute Profile)是蓝牙协议栈中负责属性数据管理的核心模块,广泛应用于低功耗蓝牙(BLE)和传统蓝牙(BR/EDR)场景。gatt_init作为 GATT 模块的初始化入口函数,其核心目标是完成模块状态初始化、与底层 L2CAP 协议的交互注册、服务数据库构建及扩展功能激活,确保 GATT 模块具备处理上层应用请求(如服务发现、属性读写)的能力。

本文聚焦gatt_init的实现细节,覆盖以下关键流程:

  1. 全局状态初始化:清空 GATT 控制块(tGATT_CB)、重置连接管理器,确保模块初始状态一致。

  2. L2CAP 通道注册:为 BLE 场景注册固定 L2CAP 通道(用于 ATT 协议),为 BR/EDR 场景注册动态 PSM(协议服务多路复用器),建立 GATT 与底层传输协议的通信链路。

  3. 服务句柄规划:通过预定义起始句柄(如GATT_GATT_START_HANDLE)划分 ATT 数据库地址范围,避免服务句柄冲突。

  4. 服务管理结构初始化:创建服务与句柄链表(hdl_list_infosrv_list_info),为后续服务发现、属性操作提供数据支撑。

  5. 标准服务数据库构建:通过gatt_profile_db_init注册 GATT 自身的标准属性(如服务变更特征、数据库哈希特征),支撑客户端对 GATT 元数据的访问。

  6. EATT 扩展激活:启动扩展 ATT 协议(EATT),支持大 MTU、长操作等高级特性,提升蓝牙数据传输效率。

二、源码分析

gatt_init

packages/modules/Bluetooth/system/stack/gatt/gatt_main.cc
tGATT_CB gatt_cb; // 全局GATT控制块(存储模块运行状态)/********************************************************************************* Function         gatt_init** Description      This function is enable the GATT profile on the device.*                  It clears out the control blocks, and registers with L2CAP.** Returns          void*******************************************************************************/
void gatt_init(void) {tL2CAP_FIXED_CHNL_REG fixed_reg;log::verbose("");// 1. 全局控制块与基础初始化gatt_cb = tGATT_CB();connection_manager::reset(true);// 2.L2CAP 通道注册(BLE 场景)memset(&fixed_reg, 0, sizeof(tL2CAP_FIXED_CHNL_REG)); // 初始化L2CAP固定通道配置结构体gatt_cb.sign_op_queue = fixed_queue_new(SIZE_MAX);gatt_cb.srv_chg_clt_q = fixed_queue_new(SIZE_MAX);/* First, register fixed L2CAP channel for ATT over BLE */fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback; // BLE连接建立/断开的回调fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind; // BLE接收到GATT数据的回调// BLE链路拥塞状态变化的回调fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback; /* congestion callback */// the GATT timeout is updated after a connection// is established, when we know whether any// clients existfixed_reg.default_idle_tout = L2CAP_NO_IDLE_TIMEOUT; // 默认空闲超时(连接建立后更新)L2CA_RegisterFixedChannel(L2CAP_ATT_CID, &fixed_reg); // 注册固定L2CAP通道(用于BLE的ATT协议)// 3. BR/EDR 场景的动态通道注册gatt_cb.over_br_enabled =osi_property_get_bool("bluetooth.gatt.over_bredr.enabled", true);/* Now, register with L2CAP for ATT PSM over BR/EDR */// 注册ATT的PSM(协议服务多路复用器)if (gatt_cb.over_br_enabled &&!L2CA_Register2(BT_PSM_ATT, dyn_info, false /* enable_snoop */, nullptr,GATT_MAX_MTU_SIZE, 0, BTM_SEC_NONE)) {log::error("ATT Dynamic Registration failed");}// 4. GATT 服务句柄初始化gatt_cb.hdl_cfg.gatt_start_hdl = GATT_GATT_START_HANDLE;gatt_cb.hdl_cfg.gap_start_hdl = GATT_GAP_START_HANDLE;gatt_cb.hdl_cfg.gmcs_start_hdl = GATT_GMCS_START_HANDLE;gatt_cb.hdl_cfg.gtbs_start_hdl = GATT_GTBS_START_HANDLE;gatt_cb.hdl_cfg.tmas_start_hdl = GATT_TMAS_START_HANDLE;gatt_cb.hdl_cfg.app_start_hdl = GATT_APP_START_HANDLE;// 5. 服务列表与数据库初始化gatt_cb.hdl_list_info = new std::list<tGATT_HDL_LIST_ELEM>();  // 句柄列表(存储服务/特征的句柄映射)gatt_cb.srv_list_info = new std::list<tGATT_SRV_LIST_ELEM>();  // 服务列表(存储服务元数据,如UUID、范围)gatt_profile_db_init();// 6. 启动 EATT 扩展EattExtension::GetInstance()->Start();
}

完成 GATT 模块的基础配置、与底层协议(L2CAP)的交互注册,以及服务管理相关的数据结构初始化。为 GATT 模块运行奠定基础,主要完成以下任务:

  1. 初始化控制块:清空全局状态,重置连接管理器。

  2. 注册 L2CAP 通道:为 BLE(固定通道)和 BR/EDR(动态 PSM)场景建立与底层协议的通信链路。

  3. 规划句柄空间:通过起始句柄划分 ATT 数据库的地址范围,避免服务冲突。

  4. 准备服务管理结构:通过链表动态管理服务和句柄信息,为后续服务发现、属性操作做准备。

  5. 启动扩展功能:支持 EATT 以兼容更复杂的蓝牙场景。

tL2CAP_FIXED_CHNL_REG fixed_reg

packages/modules/Bluetooth/system/stack/include/l2c_api.h
/*********************************************************************************                      Fixed Channel callback prototypes*******************************************************************************//* Fixed channel connected and disconnected. Parameters are*      channel*      BD Address of remote*      true if channel is connected, false if disconnected*      Reason for connection failure*      transport : physical transport, BR/EDR or LE*/
typedef void(tL2CA_FIXED_CHNL_CB)(uint16_t, const RawAddress&, bool, uint16_t,tBT_TRANSPORT);/* Signalling data received. Parameters are*      channel*      BD Address of remote*      Pointer to buffer with data*/
typedef void(tL2CA_FIXED_DATA_CB)(uint16_t, const RawAddress&, BT_HDR*);/* Congestion status callback protype. This callback is optional. If* an application tries to send data when the transmit queue is full,* the data will anyways be dropped. The parameter is:*      remote BD_ADDR*      true if congested, false if uncongested*/
typedef void(tL2CA_FIXED_CONGESTION_STATUS_CB)(const RawAddress&, bool);/* Fixed channel registration info (the callback addresses and channel config)*/
typedef struct {tL2CA_FIXED_CHNL_CB* pL2CA_FixedConn_Cb; // 连接状态回调(处理通道连接 / 断开事件)tL2CA_FIXED_DATA_CB* pL2CA_FixedData_Cb; // 数据接收回调(处理收到的固定通道数据)tL2CA_FIXED_CONGESTION_STATUS_CB* pL2CA_FixedCong_Cb; // 拥塞状态回调(监控发送队列状态)// 默认空闲超时时间(毫秒)。若通道在此时长内无数据传输,L2CAP 层可能自动断开以释放资源uint16_t default_idle_tout; // 传输完成回调(仅 eRTM 模式有效,通知数据包发送或丢弃结果)tL2CA_TX_COMPLETE_CB*pL2CA_FixedTxComplete_Cb; /* fixed channel tx complete callback */
} tL2CAP_FIXED_CHNL_REG;

L2CAP的固定通道(Fixed Channel)回调接口与注册结构体,用于管理预定义的标准化 L2CAP 通道(如 SDP、RFCOMM 等)。固定通道通常用于蓝牙核心协议中规定的 “必选服务”,其通道号(Channel ID)是预定义的(如 SDP 固定使用通道 0x0001),无需动态协商。

典型使用场景:SDP 固定通道

以 SDP(服务发现协议)为例,其固定使用 L2CAP 通道 0x0001。SDP 模块通过 tL2CAP_FIXED_CHNL_REG 注册回调,处理通道事件:

①注册固定通道:SDP 模块初始化时,填充 tL2CAP_FIXED_CHNL_REG 并注册;

②连接建立:当手机(客户端)发起 SDP 连接请求时,耳机(服务器)的 L2CAP 层触发 sdp_fixed_conn_cb(连接状态回调),通知 SDP 模块 “通道 0x0001 已连接”,SDP 模块准备接收服务发现请求。

③数据传输:手机发送 SDP 请求(如搜索音频服务),耳机 L2CAP 层通过固定通道 0x0001 接收数据,触发 sdp_fixed_data_cb(数据接收回调),SDP 模块解析请求并生成响应。

④拥塞处理:若耳机发送队列满,L2CAP 层触发 sdp_fixed_cong_cb(拥塞状态回调),SDP 模块暂停发送响应数据,避免丢包;队列空闲后,恢复发送。

⑤断开连接:手机断开蓝牙链路时,耳机 L2CAP 层触发 sdp_fixed_conn_cb(连接状态回调),通知 SDP 模块 “通道 0x0001 断开”,SDP 模块释放相关资源。

固定通道的设计意义

  • 标准化与兼容性:固定通道用于蓝牙核心协议规定的必选服务(如 SDP、RFCOMM),通道号预定义,无需动态协商,确保不同设备间的互操作性。

  • 高效性:固定通道的连接过程简化(无需 PSM 协商、动态 CID 分配),降低协议开销,适合高频次、低延迟的服务(如 SDP 发现请求)。

  • 可管理性:通过 default_idle_tout 控制空闲超时,避免资源长期占用;拥塞回调提供流量控制,保障数据传输可靠性。

tGATT_CB

packages/modules/Bluetooth/system/stack/gatt/gatt_int.h
typedef struct {tGATT_TCB tcb[GATT_MAX_PHY_CHANNEL];fixed_queue_t* sign_op_queue;uint16_t next_handle;     /* next available handle */uint16_t last_service_handle; /* handle of last service */tGATT_SVC_CHG gattp_attr; /* GATT profile attribute service change */tGATT_IF gatt_if;std::list<tGATT_HDL_LIST_ELEM>* hdl_list_info;std::list<tGATT_SRV_LIST_ELEM>* srv_list_info;fixed_queue_t* srv_chg_clt_q; /* service change clients queue */tGATT_REG cl_rcb[GATT_MAX_APPS];/* list of connection link control blocks.* Since clcbs are also keep in the channels (ATT and EATT) queues while* processing, we want to make sure that references to elements are not* invalidated when elements are added or removed from the list. This is why* std::list is used.*/std::list<tGATT_CLCB> clcb_queue;#if (GATT_CONFORMANCE_TESTING == TRUE)bool enable_err_rsp;uint8_t req_op_code;uint8_t err_status;uint16_t handle;
#endiftGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS];uint16_thandle_of_h_r; /* Handle of the handles reused characteristic value */uint16_t handle_cl_supported_feat;uint16_t handle_sr_supported_feat;uint8_tgatt_svr_supported_feat_mask; /* Local supported features as a server *//* Supported features as a client. To be written to remote device.* Note this is NOT a value of the characteristic with handle* handle_cl_support_feat, as that one should be written by remote device.*/uint8_t gatt_cl_supported_feat_mask;uint16_t handle_of_database_hash;Octet16 database_hash;tGATT_APPL_INFO cb_info;tGATT_HDL_CFG hdl_cfg;bool over_br_enabled;
} tGATT_CB;

tGATT_CB 是 GATT 模块的 “核心大脑”,通过丰富的成员变量管理:

  • 传输状态与操作队列(确保数据有序处理);

  • ATT 数据库的句柄与服务(支撑属性的查找与管理);

  • 连接与客户端(支持多设备并发连接);

  • 特性能力与扩展(兼容规范中的高级功能);

该结构体为 GATT 服务发现、属性读写、通知 / 指示等核心功能提供了基础支撑。

connection_manager::reset

packages/modules/Bluetooth/system/stack/gatt/connection_manager.cc
/** Reset bg device list. If called after controller reset, set |after_reset|* to true, as there is no need to wipe controller acceptlist in this case. */
void reset(bool after_reset) {bgconn_dev.clear(); // 清空所有设备的连接跟踪信息if (!after_reset) {target_announcements_filtering_set(false);BTM_AcceptlistClear();}
}// Maps address to apps trying to connect to it
// 映射:蓝牙地址 → 尝试连接该地址的应用集合
std::map<RawAddress, tAPPS_CONNECTING> bgconn_dev;

统一重置连接管理模块的状态,通过清空上层的连接跟踪信息(bgconn_dev)和选择性清理底层控制器配置(广播过滤、接受列表),确保蓝牙连接逻辑的一致性和可靠性。bgconn_dev 则作为上层应用连接请求的 “追踪器”,避免重复连接,是连接管理的关键数据结构。

L2CA_RegisterFixedChannel

packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
bool L2CA_RegisterFixedChannel(uint16_t fixed_cid,tL2CAP_FIXED_CHNL_REG* p_freg) {if ((fixed_cid < L2CAP_FIRST_FIXED_CHNL) ||(fixed_cid > L2CAP_LAST_FIXED_CHNL)) {log::error("Invalid fixed CID: 0x{:04x}", fixed_cid);return false;}// 存储固定通道的注册信息l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = *p_freg;log::debug("Registered fixed channel:{}", fixed_channel_text(fixed_cid));return true;
}

注册 L2CAP 固定通道,为上层协议(如 GATT、SMP 等)提供与 L2CAP 层交互的入口。

流程:GATT 模块初始化时,将自身的回调函数(如处理连接建立、接收 ATT 数据)封装到 tL2CAP_FIXED_CHNL_REG 结构体,通过 L2CA_RegisterFixedChannel 注册到 L2CAP 层。

当蓝牙控制器接收到 ATT 协议数据(如来自对端设备的读属性请求),L2CAP 层会根据 CID(0x0004)查找 fixed_reg 数组,调用 GATT 注册的 gatt_le_data_ind 回调,将数据传递给 GATT 模块处理。

L2CA_Register2

packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
uint16_t L2CA_Register2(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info,uint16_t my_mtu, uint16_t required_remote_mtu,uint16_t sec_level) {auto ret = L2CA_Register(psm, p_cb_info, enable_snoop, p_ertm_info, my_mtu,required_remote_mtu, sec_level);get_btm_client_interface().security.BTM_SetSecurityLevel(false, "", 0, sec_level, psm, 0, 0);return ret;
}

动态注册 L2CAP 通道(区别于固定通道注册),并为注册的通道配置安全等级。

GATT 模块启用 BR/EDR 支持时,通过 L2CA_Register2 注册 ATT 协议的 PSM(BT_PSM_ATT),并指定本地 MTU(GATT_MAX_MTU_SIZE)和安全等级(BTM_SEC_NONE,无安全要求)。

L2CAP 层为 ATT 协议分配动态通道资源,并在后续接收到对端设备的连接请求(基于此 PSM)时,调用 GATT 注册的回调函数(如数据接收回调)处理业务逻辑。

gatt_profile_db_init

packages/modules/Bluetooth/system/stack/gatt/gatt_attr.cc
static std::map<uint16_t, std::deque<gatt_op_cb_data>> OngoingOps;/********************************************************************************* Function         gatt_profile_db_init** Description      Initializa the GATT profile attribute database.*******************************************************************************/
void gatt_profile_db_init(void) {// 1. 初始化准备:填充 UUID 与清空操作队列/* Fill our internal UUID with a fixed pattern 0x81 */std::array<uint8_t, Uuid::kNumBytes128> tmp;tmp.fill(0x81);OngoingOps.clear();// 2. 注册并启动 GATT 服务接口/* Create a GATT profile service */gatt_cb.gatt_if = GATT_Register(Uuid::From128BitBE(tmp), "GattProfileDb",&gatt_profile_cback, false);GATT_StartIf(gatt_cb.gatt_if);// 3. 定义标准特征的 UUIDUuid service_uuid = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);// GATT 服务器服务的 UUID// 服务变更特征(用于通知客户端服务数据库变化)Uuid srv_changed_char_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);// 服务器支持的特性特征(如是否支持 EATT)Uuid svr_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);// 客户端支持的特性特征(客户端能力声明)Uuid cl_sup_feat_uuid = Uuid::From16Bit(GATT_UUID_CLIENT_SUP_FEAT);// 数据库哈希特征(ATT 数据库的哈希值,用于快速同步)Uuid database_hash_uuid = Uuid::From16Bit(GATT_UUID_DATABASE_HASH);// 4. 构建 GATT 服务的属性数据库:包含一个主服务和四个特征btgatt_db_element_t service[] = {// 1. 主服务声明{.uuid = service_uuid,.type = BTGATT_DB_PRIMARY_SERVICE, // 类型:主服务},// 2. 服务变更特征(Service Changed Characteristic){.uuid = srv_changed_char_uuid,.type = BTGATT_DB_CHARACTERISTIC, // 类型:特征.properties = GATT_CHAR_PROP_BIT_INDICATE,  // 属性:可指示(需确认后发送通知).permissions = 0,  // 权限:无(实际由特征值的描述符控制)},// 3. 服务器支持特征(Server Supported Features){.uuid = svr_sup_feat_uuid,.type = BTGATT_DB_CHARACTERISTIC,.properties = GATT_CHAR_PROP_BIT_READ,  // 属性:可读.permissions = GATT_PERM_READ,  // 权限:读允许},// 4. 客户端支持特征(Client Supported Features){.uuid = cl_sup_feat_uuid,.type = BTGATT_DB_CHARACTERISTIC,.properties = GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE, // 属性:可读、可写.permissions = GATT_PERM_READ | GATT_PERM_WRITE, // 权限:读/写允许},// 5. 数据库哈希特征(Database Hash){.uuid = database_hash_uuid,.type = BTGATT_DB_CHARACTERISTIC,.properties = GATT_CHAR_PROP_BIT_READ, // 属性:可读.permissions = GATT_PERM_READ, // 权限:读允许}};// 5. 向 GATT 服务器添加服务GATTS_AddService(gatt_cb.gatt_if, service,sizeof(service) / sizeof(btgatt_db_element_t));// 6. 记录特征句柄到全局控制块gatt_cb.handle_of_h_r = service[1].attribute_handle; // 服务变更特征句柄gatt_cb.handle_sr_supported_feat = service[2].attribute_handle; // 服务器支持特征句柄gatt_cb.handle_cl_supported_feat = service[3].attribute_handle; // 客户端支持特征句柄gatt_cb.handle_of_database_hash = service[4].attribute_handle;  // 数据库哈希特征句柄// 7. 配置支持的特性掩码// 服务器支持的特性:启用 EATT(扩展 ATT)gatt_cb.gatt_svr_supported_feat_mask |= BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK;// 客户端支持的特性:启用 Android 特定功能gatt_cb.gatt_cl_supported_feat_mask |= BLE_GATT_CL_ANDROID_SUP_FEAT;// 若启用健壮缓存,添加缓存支持位if (gatt_cl_is_robust_caching_enabled())gatt_cb.gatt_cl_supported_feat_mask |= BLE_GATT_CL_SUP_FEAT_CACHING_BITMASK;log::verbose("gatt_if={} EATT supported", gatt_cb.gatt_if);
}

GATT 服务器需要暴露一些 “自身相关” 的属性(如服务变更状态、支持的特性),供客户端发现和操作。gatt_profile_db_init 的目的是在 ATT 数据库中创建并注册 GATT 服务自身的属性(即 GATT 配置文件服务),确保客户端能通过标准流程访问这些元数据。

包含以下关键能力:

  • 服务变更通知:通过 “服务变更特征” 告知客户端服务数据库的变化。

  • 能力声明:通过 “服务器 / 客户端支持特征” 暴露双方的协议能力(如 EATT、缓存)。

  • 数据库哈希:通过 “数据库哈希特征” 帮助客户端快速判断是否需要同步数据库。

这些功能是 GATT 协议的核心元数据管理机制,确保客户端能与服务器高效交互,是蓝牙设备间服务发现和属性操作的基础支撑。

GATT_Register
packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc
#ifndef GATT_MAX_APPS
#define GATT_MAX_APPS 32 /* note: 2 apps used internally GATT and GAP */
#endif/********************************************************************************* Function         GATT_Register** Description      This function is called to register an  application*                  with GATT** Parameter        p_app_uuid128: Application UUID*                  p_cb_info: callback functions.*                  eatt_support: indicate eatt support.** Returns          0 for error, otherwise the index of the client registered*                  with GATT*******************************************************************************/
tGATT_IF GATT_Register(const Uuid& app_uuid128, const std::string& name,tGATT_CBACK* p_cb_info, bool eatt_support) {tGATT_REG* p_reg;uint8_t i_gatt_if = 0;tGATT_IF gatt_if = 0;// 1. 检查重复注册(避免 UUID 冲突)for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS;i_gatt_if++, p_reg++) {if (p_reg->in_use && p_reg->app_uuid128 == app_uuid128) {log::error("Application already registered, uuid={}",app_uuid128.ToString());return 0;}}// 2. 强制启用 eATT(测试场景兼容)if (stack_config_get_interface()->get_pts_use_eatt_for_all_services()) {log::info("PTS: Force to use EATT for servers");eatt_support = true;}// 3. 分配 GATT 接口(分配可用槽位)for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS;i_gatt_if++, p_reg++) {if (!p_reg->in_use) {*p_reg = {}; // 清空槽位(防止旧数据残留)i_gatt_if++; /* one based number */  // 转换为 1-based 索引(接口从 1 开始)p_reg->app_uuid128 = app_uuid128;gatt_if = p_reg->gatt_if = (tGATT_IF)i_gatt_if;p_reg->app_cb = *p_cb_info; // 保存应用回调p_reg->in_use = true;p_reg->eatt_support = eatt_support; // 记录 eATT 支持状态p_reg->name = name;log::info("Allocated name:{} uuid:{} gatt_if:{} eatt_support:{}", name,app_uuid128.ToString(), gatt_if, eatt_support);return gatt_if; // 返回分配的接口索引}}// 4. 处理最大应用数限制log::error("Unable to register GATT client, MAX client reached: {}",GATT_MAX_APPS);return 0;
}

向上层应用分配 GATT 接口(tGATT_IF),并注册应用的 UUID、回调函数及 eATT(扩展 ATT 协议)支持能力。

PTS(Protocol Test Suite,蓝牙协议测试套件)用于验证设备的协议兼容性。某些测试用例要求强制启用 eATT。

典型调用流程

以手机上的心率监测应用为例,注册到 GATT 模块的流程如下:

  1. 应用初始化: 心率应用启动后,生成唯一的 128 位 UUID(如 0000ffe1-0000-1000-8000-00805f9b34fb),定义回调函数(如 on_service_discovered 处理服务发现完成事件),并调用 GATT_Register 注册。

  2. GATT 模块检查: GATT 模块遍历 gatt_cb.cl_rcb 数组,确认该 UUID 未被注册(避免重复),并检查 PTS 配置(若启用则强制 eATT)。

  3. 分配接口: 找到可用槽位后,分配 gatt_if=1(假设第一个可用槽位),保存应用 UUID、回调、eATT 状态,标记槽位为已使用。

  4. 应用后续操作:心率应用使用返回的 gatt_if=1 作为句柄,调用其他 GATT 接口(如 GATT_DiscoverServices 发现心率服务),GATT 模块通过 app_cb 回调通知应用事件(如服务发现结果)。

GATT_StartIf
packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc
/********************************************************************************* Function         GATT_StartIf** Description      This function is called after registration to start*                  receiving callbacks for registered interface.  Function may*                  call back with connection status and queued notifications** Parameter        gatt_if: applicaiton interface.** Returns          None.*******************************************************************************/
void GATT_StartIf(tGATT_IF gatt_if) {tGATT_REG* p_reg;tGATT_TCB* p_tcb;RawAddress bda = {};uint8_t start_idx, found_idx;uint16_t conn_id;tBT_TRANSPORT transport;log::debug("Starting GATT interface gatt_if_:{}", gatt_if);// 1. 获取应用注册信息(tGATT_REG)p_reg = gatt_get_regcb(gatt_if);if (p_reg != NULL) {// 2. 遍历已连接的蓝牙设备start_idx = 0;while (gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) {// 3. 获取设备连接上下文(tGATT_TCB)p_tcb = gatt_find_tcb_by_addr(bda, transport);log::info("GATT interface {} already has connected device {}", gatt_if,ADDRESS_TO_LOGGABLE_CSTR(bda));// 4. 触发应用的连接回调if (p_reg->app_cb.p_conn_cb && p_tcb) {conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if);log::info("Invoking callback with connection id {}", conn_id);(*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, true, GATT_CONN_OK,transport);} else {log::info("Skipping callback as none is registered");}// 5. 更新遍历索引start_idx = ++found_idx;}}
}

GATT中的接口启动函数,在应用完成注册(通过 GATT_Register 获取 gatt_if 接口)后,同步当前已存在的 GATT 连接状态,并触发应用的连接回调,确保应用启动后能立即感知已连接的蓝牙设备。

典型使用场景

假设手机上的心率监测应用(通过 GATT_Register 注册,gatt_if=1)启动后,调用 GATT_StartIf(gatt_if=1)。此时,手机可能已通过 GATT 连接了一个心率带(设备地址 AA:BB:CC:DD:EE:FF,传输类型为低功耗蓝牙)。

①遍历已连接设备:gatt_find_the_connected_bda 找到心率带的地址 AA:BB:CC:DD:EE:FF,传输类型 BT_TRANSPORT_LE,索引 found_idx=0

②获取连接上下文:gatt_find_tcb_by_addr 找到该设备的 tGATT_TCB 结构体(包含连接状态、MTU 等信息)。

③触发连接回调:

应用的 p_conn_cb 被调用,参数为:

  • gatt_if=1(当前应用接口)。

  • bda=AA:BB:CC:DD:EE:FF(心率带地址)。

  • conn_id(如 0x00010001,由 tcb_idx=1gatt_if=1 组合)。

  • true(已连接)。

  • GATT_CONN_OK(连接成功)。

  • transport=BT_TRANSPORT_LE(低功耗蓝牙)。

④应用处理回调:心率应用收到回调后,知道已与心率带建立 GATT 连接,随即开始发现心率服务(如 GATT_DiscoverServices),读取心率数据。

gatt_get_regcb

packages/modules/Bluetooth/system/stack/gatt/gatt_utils.cc
/********************************************************************************* Function         gatt_get_regcb** Description      The function returns the registration control block.** Returns          pointer to the registration control block or NULL*******************************************************************************/
tGATT_REG* gatt_get_regcb(tGATT_IF gatt_if) {// 1. 接口索引有效性检查uint8_t ii = (uint8_t)gatt_if;tGATT_REG* p_reg = NULL;if (ii < 1 || ii > GATT_MAX_APPS) {log::warn("gatt_if out of range = {}", ii);return NULL;}// 2. 计算注册数组索引// Index for cl_rcb is always 1 less than gatt_if.p_reg = &gatt_cb.cl_rcb[ii - 1];// 3. 验证注册控制块是否在使用if (!p_reg->in_use) {log::warn("gatt_if found but not in use.");return NULL;}return p_reg;
}

根据 gatt_if 接口索引,查找并返回对应的应用注册控制块(tGATT_REG 结构体指针)。

gatt_find_the_connected_bda

packages/modules/Bluetooth/system/stack/gatt/gatt_utils.cc
/********************************************************************************* Function         gatt_find_the_connected_bda** Description      This function find the connected bda** Returns           true if found*******************************************************************************/
bool gatt_find_the_connected_bda(uint8_t start_idx, RawAddress& bda,uint8_t* p_found_idx,tBT_TRANSPORT* p_transport) {uint8_t i;bool found = false;log::debug("start_idx={}", start_idx);// 1. 遍历连接列表(gatt_cb.tcb 数组)for (i = start_idx; i < GATT_MAX_PHY_CHANNEL; i++) {// 2. 检查连接有效性if (gatt_cb.tcb[i].in_use && gatt_cb.tcb[i].ch_state == GATT_CH_OPEN) {// 3. 输出找到的设备信息bda = gatt_cb.tcb[i].peer_bda;*p_found_idx = i;*p_transport = gatt_cb.tcb[i].transport;found = true;log::debug("bda: {}", ADDRESS_TO_LOGGABLE_CSTR(bda));break;}}log::debug("found={} found_idx={}", found, i);return found;
}

在 GATT 连接列表中查找已连接且活跃的蓝牙设备,返回其地址、索引及传输类型。

GATTS_AddService
packages/modules/Bluetooth/system/stack/gatt/gatt_api.cc
/********************************************************************************* Function         GATTS_AddService** Description      This function is called to add GATT service.** Parameter        gatt_if : application if*                  service : pseudo-representation of service and it's content*                  count   : size of service** Returns          on success GATT_SERVICE_STARTED is returned, and*                  attribute_handle field inside service elements are filled.*                  on error error status is returned.*******************************************************************************/
tGATT_STATUS GATTS_AddService(tGATT_IF gatt_if, btgatt_db_element_t* service,int count) {uint16_t s_hdl = 0;bool save_hdl = false;  tGATT_REG* p_reg = gatt_get_regcb(gatt_if);bool is_pri = (service->type == BTGATT_DB_PRIMARY_SERVICE) ? true : false;Uuid svc_uuid = service->uuid;log::info("");// 1. 验证应用接口有效性if (!p_reg) {log::error("Inavlid gatt_if={}", gatt_if);return GATT_INTERNAL_ERROR;}// 2. 计算服务所需句柄数量uint16_t num_handles = compute_service_size(service, count);// 3. 分配服务起始句柄(s_hdl)// 标准服务(预定义 UUID):标准服务(如 GATT 服务器、GAP 服务器)使用预配置的固定起始句柄(gatt_cb.hdl_cfg 中的预定义值),确保不同设备间的互操作性if (svc_uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER)) {s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl; // GATT 服务器服务} else if (svc_uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {s_hdl = gatt_cb.hdl_cfg.gap_start_hdl; // GAP 服务器服务} else if (svc_uuid == Uuid::From16Bit(UUID_SERVCLASS_GMCS_SERVER)) {s_hdl = gatt_cb.hdl_cfg.gmcs_start_hdl;} else if (svc_uuid == Uuid::From16Bit(UUID_SERVCLASS_GTBS_SERVER)) {s_hdl = gatt_cb.hdl_cfg.gtbs_start_hdl;} else if (svc_uuid == Uuid::From16Bit(UUID_SERVCLASS_TMAS_SERVER)) {s_hdl = gatt_cb.hdl_cfg.tmas_start_hdl;} else { // 自定义服务(非预定义 UUID)if (!gatt_cb.hdl_list_info->empty()) {s_hdl = gatt_cb.hdl_list_info->front().asgn_range.e_handle + 1;}if (s_hdl < gatt_cb.hdl_cfg.app_start_hdl)s_hdl = gatt_cb.hdl_cfg.app_start_hdl;  // 确保不低于应用起始句柄save_hdl = true; // 标记需要保存句柄信息(如非易失性存储)}// 4. 句柄空间有效性检查/* check for space */if (num_handles > (0xFFFF - s_hdl + 1)) {log::error("no handles, s_hdl={} needed={}", s_hdl, num_handles);return GATT_INTERNAL_ERROR;}// 5. 注册句柄信息并初始化服务数据库tGATT_HDL_LIST_ELEM& list = gatt_add_an_item_to_list(s_hdl);list.asgn_range.app_uuid128 = p_reg->app_uuid128;list.asgn_range.svc_uuid = svc_uuid;list.asgn_range.s_handle = s_hdl;list.asgn_range.e_handle = s_hdl + num_handles - 1;list.asgn_range.is_primary = is_pri;if (save_hdl) {if (gatt_cb.cb_info.p_nv_save_callback)(*gatt_cb.cb_info.p_nv_save_callback)(true, &list.asgn_range);}gatts_init_service_db(list.svc_db, svc_uuid, is_pri, s_hdl, num_handles); // 初始化服务数据库log::verbose("handles needed={}, s_hdl={}, e_hdl={}, uuid={}, is_primary={}",num_handles, loghex(list.asgn_range.s_handle),loghex(list.asgn_range.e_handle), list.asgn_range.svc_uuid,list.asgn_range.is_primary);service->attribute_handle = s_hdl;// 6. 处理服务元素(特性、描述符、包含服务)btgatt_db_element_t* el = service + 1;for (int i = 0; i < count - 1; i++, el++) {const Uuid& uuid = el->uuid;if (el->type == BTGATT_DB_CHARACTERISTIC) { // 特性// 检查属性与权限的兼容性(如认证写需匹配权限)/* data validity checking */if (((el->properties & GATT_CHAR_PROP_BIT_AUTH) &&!(el->permissions & GATT_WRITE_SIGNED_PERM)) ||((el->permissions & GATT_WRITE_SIGNED_PERM) &&!(el->properties & GATT_CHAR_PROP_BIT_AUTH))) {log::verbose("Invalid configuration property={}, perm={}",loghex(el->properties), loghex(el->permissions));return GATT_INTERNAL_ERROR;}// 禁止使用 GATT 属性类型 UUID(如 0x2800 是服务声明 UUID)if (is_gatt_attr_type(uuid)) {log::error("attept to add characteristic with UUID equal to GATT Attribute ""Type {}",uuid);return GATT_INTERNAL_ERROR;}// 添加特性到服务数据库,分配特性值句柄el->attribute_handle = gatts_add_characteristic(list.svc_db, el->permissions, el->properties, uuid);// 若特性有扩展属性,添加扩展属性描述符// add characteristic extended properties descriptor if neededif (el->properties & GATT_CHAR_PROP_BIT_EXT_PROP) {gatts_add_char_ext_prop_descr(list.svc_db, el->extended_properties);}} else if (el->type == BTGATT_DB_DESCRIPTOR) { // 描述符if (is_gatt_attr_type(uuid)) {log::error("attept to add descriptor with UUID equal to GATT Attribute Type ""{}",uuid);return GATT_INTERNAL_ERROR;}// 添加描述符到服务数据库,分配描述符句柄el->attribute_handle =gatts_add_char_descr(list.svc_db, el->permissions, uuid);} else if (el->type == BTGATT_DB_INCLUDED_SERVICE) { // 包含服务tGATT_HDL_LIST_ELEM* p_incl_decl;// 查找被包含服务的句柄信息(需已提前添加)p_incl_decl = gatt_find_hdl_buffer_by_handle(el->attribute_handle);if (p_incl_decl == nullptr) {log::verbose("Included Service not created");return GATT_INTERNAL_ERROR;}// 添加包含服务声明到数据库,分配包含服务句柄el->attribute_handle = gatts_add_included_service(list.svc_db, p_incl_decl->asgn_range.s_handle,p_incl_decl->asgn_range.e_handle, p_incl_decl->asgn_range.svc_uuid);}}log::info("service parsed correctly, now starting");/*this is a new application service start */// 7. 将服务加入全局服务列表// find a place for this service in the listauto lst_ptr = gatt_cb.srv_list_info;auto it = lst_ptr->begin();for (; it != lst_ptr->end(); it++) {if (list.asgn_range.s_handle < it->s_hdl) break; // 按起始句柄排序}auto rit = lst_ptr->emplace(it); // 插入到正确位置tGATT_SRV_LIST_ELEM& elem = *rit;elem.gatt_if = gatt_if;elem.s_hdl = list.asgn_range.s_handle;elem.e_hdl = list.asgn_range.e_handle;elem.p_db = &list.svc_db;elem.is_primary = list.asgn_range.is_primary;elem.app_uuid = list.asgn_range.app_uuid128;elem.type = list.asgn_range.is_primary ? GATT_UUID_PRI_SERVICE: GATT_UUID_SEC_SERVICE;// 8. 同步到服务发现协议(SDP)if (elem.type == GATT_UUID_PRI_SERVICE && gatt_cb.over_br_enabled) {Uuid* p_uuid = gatts_get_service_uuid(elem.p_db);// 标准服务(如 GMCS、GTBS)不添加 SDP 记录(由协议栈自动处理)if (*p_uuid != Uuid::From16Bit(UUID_SERVCLASS_GMCS_SERVER) &&*p_uuid != Uuid::From16Bit(UUID_SERVCLASS_GTBS_SERVER)) {elem.sdp_handle = gatt_add_sdp_record(*p_uuid, elem.s_hdl, elem.e_hdl);} else {elem.sdp_handle = 0;}} else {elem.sdp_handle = 0;}// 9. 更新状态并返回gatt_update_last_srv_info(); // 更新最后服务信息(如最近添加的服务)log::verbose("allocated el s_hdl={}, e_hdl={}, type={}, sdp_hdl={}",loghex(elem.s_hdl), loghex(elem.e_hdl), loghex(elem.type),loghex(elem.sdp_handle));gatt_update_for_database_change();  // 通知数据库变更(如触发缓存更新)gatt_proc_srv_chg(); // 处理服务变更事件(如发送服务变更指示)return GATT_SERVICE_STARTED;
}

将上层应用定义的服务(包含特性、描述符等属性)注册到 GATT 协议栈中,分配唯一的属性句柄,并同步到服务发现数据库(SDP),最终使服务可被远程设备发现和访问。

典型使用场景

以智能手表添加 “心率服务” 为例:

  1. 应用调用 GATTS_AddService,传入 gatt_if=1(应用接口)、service 数组(包含主服务声明、心率特性、心率测量描述符)、count=3(3 个元素)。

  2. 函数验证 gatt_if=1 有效,计算需要 3 个句柄(服务声明、特性、描述符)。

  3. 分配起始句柄 s_hdl=0x000A(假设前一个服务结束于 0x0009),检查句柄空间足够。

  4. 注册句柄信息,初始化服务数据库,添加服务声明(句柄 0x000A)。

  5. 处理特性元素(心率特性,UUID 0x2A37),分配特性值句柄 0x000B,并添加客户端特征配置描述符(句柄 0x000C)。

  6. 服务加入全局列表,按句柄排序(0x000A 插入到合适位置)。

  7. 向 SDP 注册心率服务(UUID 0x180D),句柄范围 0x000A-0x000C

  8. 最终返回 GATT_SERVICE_STARTED,心率服务可被手机发现和读取。

EattExtension::GetInstance()->Start()

packages/modules/Bluetooth/system/stack/eatt/eatt.cc
void EattExtension::Start() { pimpl_->Start(); }

是触发 EATT 功能的初始化与激活。

EATT 是蓝牙 5.0 引入的扩展协议,主要特性包括:

  • 支持更大的 MTU(最大传输单元,默认 ATT MTU 为 23 字节,EATT 可协商至 128-512 字节甚至更高)。

  • 支持长操作(如长写、可靠写),允许传输超过 MTU 大小的属性值。

  • 优化错误处理(如更精确的错误码)。

Start
packages/modules/Bluetooth/system/stack/eatt/eatt.cc
tL2CAP_APPL_INFO reg_info_;void Start() {// 1. 防止重复启动if (eatt_impl_) {log::error("Eatt already started");return;};// 2. 初始化 L2CAP 回调结构体(reg_info_)/* Register server for Eatt */memset(&reg_info_, 0, sizeof(reg_info_));// 处理 L2CAP 连接请求(对端设备发起 EATT 连接)reg_info_.pL2CA_CreditBasedConnectInd_Cb = eatt_connect_ind; // 处理 L2CAP 连接确认(本地接受连接后的确认响应)reg_info_.pL2CA_CreditBasedConnectCfm_Cb = eatt_connect_cfm; // 处理 L2CAP 连接重新配置完成(如 MTU 更新)reg_info_.pL2CA_CreditBasedReconfigCompleted_Cb = eatt_reconfig_completed;// 处理 L2CAP 断开连接指示(连接终止)reg_info_.pL2CA_DisconnectInd_Cb = eatt_disconnect_ind;// 处理 L2CAP 错误事件(如传输错误)reg_info_.pL2CA_Error_Cb = eatt_error_cb;// 处理 L2CAP 数据到达指示(接收对端发送的 EATT 数据)reg_info_.pL2CA_DataInd_Cb = eatt_data_ind;// 处理 L2CAP 连接冲突指示(如同一 PSM 的多个连接请求)reg_info_.pL2CA_CreditBasedCollisionInd_Cb = eatt_collision_ind;// 3. 注册 L2CAP LE CoC 服务if (L2CA_RegisterLECoc(BT_PSM_EATT, reg_info_, BTM_SEC_NONE, {}) == 0) {log::error("cannot register EATT");} else {eatt_impl_ = std::make_unique<eatt_impl>();}}

启动 EATT 扩展模块,完成 L2CAP 服务注册与回调绑定,初始化 EATT 核心实现。

关键步骤:

  1. 防止重复启动(检查 eatt_impl_ 是否已初始化)。

  2. 初始化 L2CAP 回调结构体(reg_info_),绑定 EATT 事件处理函数。

  3. 注册 L2CAP LE CoC 服务(使用 EATT 专用 PSM BT_PSM_EATT)。

  4. 注册成功后创建 EATT 实现实例(eatt_impl_)。

reg_info_ 结构体:L2CAP 信用基础连接(LE CoC)的回调注册结构体,用于绑定 EATT 处理 L2CAP 事件的函数。

LE CoC 与 EATT 的关系:EATT 依赖 LE CoC(Low Energy Connection-Oriented Channels) 作为底层传输协议。LE CoC 是蓝牙 5.0 引入的面向连接的传输机制,相比传统的 L2CAP 流模式(如 ATT 使用的 L2CAP_LE_ CreditBased),LE CoC 提供:

  • 可靠传输:支持流量控制(信用机制)和重传,确保数据完整到达。

  • 大 MTU 支持:可协商更大的 MTU(最大 65535 字节),满足 EATT 长操作(如长写、可靠写)的需求。

  • 服务质量(QoS):支持带宽、延迟等参数配置,适用于音视频流等实时性要求高的场景。

典型流程:EATT 启动与连接建立

结合 Start() 函数,EATT 模块的启动与连接流程如下:

  1. 调用 Start() 启动 EATT:蓝牙协议栈初始化 EATT 模块时调用 Start(),触发 L2CAP 服务注册。

  2. L2CAP 服务注册成功L2CA_RegisterLECoc 成功后,eatt_impl_ 被创建,EATT 模块准备就绪。

  3. 对端设备发起 EATT 连接:当对端设备(如手机)需要使用 EATT 与本地设备(如智能手表)通信时,通过 L2CAP 发送连接请求(触发 eatt_connect_ind 回调)。

  4. 处理连接与数据传输:本地 EATT 模块通过 eatt_connect_cfm 确认连接,后续通过 eatt_data_ind 接收对端发送的 EATT 数据(如 MTU 协商请求、长写操作),完成 EATT 功能。

L2CA_RegisterLECoc
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
/********************************************************************************* Function         L2CA_RegisterLECoc** Description      Other layers call this function to register for L2CAP*                  Connection Oriented Channel.** Returns          PSM to use or zero if error. Typically, the PSM returned*                  is the same as was passed in, but for an outgoing-only*                  connection to a dynamic PSM, a "virtual" PSM is returned*                  and should be used in the calls to L2CA_ConnectLECocReq()*                  and L2CA_DeregisterLECoc()*******************************************************************************/
uint16_t L2CA_RegisterLECoc(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,uint16_t sec_level, tL2CAP_LE_CFG_INFO cfg) {// 1. 安全等级设置if (p_cb_info.pL2CA_ConnectInd_Cb != nullptr || psm < LE_DYNAMIC_PSM_START) {//  If we register LE COC for outgoing connection only, don't register with//  BTM_Sec, because it's handled by L2CA_ConnectLECocReq.get_btm_client_interface().security.BTM_SetSecurityLevel(false, "", 0, sec_level, psm, 0, 0);}// 2. 回调函数有效性验证/* Verify that the required callback info has been filled in**      Note:  Connection callbacks are required but not checked**             for here because it is possible to be only a client**             or only a server.*/if ((!p_cb_info.pL2CA_DataInd_Cb) || (!p_cb_info.pL2CA_DisconnectInd_Cb)) {log::error("No cb registering BLE PSM: 0x{:04x}", psm);return 0;}// 3. PSM 有效性验证/* Verify PSM is valid */if (!L2C_IS_VALID_LE_PSM(psm)) {log::error("Invalid BLE PSM value, PSM: 0x{:04x}", psm);return 0;}tL2C_RCB* p_rcb;uint16_t vpsm = psm;// 4. 动态 PSM 的虚拟分配/* Check if this is a registration for an outgoing-only connection to *//* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */if ((psm >= LE_DYNAMIC_PSM_START) &&(p_cb_info.pL2CA_ConnectInd_Cb == NULL)) {vpsm = L2CA_AllocateLePSM();if (vpsm == 0) {log::error("Out of free BLE PSM");return 0;}log::debug("Real PSM: 0x{:04x}  Virtual PSM: 0x{:04x}", psm, vpsm);}// 5. 注册控制块(tL2C_RCB)管理/* If registration block already there, just overwrite it */p_rcb = l2cu_find_ble_rcb_by_psm(vpsm); // 查找已存在的 RCBif (p_rcb == NULL) {log::debug("Allocate rcp for Virtual PSM: 0x{:04x}", vpsm);p_rcb = l2cu_allocate_ble_rcb(vpsm); // 分配新的 RCBif (p_rcb == NULL) {log::warn("No BLE RCB available, PSM: 0x{:04x}  vPSM: 0x{:04x}", psm,vpsm);return 0;}}log::info("Registered service LE COC PSM: 0x{:04x}", psm);p_rcb->api = p_cb_info; // 绑定回调p_rcb->real_psm = psm; // 记录真实 PSM(动态场景)p_rcb->coc_cfg = cfg;  // 保存配置信息return vpsm;
}

注册 LE CoC 服务,分配 / 验证 PSM(协议服务多路复用器),绑定事件回调,并管理 L2CAP 注册控制块(tL2C_RCB)。

虚拟 PSM 的作用:

  • 避免客户端直接使用动态 PSM(可能与其他服务冲突)。

  • 上层通过虚拟 PSM 调用 L2CA_ConnectLECocReq 发起连接,协议栈内部映射到真实 PSM。

通过 PSM 管理、回调绑定和资源分配,为上层协议(如 EATT、音频流)提供可靠的底层传输支持。

L2CA_AllocateLePSM
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
/* Constants for LE Dynamic PSM values */
#define LE_DYNAMIC_PSM_START 0x0080
#define LE_DYNAMIC_PSM_END 0x00FF
#define LE_DYNAMIC_PSM_RANGE (LE_DYNAMIC_PSM_END - LE_DYNAMIC_PSM_START + 1)/********************************************************************************* Function         L2CA_AllocateLePSM** Description      To find an unused LE PSM for L2CAP services.** Returns          LE_PSM to use if success. Otherwise returns 0.*******************************************************************************/
uint16_t L2CA_AllocateLePSM(void) {bool done = false;uint16_t psm = l2cb.le_dyn_psm;  // 从上次分配的 PSM 位置开始uint16_t count = 0;log::verbose("last psm={}", psm);while (!done) {count++;if (count > LE_DYNAMIC_PSM_RANGE) { // 限制最大尝试次数log::error("Out of free BLE PSM");return 0;}// 环形 PSM 递增与回绕psm++;if (psm > LE_DYNAMIC_PSM_END) {psm = LE_DYNAMIC_PSM_START; // 超出范围则回绕到起始点}}// 标记数组验证if (!l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START]) {/* make sure the newly allocated psm is not used right now */if (l2cu_find_ble_rcb_by_psm(psm)) { // 检查 PSM 是否已被注册控制块使用log::warn("supposedly-free PSM={} have allocated rcb!", psm);continue; // 若已被 RCB 使用,跳过当前 PSM}// 分配 PSM 并更新状态l2cb.le_dyn_psm_assigned[psm - LE_DYNAMIC_PSM_START] = true; // 标记为已分配log::verbose("assigned PSM={}", psm);done = true;break;}}l2cb.le_dyn_psm = psm; // 更新上次分配位置,优化下次查找return (psm);
}

在动态 PSM 范围内(LE_DYNAMIC_PSM_STARTLE_DYNAMIC_PSM_END)查找并分配一个未被使用的 PSM,为上层服务(如客户端模式的 LE CoC 连接)提供动态端口管理。

动态 PSM 的用途

蓝牙 LE CoC(低功耗面向连接通道)的 PSM 分为两类:

  • 固定 PSM(0x0001-0x007F:用于标准服务(如 EATT 的 0x2B),由蓝牙 SIG 预定义。

  • 动态 PSM(0x0080-0xFFFF:用于自定义服务或客户端模式(仅发起连接,不监听请求),由协议栈动态分配。

四、GATT 初始化流程图 (gatt_init)

五、EATT 启动时序图 (EattExtension::Start)

六、总结

gatt_init是 GATT 模块运行的 “起点”,通过多步骤协同初始化,实现了以下核心价值:

  • 状态一致性:通过清空控制块、重置连接管理器,确保模块初始状态无残留数据,避免运行时逻辑异常。

  • 协议互操作性:通过 L2CAP 固定通道注册(BLE)和动态 PSM 注册(BR/EDR),建立与底层协议的标准化交互接口,保障不同设备间的 GATT 通信兼容性。

  • 服务可管理性:通过句柄规划、服务链表和标准属性数据库构建,实现对 ATT 数据库的高效管理,支撑上层应用对服务的发现、访问与修改。

  • 功能扩展性:通过 EATT 扩展激活,为蓝牙设备提供大 MTU、长操作等高级能力,满足音视频流、大数据传输等新兴场景需求。


http://www.xdnf.cn/news/12014.html

相关文章:

  • DrissionPage爬虫包实战分享
  • 汽车加气站操作工证考试重点
  • 文献阅读|基于PSMA PET/CT与mpMRI多模态深度学习预测前列腺癌的不良病变
  • Spring AI 之工具调用
  • cpp多线程学习
  • 无人机光纤FC接口模块技术分析
  • [华为eNSP] 在eNSP上实现IPv4地址以及IPv4静态路由的配置
  • 融智学的数学基础,通过微分几何的纤维丛结构,构建理论框架模型包含生物层、动物层、心智层、人造物层和人格层五个维度
  • C++算法训练营 Day7 哈希表及双指针
  • 聊聊FlaUI:让Windows UI自动化测试优雅起飞!
  • Deepin 安装 Nginx
  • (eNSP)配置WDS手拉手业务
  • .NET 生态中的 MCP 项目及技术分析
  • 那些Java 线程中断的实现方式
  • 单锁与分布式锁
  • AI工程师的武器库:核心技术与实战案例
  • MCP与检索增强生成(RAG):AI应用的强大组合
  • 《Coevolutionary computation and its application》协同演化及其应用中文对照·第一章
  • 告别无效号码,精准营销从空号过滤开始
  • 固定ip和非固定ip的区别是什么?如何固定ip地址
  • Lifecycle 核心原理面试回答
  • DeepSwiftSeek 开源软件 |用于 DeepSeek LLM 模型的 Swift 客户端 |轻量级和高效的 DeepSeek 核心功能通信
  • FTPS、HTTPS、SMTPS以及WebSockets over TLS的概念及其应用场景
  • 部署SD-WAN与现有网络架构的兼容性分析:如何实现平滑集成与避免设备浪费?
  • 夏普比率(Sharpe ratio)​
  • SSL安全证书怎么安装?
  • 数据湖是什么?数据湖和数据仓库的区别是什么?
  • 记一次运行spark报错
  • 【Linux】编译器gcc/g++及其库的详细介绍
  • NumPy 2.x 完全指南【二十四】结构化数组