Skip to content

Commit 6f2b8f8

Browse files
lylezhu2012nashif
authored andcommitted
Bluetooth: Classic: L2CAP: implement ECHO REQ/RSP
Handle the ECHO request/response of classic L2CAP signaling packets. Add the functions `bt_l2cap_br_echo_cb_register()` and `bt_l2cap_br_echo_cb_unregister()` to register/unregister the ECHO callbacks to monitor the ECHO REQ and RSP. Add the function `bt_l2cap_br_echo_req()` to send the ECHO REQ through classic L2CAP signaling channel. Add the function `bt_l2cap_br_echo_rsp()` to reply the ECHO REQ through the classic L2CAP signaling channel. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
1 parent 02f2bea commit 6f2b8f8

File tree

3 files changed

+331
-0
lines changed

3 files changed

+331
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/** @file
2+
* @brief Bluetooth L2CAP BR/EDR handling
3+
*/
4+
5+
/*
6+
* Copyright 2025 NXP
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*/
10+
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_BR_H_
11+
#define ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_BR_H_
12+
13+
/**
14+
* @brief L2CAP
15+
* @defgroup bt_l2cap L2CAP
16+
* @ingroup bluetooth
17+
* @{
18+
*/
19+
20+
#include <stddef.h>
21+
#include <stdint.h>
22+
23+
#include <zephyr/bluetooth/buf.h>
24+
#include <zephyr/bluetooth/conn.h>
25+
#include <zephyr/bluetooth/hci.h>
26+
#include <zephyr/kernel.h>
27+
#include <zephyr/net_buf.h>
28+
#include <zephyr/sys/atomic.h>
29+
#include <zephyr/sys/slist.h>
30+
#include <zephyr/sys/util.h>
31+
#include <sys/types.h>
32+
33+
#ifdef __cplusplus
34+
extern "C" {
35+
#endif
36+
37+
/**
38+
* @brief ECHO request/response callback structure.
39+
*
40+
* This structure is used for tracking the ECHO request/response signaling packets of L2CAP BR.
41+
* It is registered with the help of the bt_l2cap_br_echo_cb_register() API.
42+
* It's permissible to register multiple instances of this @ref bt_l2cap_br_echo_cb type, in case
43+
* different modules of an application are interested in tracking the ECHO request/response
44+
* signaling packets. If a callback is not of interest for an instance, it may be set to NULL and
45+
* will as a consequence not be used for that instance.
46+
*/
47+
struct bt_l2cap_br_echo_cb {
48+
/**
49+
* @brief A ECHO request has been received.
50+
*
51+
* This callback notifies the application of a ECHO request has been received.
52+
* The ECHO response should be performed by calling the bt_l2cap_br_echo_rsp() API.
53+
*
54+
* @param conn The ACL connection object.
55+
* @param identifier The identifier of the ECHO request.
56+
* @param buf Received ECHO data.
57+
*/
58+
void (*req)(struct bt_conn *conn, uint8_t identifier, struct net_buf *buf);
59+
60+
/**
61+
* @brief A ECHO response has been received.
62+
*
63+
* This callback notifies the application of a ECHO response has been received.
64+
*
65+
* @param conn The ACL connection object.
66+
* @param buf Received ECHO data.
67+
*/
68+
void (*rsp)(struct bt_conn *conn, struct net_buf *buf);
69+
70+
/** @internal Internally used field for list handling */
71+
sys_snode_t _node;
72+
};
73+
74+
/**
75+
* @brief Register ECHO callbacks.
76+
*
77+
* Register callbacks to monitor the packets of L2CAP BR echo request/response.
78+
*
79+
* @param cb Callback struct. Must point to memory that remains valid.
80+
*
81+
* @retval 0 Success.
82+
* @retval -EEXIST if @p cb was already registered.
83+
*/
84+
int bt_l2cap_br_echo_cb_register(struct bt_l2cap_br_echo_cb *cb);
85+
86+
/**
87+
* @brief Unregister ECHO callbacks.
88+
*
89+
* Unregister callbacks that are used to monitor the packets of L2CAP BR echo request/response.
90+
*
91+
* @param cb Callback struct point to memory that remains valid.
92+
*
93+
* @retval 0 Success.
94+
* @retval -EINVAL If @p cb is NULL.
95+
* @retval -ENOENT if @p cb was not registered.
96+
*/
97+
int bt_l2cap_br_echo_cb_unregister(struct bt_l2cap_br_echo_cb *cb);
98+
99+
/**
100+
* @brief Headroom needed for outgoing L2CAP ECHO REQ PDUs.
101+
*/
102+
#define BT_L2CAP_BR_ECHO_REQ_RESERVE BT_L2CAP_BUF_SIZE(4)
103+
104+
/**
105+
* @brief Headroom needed for outgoing L2CAP ECHO RSP PDUs.
106+
*/
107+
#define BT_L2CAP_BR_ECHO_RSP_RESERVE BT_L2CAP_BUF_SIZE(4)
108+
109+
/**
110+
* @brief Send ECHO data through ECHO request
111+
*
112+
* Send ECHO data through ECHO request. The application is required to have reserved
113+
* @ref BT_L2CAP_BR_ECHO_REQ_RESERVE bytes in the buffer before sending.
114+
*
115+
* @param conn The ACL connection object.
116+
* @param buf Sending ECHO data.
117+
*
118+
* @return 0 in case of success or negative value in case of error.
119+
*/
120+
int bt_l2cap_br_echo_req(struct bt_conn *conn, struct net_buf *buf);
121+
122+
/**
123+
* @brief Send ECHO data through ECHO response
124+
*
125+
* Send ECHO data through ECHO response. The application is required to have reserved
126+
* @ref BT_L2CAP_BR_ECHO_RSP_RESERVE bytes in the buffer before sending.
127+
*
128+
* @param conn The ACL connection object.
129+
* @param identifier The identifier of the ECHO request.
130+
* @param buf Sending ECHO data.
131+
*
132+
* @return 0 in case of success or negative value in case of error.
133+
*/
134+
int bt_l2cap_br_echo_rsp(struct bt_conn *conn, uint8_t identifier, struct net_buf *buf);
135+
136+
#ifdef __cplusplus
137+
}
138+
#endif
139+
140+
/**
141+
* @}
142+
*/
143+
144+
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_BR_H_ */

