Skip to content

Feature/add role change cb #90126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/zephyr/bluetooth/conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,18 @@ struct bt_conn_cb {

#endif

#if defined(CONFIG_BT_CLASSIC)
/** @brief The role of the connection has changed.
*
* This callback notifies the application that the role of the
* connection has changed.
*
* @param conn Connection object.
* @param status The HCI_Role_Change event's Status.
*/
void (*role_changed)(struct bt_conn *conn, uint8_t status);
#endif

/** @internal Internally used field for list handling */
sys_snode_t _node;
};
Expand Down
9 changes: 9 additions & 0 deletions include/zephyr/bluetooth/hci_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ struct bt_hci_cmd_hdr {

/* OpCode Group Fields */
#define BT_OGF_LINK_CTRL 0x01
#define BT_OGF_LINK_POLICY 0x02
#define BT_OGF_BASEBAND 0x03
#define BT_OGF_INFO 0x04
#define BT_OGF_STATUS 0x05
Expand Down Expand Up @@ -558,6 +559,14 @@ struct bt_hci_cp_io_capability_neg_reply {
uint8_t reason;
} __packed;

#define BT_HCI_SWITCH_ROLE_CENTRAL 0
#define BT_HCI_SWITCH_ROLE_PERIPHERAL 1
#define BT_HCI_OP_SWITCH_ROLE BT_OP(BT_OGF_LINK_POLICY, 0x000b)
struct bt_hci_cp_switch_role {
bt_addr_t bdaddr;
uint8_t role;
} __packed;

#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001) /* 0x0c01 */
struct bt_hci_cp_set_event_mask {
uint8_t events[8];
Expand Down
16 changes: 8 additions & 8 deletions subsys/bluetooth/host/classic/br.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,22 +681,22 @@ void bt_hci_role_change(struct net_buf *buf)

LOG_DBG("status 0x%02x role %u addr %s", evt->status, evt->role, bt_addr_str(&evt->bdaddr));

if (evt->status) {
return;
}

conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
LOG_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}

if (evt->role) {
conn->role = BT_CONN_ROLE_PERIPHERAL;
} else {
conn->role = BT_CONN_ROLE_CENTRAL;
if (!evt->status) {
if (evt->role) {
conn->role = BT_CONN_ROLE_PERIPHERAL;
} else {
conn->role = BT_CONN_ROLE_CENTRAL;
}
}

bt_conn_role_changed(conn, evt->status);

bt_conn_unref(conn);
}

Expand Down
74 changes: 74 additions & 0 deletions subsys/bluetooth/host/classic/shell/bredr.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

/*
* Copyright (c) 2018 Intel Corporation
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -989,6 +990,78 @@ static int cmd_clear(const struct shell *sh, size_t argc, char *argv[])
return err;
}

static const char *get_conn_role_str(uint8_t role)
{
switch (role) {
case BT_CONN_ROLE_CENTRAL: return "central";
case BT_CONN_ROLE_PERIPHERAL: return "peripheral";
default: return "Invalid";
}
}

void role_changed(struct bt_conn *conn, uint8_t status)
{
struct bt_conn_info info;
int err;

bt_shell_print("Role changed (HCI status 0x%02x)", status);

err = bt_conn_get_info(conn, &info);
if (err) {
bt_shell_print("Failed to get info");
return;
}

bt_shell_print("Role: %s", get_conn_role_str(info.role));
}

static int cmd_switch_role(const struct shell *sh, size_t argc, char *argv[])
{
struct net_buf *buf = NULL;
struct bt_hci_cp_switch_role *cp;
const bt_addr_t *dest;
const char *action;
int err;
uint8_t role;

if (!default_conn) {
shell_print(sh, "Not connected");
return -ENOEXEC;
}

action = argv[1];

if (!strcmp(action, "central")) {
role = BT_HCI_SWITCH_ROLE_CENTRAL;
} else if (!strcmp(action, "peripheral")) {
role = BT_HCI_SWITCH_ROLE_PERIPHERAL;
} else {
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}

dest = bt_conn_get_dst_br(default_conn);
if (!dest) {
shell_error(sh, "Failed to get br addr");
return -EINVAL;
}

buf = bt_hci_cmd_create(BT_HCI_OP_SWITCH_ROLE, sizeof(*cp));
if (buf != NULL) {
cp = net_buf_add(buf, sizeof(*cp));
memcpy(&cp->bdaddr, &dest->val[0], sizeof(cp->bdaddr));
cp->role = role;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the new role is the same as the old one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my test, the controller gives error for the result.

err = bt_hci_cmd_send_sync(BT_HCI_OP_SWITCH_ROLE, buf, NULL);
if (err) {
shell_print(sh, "fail to send hci cmd (err %d)", err);
}
} else {
shell_error(sh, "hi cmd fail to create");
}

return 0;
}

static int cmd_default_handler(const struct shell *sh, size_t argc, char **argv)
{
if (argc == 1) {
Expand Down Expand Up @@ -1041,6 +1114,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds,
SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0),
SHELL_CMD_ARG(pscan, NULL, "<value: on, off>", cmd_connectable, 2, 0),
SHELL_CMD_ARG(sdp-find, NULL, "<HFPAG, HFPHF>", cmd_sdp_find_record, 2, 0),
SHELL_CMD_ARG(switch-role, NULL, "<central, peripheral>", cmd_switch_role, 2, 0),
SHELL_SUBCMD_SET_END
);

Expand Down
20 changes: 20 additions & 0 deletions subsys/bluetooth/host/classic/shell/bredr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @file
* @brief Bluetooth shell functions
*
* This is not to be included by the application.
*/

/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef __BREDR_H
#define __BREDR_H
#include <stddef.h>
#include <stdint.h>

void role_changed(struct bt_conn *conn, uint8_t status);

#endif /* __BREDR_H */
19 changes: 19 additions & 0 deletions subsys/bluetooth/host/conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,25 @@ void bt_conn_connected(struct bt_conn *conn)
notify_connected(conn);
}

