Skip to content

Commit 0dacc61

Browse files
bluetooth: classic: add role switch API
add bt_conn_br_switch_role and bt_conn_br_set_role_switchable to control the role switch, add DEFAULT_ROLE_SWITCHABLE Kconfig to control the default role switch state. Signed-off-by: Mark Wang <[email protected]>
1 parent 5fba281 commit 0dacc61

File tree

8 files changed

+313
-0
lines changed

8 files changed

+313
-0
lines changed

include/zephyr/bluetooth/conn.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2688,6 +2688,29 @@ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer);
26882688
*/
26892689
const bt_addr_t *bt_conn_get_dst_br(const struct bt_conn *conn);
26902690

2691+
/** @brief Change the role of the conn.
2692+
*
2693+
* @param conn Connection object.
2694+
* @param role The role that want to switch as, the value is @ref BT_HCI_ROLE_CENTRAL and
2695+
* @ref BT_HCI_ROLE_PERIPHERAL from hci_types.h.
2696+
*
2697+
* @return Zero on success or (negative) error code on failure.
2698+
* @return -ENOBUFS HCI command buffer is not available.
2699+
* @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection
2700+
*/
2701+
int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role);
2702+
2703+
/** @brief Enable/disable role switch of the connection by setting the connection's link policy.
2704+
*
2705+
* @param conn Connection object.
2706+
* @param enable Value enable/disable role switch of controller.
2707+
*
2708+
* @return Zero on success or (negative) error code on failure.
2709+
* @return -ENOBUFS HCI command buffer is not available.
2710+
* @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection.
2711+
*/
2712+
int bt_conn_br_set_role_switch_enable(const struct bt_conn *conn, bool enable);
2713+
26912714
#ifdef __cplusplus
26922715
}
26932716
#endif

include/zephyr/bluetooth/hci_types.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ struct bt_hci_cmd_hdr {
363363

364364
/* OpCode Group Fields */
365365
#define BT_OGF_LINK_CTRL 0x01
366+
#define BT_OGF_LINK_POLICY 0x02
366367
#define BT_OGF_BASEBAND 0x03
367368
#define BT_OGF_INFO 0x04
368369
#define BT_OGF_STATUS 0x05
@@ -558,6 +559,43 @@ struct bt_hci_cp_io_capability_neg_reply {
558559
uint8_t reason;
559560
} __packed;
560561

562+
#define BT_HCI_OP_SWITCH_ROLE BT_OP(BT_OGF_LINK_POLICY, 0x000b)
563+
struct bt_hci_cp_switch_role {
564+
bt_addr_t bdaddr;
565+
uint8_t role;
566+
} __packed;
567+
568+
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH BIT(0)
569+
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_HOLD_MODE BIT(1)
570+
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_SNIFF_SWITCH BIT(2)
571+
572+
#define BT_HCI_OP_READ_LINK_POLICY_SETTINGS BT_OP(BT_OGF_LINK_POLICY, 0x000c)
573+
struct bt_hci_cp_read_link_policy_settings {
574+
uint16_t handle;
575+
} __packed;
576+
struct bt_hci_rp_read_link_policy_settings {
577+
uint8_t status;
578+
uint16_t handle;
579+
uint16_t link_policy_settings;
580+
} __packed;
581+
582+
#define BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS BT_OP(BT_OGF_LINK_POLICY, 0x000d)
583+
struct bt_hci_cp_write_link_policy_settings {
584+
uint16_t handle;
585+
uint16_t link_policy_settings;
586+
} __packed;
587+
588+
#define BT_HCI_OP_READ_DEFAULT_LINK_POLICY_SETTINGS BT_OP(BT_OGF_LINK_POLICY, 0x000e)
589+
struct bt_hci_rp_read_default_link_policy_settings {
590+
uint8_t status;
591+
uint16_t default_link_policy_settings;
592+
} __packed;
593+
594+
#define BT_HCI_OP_WRITE_DEFAULT_LINK_POLICY_SETTINGS BT_OP(BT_OGF_LINK_POLICY, 0x000f)
595+
struct bt_hci_cp_write_default_link_policy_settings {
596+
uint16_t default_link_policy_settings;
597+
} __packed;
598+
561599
#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001) /* 0x0c01 */
562600
struct bt_hci_cp_set_event_mask {
563601
uint8_t events[8];

subsys/bluetooth/host/classic/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,12 @@ config BT_COD
508508
consult the following link:
509509
https://www.bluetooth.com/specifications/assigned-numbers
510510

511+
config BT_DEFAULT_ROLE_SWITCH_ENABLE
512+
bool "Default Bluetooth Role Switch Enable/Disable State"
513+
help
514+
This option sets the controller's default link policy to
515+
enable/disable the role switch.
516+
511517
endif # BT_CLASSIC
512518

513519
endmenu

subsys/bluetooth/host/classic/br.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,10 @@ int bt_br_init(void)
801801
struct bt_hci_cp_write_inquiry_mode *inq_cp;
802802
struct bt_hci_write_local_name *name_cp;
803803
struct bt_hci_cp_write_class_of_device *cod;
804+
struct bt_hci_rp_read_default_link_policy_settings *rp;
805+
struct net_buf *rsp;
804806
int err;
807+
uint16_t default_link_policy_settings;
805808

806809
/* Read extended local features */
807810
if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
@@ -907,6 +910,39 @@ int bt_br_init(void)
907910
}
908911
}
909912