subsys/bluetooth/host/classic/l2cap_br.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
#include <errno.h>
1212
#include <zephyr/sys/atomic.h>
1313
#include <zephyr/sys/byteorder.h>
14+
#include <zephyr/sys/check.h>
1415
#include <zephyr/sys/iterable_sections.h>
1516
#include <zephyr/sys/util.h>
1617
#include <zephyr/sys/crc.h>
1718

1819
#include <zephyr/bluetooth/hci.h>
1920
#include <zephyr/bluetooth/bluetooth.h>
2021
#include <zephyr/bluetooth/conn.h>
22+
#include <zephyr/bluetooth/classic/l2cap_br.h>
2123

2224
#include "host/buf_view.h"
2325
#include "host/hci_core.h"
@@ -75,6 +77,7 @@ LOG_MODULE_REGISTER(bt_l2cap_br, CONFIG_BT_L2CAP_LOG_LEVEL);
7577
#define L2CAP_BR_CFG_TIMEOUT K_SECONDS(4)
7678
#define L2CAP_BR_DISCONN_TIMEOUT K_SECONDS(1)
7779
#define L2CAP_BR_CONN_TIMEOUT K_SECONDS(40)
80+
#define L2CAP_BR_ECHO_TIMEOUT K_SECONDS(30)
7881

7982
#define L2CAP_FEAT_FC_MASK BIT(0)
8083
#define L2CAP_FEAT_RET_MASK BIT(1)
@@ -228,6 +231,8 @@ struct bt_l2cap_br {
228231

229232
static struct bt_l2cap_br bt_l2cap_br_pool[CONFIG_BT_MAX_CONN];
230233

234+
static sys_slist_t bt_l2cap_br_echo_cbs = SYS_SLIST_STATIC_INIT(&bt_l2cap_br_echo_cbs);
235+
231236
struct bt_l2cap_chan *bt_l2cap_br_lookup_rx_cid(struct bt_conn *conn,
232237
uint16_t cid)
233238
{
@@ -343,6 +348,7 @@ static void l2cap_br_rtx_timeout(struct k_work *work)
343348
if (chan->rx.cid == BT_L2CAP_CID_BR_SIG) {
344349
LOG_DBG("Skip BR/EDR signalling channel ");
345350
atomic_clear_bit(chan->flags, L2CAP_FLAG_SIG_INFO_PENDING);
351+
chan->ident = 0;
346352
return;
347353
}
348354

@@ -4521,6 +4527,7 @@ static void l2cap_br_disconnected(struct bt_l2cap_chan *chan)
45214527
* so this should always succeed.
45224528
*/
45234529
(void)k_work_cancel_delayable(&br_chan->rtx_work);
4530+
br_chan->ident = 0;
45244531
}
45254532
}
45264533

@@ -4820,6 +4827,40 @@ static void l2cap_br_reject_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, struct
48204827
} while (chan != NULL);
48214828
}
48224829