#if defined(CONFIG_BT_CLASSIC)
void bt_conn_role_changed(struct bt_conn *conn, uint8_t status)
{
struct bt_conn_cb *callback;

SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) {
if (callback->role_changed) {
callback->role_changed(conn, status);
}
}

STRUCT_SECTION_FOREACH(bt_conn_cb, cb) {
if (cb->role_changed) {
cb->role_changed(conn, status);
}
}
}
#endif

static int conn_disconnect(struct bt_conn *conn, uint8_t reason)
{
int err;
Expand Down
4 changes: 4 additions & 0 deletions subsys/bluetooth/host/conn_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@ void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state);

void bt_conn_connected(struct bt_conn *conn);

#if defined(CONFIG_BT_CLASSIC)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As my understand, function declarations do not need to be controlled by conditional compilation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void bt_conn_role_changed(struct bt_conn *conn, uint8_t status);
#endif

int bt_conn_le_conn_update(struct bt_conn *conn,
const struct bt_le_conn_param *param);

Expand Down
6 changes: 6 additions & 0 deletions subsys/bluetooth/host/shell/bt.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
#endif /* CONFIG_BT_LL_SW_SPLIT */
#include "host/shell/bt.h"
#include "mesh/shell/hci.h"
#if defined(CONFIG_BT_CLASSIC)
#include "host/classic/shell/bredr.h"
#endif

static bool no_settings_load;

Expand Down Expand Up @@ -1182,6 +1185,9 @@ static struct bt_conn_cb conn_callbacks = {
.le_cs_config_complete = le_cs_config_created,
.le_cs_config_removed = le_cs_config_removed,
#endif
#if defined(CONFIG_BT_CLASSIC)
.role_changed = role_changed,
#endif
};
#endif /* CONFIG_BT_CONN */

Expand Down