Skip to content

Commit ab7bb7d

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 03b7050 commit ab7bb7d

File tree

8 files changed

+316
-0
lines changed

8 files changed

+316
-0
lines changed

include/zephyr/bluetooth/conn.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2688,6 +2688,28 @@ 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.
2695+
*
2696+
* @return Zero on success or (negative) error code on failure.
2697+
* @return -ENOBUFS HCI command buffer is not available.
2698+
* @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection
2699+
*/
2700+
int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role);
2701+
2702+
/** @brief Enable/disable role switch of the connection by setting the connection's link policy.
2703+
*
2704+
* @param conn Connection object.
2705+
* @param enable Value allowing/disallowing controller to be role switchable.
2706+
*
2707+
* @return Zero on success or (negative) error code on failure.
2708+
* @return -ENOBUFS HCI command buffer is not available.
2709+
* @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection.
2710+
*/
2711+
int bt_conn_br_set_role_switchable(const struct bt_conn *conn, bool enable);
2712+
26912713
#ifdef __cplusplus
26922714
}
26932715
#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 0x01
569+
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_HOLD_MODE 0x02
570+
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_SNIFF_SWITCH 0x04
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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ config BT_COD
508508
consult the following link:
509509
https://www.bluetooth.com/specifications/assigned-numbers
510510

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

513518
endmenu

subsys/bluetooth/host/classic/br.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,11 @@ 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 bt_hci_cp_write_default_link_policy_settings *policy_cp;
806+
struct net_buf *rsp;
804807
int err;
808+
uint16_t default_link_policy_settings;
805809

806810
/* Read extended local features */
807811
if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
@@ -907,6 +911,42 @@ int bt_br_init(void)
907911
}
908912
}
909913

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

subsys/bluetooth/host/classic/conn_br.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,105 @@ 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 = NULL;
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, uint16_t *link_policy_settings)
202+
{
203+
int err;
204+
struct net_buf *buf = NULL;
205+
struct bt_hci_cp_read_link_policy_settings *cp;
206+
struct bt_hci_rp_read_link_policy_settings *rp;
207+
struct net_buf *rsp;
208+
209+
buf = bt_hci_cmd_create(BT_HCI_OP_READ_LINK_POLICY_SETTINGS, sizeof(*cp));
210+
if (!buf) {
211+
return -ENOBUFS;
212+
}
213+
214+
cp = net_buf_add(buf, sizeof(*cp));
215+
cp->handle = conn->handle;
216+
217+
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LINK_POLICY_SETTINGS, buf, &rsp);
218+
if (!err) {
219+
rp = (void *)rsp->data;
220+
*link_policy_settings = rp->link_policy_settings;
221+
}
222+
223+
return err;
224+
}
225+
226+
static int bt_conn_br_write_link_policy_settings(const struct bt_conn *conn, uint16_t link_policy_settings)
227+
{
228+
struct net_buf *buf = NULL;
229+
struct bt_hci_cp_write_link_policy_settings *cp;
230+
231+
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS, sizeof(*cp));
232+
if (!buf) {
233+
return -ENOBUFS;
234+
}
235+
236+
cp = net_buf_add(buf, sizeof(*cp));
237+
cp->handle = conn->handle;
238+
cp->link_policy_settings = link_policy_settings;
239+
240+
return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS, buf, NULL);
241+
}
242+
243+
int bt_conn_br_set_role_switchable(const struct bt_conn *conn, bool enable)
244+
{
245+
int err;
246+
uint16_t link_policy_settings;
247+
bool is_enabled;
248+
249+
CHECKIF(conn == NULL) {
250+
LOG_DBG("conn is NULL");
251+
return -EINVAL;
252+
}
253+
254+
if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
255+
LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
256+
return -EINVAL;
257+
}
258+
259+
err = bt_conn_br_read_link_policy_settings(conn, &link_policy_settings);
260+
if (err) {
261+
return err;
262+
}
263+
264+
is_enabled = link_policy_settings & BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;
265+
266+
if (enable == is_enabled) {
267+
return 0;
268+
}
269+
270+
link_policy_settings ^= BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;
271+
err = bt_conn_br_write_link_policy_settings(conn, link_policy_settings);
272+
273+
return err;
274+
}

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_switchable(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)