4830+
static void l2cap_br_echo_req(struct bt_l2cap_br *l2cap, uint8_t ident, struct net_buf *buf)
4831+
{
4832+
struct bt_conn *conn = l2cap->chan.chan.conn;
4833+
struct bt_l2cap_br_echo_cb *callback;
4834+
4835+
SYS_SLIST_FOR_EACH_CONTAINER(&bt_l2cap_br_echo_cbs, callback, _node) {
4836+
if (callback->req) {
4837+
callback->req(conn, ident, buf);
4838+
}
4839+
}
4840+
}
4841+
4842+
static void l2cap_br_echo_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, struct net_buf *buf)
4843+
{
4844+
struct bt_conn *conn = l2cap->chan.chan.conn;
4845+
struct bt_l2cap_br_echo_cb *callback;
4846+
4847+
if (ident != l2cap->chan.ident) {
4848+
LOG_WRN("ident mismatch (%u != %u)!", l2cap->chan.ident, ident);
4849+
goto failed;
4850+
}
4851+
4852+
SYS_SLIST_FOR_EACH_CONTAINER(&bt_l2cap_br_echo_cbs, callback, _node) {
4853+
if (callback->rsp) {
4854+
callback->rsp(conn, buf);
4855+
}
4856+
}
4857+
4858+
failed:
4859+
l2cap->chan.ident = 0;
4860+
/* Release RTX work since got the response */
4861+
k_work_cancel_delayable(&l2cap->chan.rtx_work);
4862+
}
4863+
48234864
static void l2cap_br_sig_handle(struct bt_l2cap_br *l2cap, struct bt_l2cap_sig_hdr *hdr,
48244865
struct net_buf *buf)
48254866
{
@@ -4858,6 +4899,12 @@ static void l2cap_br_sig_handle(struct bt_l2cap_br *l2cap, struct bt_l2cap_sig_h
48584899
case BT_L2CAP_CMD_REJECT:
48594900
l2cap_br_reject_rsp(l2cap, hdr->ident, buf);
48604901
break;
4902+
case BT_L2CAP_ECHO_REQ:
4903+
l2cap_br_echo_req(l2cap, hdr->ident, buf);
4904+
break;
4905+
case BT_L2CAP_ECHO_RSP:
4906+
l2cap_br_echo_rsp(l2cap, hdr->ident, buf);
4907+
break;
48614908
default:
48624909
LOG_WRN("Unknown/Unsupported L2CAP PDU code 0x%02x", hdr->code);
48634910
l2cap_br_send_reject(l2cap->chan.chan.conn, hdr->ident, BT_L2CAP_REJ_NOT_UNDERSTOOD,
@@ -6003,3 +6050,133 @@ void bt_l2cap_br_init(void)
60036050
bt_avrcp_init();
60046051
}
60056052
}
6053+
6054+
int bt_l2cap_br_echo_cb_register(struct bt_l2cap_br_echo_cb *cb)
6055+
{
6056+
CHECKIF(cb == NULL) {
6057+
return -EINVAL;
6058+
}
6059+
6060+
if (sys_slist_find(&bt_l2cap_br_echo_cbs, &cb->_node, NULL)) {
6061+
return -EEXIST;
6062+
}
6063+
6064+
sys_slist_append(&bt_l2cap_br_echo_cbs, &cb->_node);
6065+
6066+
return 0;
6067+
}
6068+
6069+
int bt_l2cap_br_echo_cb_unregister(struct bt_l2cap_br_echo_cb *cb)
6070+
{
6071+
CHECKIF(cb == NULL) {
6072+
return -EINVAL;
6073+
}
6074+
6075+
if (!sys_slist_find_and_remove(&bt_l2cap_br_echo_cbs, &cb->_node)) {
6076+
return -ENOENT;
6077+
}
6078+
6079+
return 0;
6080+
}
6081+
6082+
int bt_l2cap_br_echo_req(struct bt_conn *conn, struct net_buf *buf)
6083+
{
6084+
struct bt_l2cap_chan *chan;
6085+
struct bt_l2cap_sig_hdr *hdr;
6086+
int err;
6087+
6088+
if ((conn == NULL) || (buf == NULL)) {
6089+
return -EINVAL;
6090+
}
6091+
6092+
LOG_DBG("ACL conn %p buf %p len %u", conn, buf, buf->len);
6093+
6094+
if (buf->ref != 1) {
6095+
LOG_WRN("Expecting 1 ref, got %d", buf->ref);
6096+
return -EINVAL;
6097+
}
6098+
6099+
if (buf->len >= (L2CAP_BR_MIN_MTU - sizeof(*hdr))) {
6100+
LOG_ERR("attempt to send %u bytes on %u MTU chan", buf->len, L2CAP_BR_MIN_MTU);
6101+
return -EMSGSIZE;
6102+
}
6103+
6104+
if (net_buf_headroom(buf) < BT_L2CAP_BR_ECHO_REQ_RESERVE) {
6105+
/* Call `net_buf_reserve(buf, BT_L2CAP_BR_ECHO_REQ_RESERVE)`
6106+
* when allocating buffers intended for bt_l2cap_br_echo_req().
6107+
*/
6108+
LOG_ERR("Not enough headroom in buf %p", buf);
6109+
return -EINVAL;
6110+
}
6111+
6112+
chan = bt_l2cap_br_lookup_rx_cid(conn, BT_L2CAP_CID_BR_SIG);
6113+
if (chan == NULL) {
6114+
LOG_ERR("Signaling Channel %u not found", BT_L2CAP_CID_BR_SIG);
6115+
return -ENOTCONN;
6116+
}
6117+
6118+
if (BR_CHAN(chan)->ident) {
6119+
LOG_ERR("Waiting for ECHO RSP");
6120+
return -EBUSY;
6121+
}
6122+
6123+
hdr = net_buf_push(buf, sizeof(*hdr));
6124+
6125+
hdr->code = BT_L2CAP_ECHO_REQ;
6126+
hdr->ident = l2cap_br_get_ident();
6127+
hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr));
6128+
6129+
/* Set the ident for the signaling request */
6130+
BR_CHAN(chan)->ident = hdr->ident;
6131+
6132+
err = bt_l2cap_br_send_cb(conn, BT_L2CAP_CID_BR_SIG, buf, NULL, NULL);
6133+
if (err == 0) {
6134+
k_work_reschedule(&BR_CHAN(chan)->rtx_work, L2CAP_BR_ECHO_TIMEOUT);
6135+
}
6136+
6137+
return err;
6138+
}
6139+
6140+
int bt_l2cap_br_echo_rsp(struct bt_conn *conn, uint8_t identifier, struct net_buf *buf)
6141+
{
6142+
struct bt_l2cap_chan *chan;
6143+
struct bt_l2cap_sig_hdr *hdr;
6144+
6145+
if ((conn == NULL) || (buf == NULL) || (identifier == 0)) {
6146+
return -EINVAL;
6147+
}
6148+
6149+
LOG_DBG("ACL conn %p buf %p len %u", conn, buf, buf->len);
6150+
6151+
if (buf->ref != 1) {
6152+
LOG_WRN("Expecting 1 ref, got %d", buf->ref);
6153+
return -EINVAL;
6154+
}
6155+
6156+
if (buf->len >= (L2CAP_BR_MIN_MTU - sizeof(*hdr))) {
6157+
LOG_ERR("attempt to send %u bytes on %u MTU chan", buf->len, L2CAP_BR_MIN_MTU);
6158+
return -EMSGSIZE;
6159+
}
6160+
6161+
if (net_buf_headroom(buf) < BT_L2CAP_BR_ECHO_RSP_RESERVE) {
6162+
/* Call `net_buf_reserve(buf, BT_L2CAP_BR_ECHO_RSP_RESERVE)`
6163+
* when allocating buffers intended for bt_l2cap_br_echo_rsp().
6164+
*/
6165+
LOG_ERR("Not enough headroom in buf %p", buf);
6166+
return -EINVAL;
6167+
}
6168+
6169+
chan = bt_l2cap_br_lookup_rx_cid(conn, BT_L2CAP_CID_BR_SIG);
6170+
if (chan == NULL) {
6171+
LOG_ERR("Signaling Channel %u not found", BT_L2CAP_CID_BR_SIG);
6172+
return -ENOTCONN;
6173+
}
6174+
6175+
hdr = net_buf_push(buf, sizeof(*hdr));
6176+
6177+
hdr->code = BT_L2CAP_ECHO_RSP;
6178+
hdr->ident = identifier;
6179+
hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr));
6180+
6181+
return bt_l2cap_br_send_cb(conn, BT_L2CAP_CID_BR_SIG, buf, NULL, NULL);
6182+
}

subsys/bluetooth/host/classic/l2cap_br_internal.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@ struct bt_l2cap_disconn_rsp {
174174
uint16_t scid;
175175
} __packed;
176176

177+
#define BT_L2CAP_ECHO_REQ 0x08
178+
struct bt_l2cap_echo_req {
179+
uint8_t data[0];
180+
} __packed;
181+
182+
#define BT_L2CAP_ECHO_RSP 0x09
183+
struct bt_l2cap_echo_rsp {
184+
uint8_t data[0];
185+
} __packed;
186+
177187
#define BT_L2CAP_INFO_FEAT_MASK 0x0002
178188
#define BT_L2CAP_INFO_FIXED_CHAN 0x0003
179189

0 commit comments

Comments
 (0)