913+
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_DEFAULT_LINK_POLICY_SETTINGS, NULL, &rsp);
914+
if (err) {
915+
return err;
916+
}
917+
918+
rp = (void *)rsp->data;
919+
default_link_policy_settings = rp->default_link_policy_settings;
920+
921+
bool should_enable = IS_ENABLED(CONFIG_BT_DEFAULT_ROLE_SWITCH_ENABLE);
922+
bool is_enabled = (default_link_policy_settings &
923+
BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH);
924+
925+
/* Enable/Disable the default role switch */
926+
if (should_enable != is_enabled) {
927+
struct bt_hci_cp_write_default_link_policy_settings *policy_cp;
928+
929+
default_link_policy_settings ^= BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;
930+
931+
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_DEFAULT_LINK_POLICY_SETTINGS,
932+
sizeof(*policy_cp));
933+
if (!buf) {
934+
return -ENOBUFS;
935+
}
936+
937+
policy_cp = net_buf_add(buf, sizeof(*policy_cp));
938+
policy_cp->default_link_policy_settings = default_link_policy_settings;
939+
940+
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_DEFAULT_LINK_POLICY_SETTINGS, buf, NULL);
941+
if (err) {
942+
return err;
943+
}
944+
}
945+
910946
return 0;
911947
}
912948

subsys/bluetooth/host/classic/conn_br.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,104 @@ void bt_br_acl_recv(struct bt_conn *conn, struct net_buf *buf, bool complete)
170170

171171
net_buf_unref(buf);
172172
}
173+
174+
int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role)
175+
{
176+
struct net_buf *buf;
177+
struct bt_hci_cp_switch_role *cp;
178+
179+
CHECKIF(conn == NULL) {
180+
LOG_DBG("conn is NULL");
181+
return -EINVAL;
182+
}
183+
184+
if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
185+
LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
186+
return -EINVAL;
187+
}
188+
189+
buf = bt_hci_cmd_create(BT_HCI_OP_SWITCH_ROLE, sizeof(*cp));
190+
if (!buf) {
191+
return -ENOBUFS;
192+
}
193+
194+
cp = net_buf_add(buf, sizeof(*cp));
195+
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
196+
cp->role = role;
197+
198+
return bt_hci_cmd_send_sync(BT_HCI_OP_SWITCH_ROLE, buf, NULL);
199+
}
200+
201+
static int bt_conn_br_read_link_policy_settings(const struct bt_conn *conn,
202+
uint16_t *link_policy_settings)
203+
{
204+
int err;
205+
struct net_buf *buf;
206+
struct bt_hci_cp_read_link_policy_settings *cp;
207+
struct bt_hci_rp_read_link_policy_settings *rp;
208+
struct net_buf *rsp;
209+
210+
buf = bt_hci_cmd_create(BT_HCI_OP_READ_LINK_POLICY_SETTINGS, sizeof(*cp));
211+
if (!buf) {
212+
return -ENOBUFS;
213+
}
214+
215+
cp = net_buf_add(buf, sizeof(*cp));
216+
cp->handle = sys_cpu_to_le16(conn->handle);
217+
218+
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LINK_POLICY_SETTINGS, buf, &rsp);
219+
if (!err) {
220+
rp = (void *)rsp->data;
221+
*link_policy_settings = rp->link_policy_settings;
222+
}
223+
224+
return err;
225+
}
226+
227+
static int bt_conn_br_write_link_policy_settings(const struct bt_conn *conn,
228+
uint16_t link_policy_settings)
229+
{
230+
struct net_buf *buf;
231+
struct bt_hci_cp_write_link_policy_settings *cp;
232+
233+
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS, sizeof(*cp));
234+
if (!buf) {
235+
return -ENOBUFS;
236+
}
237+
238+
cp = net_buf_add(buf, sizeof(*cp));
239+
cp->handle = sys_cpu_to_le16(conn->handle);
240+
cp->link_policy_settings = link_policy_settings;
241+
242+
return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS, buf, NULL);
243+
}
244+
245+
int bt_conn_br_set_role_switch_enable(const struct bt_conn *conn, bool enable)
246+
{
247+
int err;
248+
uint16_t link_policy_settings;
249+
bool is_enabled;
250+
251+
CHECKIF(conn == NULL) {
252+
LOG_DBG("conn is NULL");
253+
return -EINVAL;
254+
}
255+
256+
if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
257+
LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
258+
return -EINVAL;
259+
}
260+
261+
err = bt_conn_br_read_link_policy_settings(conn, &link_policy_settings);
262+
if (err) {
263+
return err;
264+
}
265+
266+
is_enabled = (link_policy_settings & BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH);
267+
if (enable == is_enabled) {
268+
return 0;
269+
}
270+
271+
link_policy_settings ^= BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;
272+
return bt_conn_br_write_link_policy_settings(conn, link_policy_settings);
273+
}

subsys/bluetooth/host/classic/shell/bredr.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,86 @@ static int cmd_info(const struct shell *sh, size_t argc, char *argv[])
12071207
return err;
12081208
}
12091209

1210+
void role_changed(struct bt_conn *conn, uint8_t status)
1211+
{
1212+
struct bt_conn_info info;
1213+
int err;
1214+
1215+
bt_shell_print("Role changed (HCI status 0x%02x)", status);
1216+
1217+
err = bt_conn_get_info(conn, &info);
1218+
if (err) {
1219+
bt_shell_print("Failed to get info");
1220+
return;
1221+
}
1222+
1223+
bt_shell_print("Current role is: %s", get_conn_role_str(info.role));
1224+
}
1225+
1226+
static int cmd_switch_role(const struct shell *sh, size_t argc, char *argv[])
1227+
{
1228+
int err;
1229+
const char *action;
1230+
uint8_t role;
1231+
1232+
if (!default_conn) {
1233+
shell_print(sh, "Not connected");
1234+
return -ENOEXEC;
1235+
}
1236+
1237+
action = argv[1];
1238+
1239+
if (!strcmp(action, "central")) {
1240+
role = BT_HCI_ROLE_CENTRAL;
1241+
} else if (!strcmp(action, "peripheral")) {
1242+
role = BT_HCI_ROLE_PERIPHERAL;
1243+
} else {
1244+
shell_help(sh);
1245+
return SHELL_CMD_HELP_PRINTED;
1246+
}
1247+
1248+
err = bt_conn_br_switch_role(default_conn, role);
1249+
1250+
if (err) {
1251+
shell_error(sh, "fail to change role (err %d)", err);
1252+
}
1253+
1254+
return 0;
1255+
}
1256+
1257+
static int cmd_set_role_switchable(const struct shell *sh, size_t argc, char *argv[])
1258+
{
1259+
int err;
1260+
const char *action;
1261+
bool enable;
1262+
1263+
if (!default_conn) {
1264+
shell_print(sh, "Not connected");
1265+
return -ENOEXEC;
1266+
}
1267+
1268+
action = argv[1];
1269+
1270+
if (!strcmp(action, "enable")) {
1271+
enable = true;
1272+
} else if (!strcmp(action, "disable")) {
1273+
enable = false;
1274+
} else {
1275+
shell_help(sh);
1276+
return SHELL_CMD_HELP_PRINTED;
1277+
}
1278+
1279+
err = bt_conn_br_set_role_switch_enable(default_conn, enable);
1280+
1281+
if (err) {
1282+
shell_error(sh, "fail to set role switchable (err %d)", err);
1283+
} else {
1284+
shell_print(sh, "success");
1285+
}
1286+
1287+
return 0;
1288+
}
1289+
12101290
static int cmd_default_handler(const struct shell *sh, size_t argc, char **argv)
12111291
{
12121292
if (argc == 1) {
@@ -1270,6 +1350,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds,
12701350
SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0),
12711351
SHELL_CMD_ARG(pscan, NULL, "<value: on, off>", cmd_connectable, 2, 0),
12721352
SHELL_CMD_ARG(sdp-find, NULL, "<HFPAG, HFPHF>", cmd_sdp_find_record, 2, 0),
1353+
SHELL_CMD_ARG(switch-role, NULL, "<value: central, peripheral>", cmd_switch_role, 2, 0),
1354+
SHELL_CMD_ARG(set-role-switchable, NULL, "<value: enable, disable>",
1355+
cmd_set_role_switchable, 2, 0),
12731356
SHELL_SUBCMD_SET_END
12741357
);
12751358

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/** @file
2+
* @brief Bluetooth shell functions
3+
*
4+
* This is not to be included by the application.
5+
*/
6+
7+
/*
8+
* Copyright 2025 NXP
9+
*
10+
* SPDX-License-Identifier: Apache-2.0
11+
*/
12+
13+
#ifndef __BREDR_H
14+
#define __BREDR_H
15+
#include <stddef.h>
16+
#include <stdint.h>
17+
18+
void role_changed(struct bt_conn *conn, uint8_t status);
19+
20+
#endif /* __BREDR_H */

subsys/bluetooth/host/shell/bt.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
#endif /* CONFIG_BT_LL_SW_SPLIT */
5555
#include "host/shell/bt.h"
5656
#include "mesh/shell/hci.h"
57+
#if defined(CONFIG_BT_CLASSIC)
58+
#include "host/classic/shell/bredr.h"
59+
#endif
5760

5861
static bool no_settings_load;
5962

@@ -1193,6 +1196,9 @@ static struct bt_conn_cb conn_callbacks = {
11931196
.le_cs_config_complete = le_cs_config_created,
11941197
.le_cs_config_removed = le_cs_config_removed,
11951198
#endif
1199+
#if defined(CONFIG_BT_CLASSIC)
1200+
.role_changed = role_changed,
1201+
#endif
11961202
};
11971203
#endif /* CONFIG_BT_CONN */
11981204

0 commit comments

Comments
 (0)