Skip to content
Merged
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
36 changes: 36 additions & 0 deletions include/zephyr/bluetooth/conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,18 @@ struct bt_conn_cb {
struct bt_conn_remote_info *remote_info);
#endif /* defined(CONFIG_BT_REMOTE_INFO) */

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
/** @brief The connection mode change
*
* This callback notifies the application that the sniff mode has changed
*
* @param conn Connection object.
* @param mode Active/Sniff mode.
* @param interval Sniff interval.
*/
void (*br_mode_changed)(struct bt_conn *conn, uint8_t mode, uint16_t interval);
#endif /* CONFIG_BT_POWER_MODE_CONTROL */

#if defined(CONFIG_BT_USER_PHY_UPDATE)
/** @brief The PHY of the connection has changed.
*
Expand Down Expand Up @@ -2944,6 +2956,30 @@ int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role);
*/
int bt_conn_br_set_role_switch_enable(const struct bt_conn *conn, bool enable);

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
/** @brief bluetooth conn check and enter sniff mode
*
* This function is used to identify which ACL link connection is to
* be placed in Sniff mode
*
* @param conn bt_conn conn
* @param min_interval Minimum sniff interval.
* @param max_interval Maxmum sniff interval.
* @param attempt Number of Baseband receive slots for sniff attempt.
* @param timeout Number of Baseband receive slots for sniff timeout.
*/
int bt_conn_br_enter_sniff_mode(struct bt_conn *conn, uint16_t min_interval,
uint16_t max_interval, uint16_t attempt, uint16_t timeout);

/** @brief bluetooth conn check and exit sniff mode
*
* @param conn bt_conn conn
*
* @return Zero for success, non-zero otherwise.
*/
int bt_conn_br_exit_sniff_mode(struct bt_conn *conn);
#endif /* CONFIG_BT_POWER_MODE_CONTROL */

#ifdef __cplusplus
}
#endif
Expand Down
27 changes: 27 additions & 0 deletions include/zephyr/bluetooth/hci_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,20 @@ struct bt_hci_cp_write_default_link_policy_settings {
uint16_t default_link_policy_settings;
} __packed;

#define BT_HCI_OP_SNIFF_MODE BT_OP(BT_OGF_LINK_POLICY, 0x0003) /* 0x0803 */
struct bt_hci_cp_sniff_mode {
uint16_t handle;
uint16_t max_interval;
uint16_t min_interval;
uint16_t attempt;
uint16_t timeout;
} __packed;

#define BT_HCI_OP_EXIT_SNIFF_MODE BT_OP(BT_OGF_LINK_POLICY, 0x0004) /* 0x0804 */
struct bt_hci_cp_exit_sniff_mode {
uint16_t handle;
} __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 Expand Up @@ -2963,6 +2977,19 @@ struct bt_hci_evt_num_completed_packets {
struct bt_hci_handle_count h[0];
} __packed;

/* Current mode */
#define BT_ACTIVE_MODE 0x00
#define BT_HOLD_MODE 0x01
#define BT_SNIFF_MODE 0x02

#define BT_HCI_EVT_MODE_CHANGE 0x14
struct bt_hci_evt_mode_change {
uint8_t status;
uint16_t handle;
uint8_t mode;
uint16_t interval;
} __packed;

#define BT_HCI_EVT_PIN_CODE_REQ 0x16
struct bt_hci_evt_pin_code_req {
bt_addr_t bdaddr;
Expand Down
7 changes: 7 additions & 0 deletions subsys/bluetooth/host/classic/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,13 @@ config BT_DEFAULT_ROLE_SWITCH_ENABLE
This option sets the controller's default link policy to
enable/disable the role switch.

config BT_POWER_MODE_CONTROL
bool "Bluetooth Power Mode Control(Active/Sniff)"
help
This option enables the power mode control feature. This feature
allows the application to control the power mode of the Bluetooth
controller.

config BT_GOEP
bool "Bluetooth GOEP Profile [EXPERIMENTAL]"
select BT_RFCOMM
Expand Down
34 changes: 34 additions & 0 deletions subsys/bluetooth/host/classic/br.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,40 @@ void bt_hci_role_change(struct net_buf *buf)
bt_conn_unref(conn);
}

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
void bt_hci_link_mode_change(struct net_buf *buf)
{
struct bt_hci_evt_mode_change *evt = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(evt->handle);
uint16_t interval = sys_le16_to_cpu(evt->interval);
struct bt_conn *conn;

conn = bt_conn_lookup_handle(handle, BT_CONN_TYPE_BR);
if (!conn) {
LOG_ERR("Can't find conn for handle 0x%x", handle);
return;
}

if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("Invalid state %d", conn->state);
bt_conn_unref(conn);
return;
}

if (evt->status) {
LOG_ERR("Error %d, type %d", evt->status, conn->type);
bt_conn_unref(conn);
return;
}

LOG_DBG("hdl 0x%x mode %d intervel %d", handle, evt->mode, interval);

conn->br.mode = evt->mode;
bt_conn_notify_mode_changed(conn, evt->mode, interval);
bt_conn_unref(conn);
}
#endif /* CONFIG_BT_POWER_MODE_CONTROL */

static int read_ext_features(void)
{
int i;
Expand Down
53 changes: 53 additions & 0 deletions subsys/bluetooth/host/classic/shell/bredr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,54 @@ static int cmd_set_role_switchable(const struct shell *sh, size_t argc, char *ar
return 0;
}

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
static int cmd_set_sniff_mode(const struct shell *sh, size_t argc, char *argv[])
{
const char *action;
int err = 0;

action = argv[1];
if (!default_conn) {
shell_print(sh, "Not connected");
return -ENOEXEC;
}

if (!strcmp(action, "on")) {
uint16_t min_interval;
uint16_t max_interval;
uint16_t attempt;
uint16_t timeout;

min_interval = atoi(argv[2]);
max_interval = atoi(argv[3]);
attempt = atoi(argv[4]);
timeout = atoi(argv[5]);
err = bt_conn_br_enter_sniff_mode(default_conn, min_interval, max_interval, attempt,
timeout);
if (err) {
shell_print(sh, "request enter sniff mode, err:%d", err);
} else {
shell_print(sh,
"request enter sniff mode, min_interval:%d, max_interval:%d, "
"attempt:%d, timeout:%d",
min_interval, max_interval, attempt, timeout);
}
} else if (!strcmp(action, "off")) {
err = bt_conn_br_exit_sniff_mode(default_conn);
if (err) {
shell_print(sh, "request enter active mode, err:%d", err);
} else {
shell_print(sh, "request enter active mode success");
}
} else {
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}

return 0;
}
#endif

#if defined(CONFIG_BT_L2CAP_CONNLESS)
static void connless_recv(struct bt_conn *conn, uint16_t psm, struct net_buf *buf)
{
Expand Down Expand Up @@ -1578,6 +1626,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds,
SHELL_CMD_ARG(switch-role, NULL, "<value: central, peripheral>", cmd_switch_role, 2, 0),
SHELL_CMD_ARG(set-role-switchable, NULL, "<value: enable, disable>",
cmd_set_role_switchable, 2, 0),
#if defined(CONFIG_BT_POWER_MODE_CONTROL)
SHELL_CMD_ARG(set_sniff_mode, NULL,
"<value:on, off> [min_interval] [max_interval] [attempt] [timeout]",
cmd_set_sniff_mode, 2, 4),
#endif
SHELL_SUBCMD_SET_END
);

Expand Down
91 changes: 91 additions & 0 deletions subsys/bluetooth/host/conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,10 @@ struct bt_conn *bt_conn_add_br(const bt_addr_t *peer)
conn->get_and_clear_cb = acl_get_and_clear_cb;
conn->has_data = acl_has_data;

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
conn->br.mode = BT_ACTIVE_MODE;
#endif /* CONFIG_BT_POWER_MODE_CONTROL */

return conn;
}
#endif /* CONFIG_BT_CLASSIC */
Expand Down Expand Up @@ -4402,6 +4406,93 @@ void bt_hci_le_df_cte_req_failed(struct net_buf *buf)
}
#endif /* CONFIG_BT_DF_CONNECTION_CTE_REQ */

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
int bt_conn_br_enter_sniff_mode(struct bt_conn *conn, uint16_t min_interval, uint16_t max_interval,
uint16_t attempt, uint16_t timeout)
{
struct bt_hci_cp_sniff_mode *cp;
struct net_buf *buf;

if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
return -EINVAL;
}

if (conn->state != BT_CONN_CONNECTED) {
return -ENOTCONN;
}

if (conn->br.mode == BT_SNIFF_MODE) {
return -EBUSY;
}

/* Check if the parameters are valid */
if (min_interval < 0x0006 || min_interval > 0x0540 || max_interval < 0x0006 ||
max_interval > 0x0540 || min_interval > max_interval || attempt == 0 ||
attempt > 0x01F3 || timeout > 0x0028) {
return -EINVAL;
}

buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}

cp = net_buf_add(buf, sizeof(*cp));
cp->handle = sys_cpu_to_le16(conn->handle);
cp->max_interval = sys_cpu_to_le16(max_interval);
cp->min_interval = sys_cpu_to_le16(min_interval);
cp->attempt = sys_cpu_to_le16(attempt);
cp->timeout = sys_cpu_to_le16(timeout);

return bt_hci_cmd_send_sync(BT_HCI_OP_SNIFF_MODE, buf, NULL);
}

int bt_conn_br_exit_sniff_mode(struct bt_conn *conn)
{
struct bt_hci_cp_exit_sniff_mode *cp;
struct net_buf *buf;

if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
return -EINVAL;
}

if (conn->state != BT_CONN_CONNECTED) {
return -ENOTCONN;
}

if (conn->br.mode == BT_ACTIVE_MODE) {
return -EBUSY;
}

buf = bt_hci_cmd_alloc(K_FOREVER);
if (!buf) {
return -ENOBUFS;
}

cp = net_buf_add(buf, sizeof(*cp));
cp->handle = sys_cpu_to_le16(conn->handle);

return bt_hci_cmd_send_sync(BT_HCI_OP_EXIT_SNIFF_MODE, buf, NULL);
}

void bt_conn_notify_mode_changed(struct bt_conn *conn, uint8_t mode, uint16_t interval)
{
struct bt_conn_cb *callback;

SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) {
if (callback->br_mode_changed) {
callback->br_mode_changed(conn, mode, interval);
}
}

STRUCT_SECTION_FOREACH(bt_conn_cb, cb) {
if (cb->br_mode_changed) {
cb->br_mode_changed(conn, mode, interval);
}
}
}

#endif /* CONFIG_BT_POWER_MODE_CONTROL */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_CONN_TX_NOTIFY_WQ)
Expand Down
10 changes: 10 additions & 0 deletions subsys/bluetooth/host/conn_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ struct bt_conn_br {
uint8_t features[LMP_MAX_PAGES][8];

struct bt_keys_link_key *link_key;

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
/* For power mode */
uint8_t mode;
#endif /* CONFIG_BT_POWER_MODE_CONTROL */
};

struct bt_conn_sco {
Expand Down Expand Up @@ -535,6 +540,11 @@ void bt_conn_identity_resolved(struct bt_conn *conn);
void bt_conn_security_changed(struct bt_conn *conn, uint8_t hci_err,
enum bt_security_err err);

#if defined(CONFIG_BT_POWER_MODE_CONTROL)
/* Notify higher layers that connection sniff mode changed */
void bt_conn_notify_mode_changed(struct bt_conn *conn, uint8_t mode, uint16_t interval);
#endif /* CONFIG_BT_POWER_MODE_CONTROL */

/* Prepare a PDU to be sent over a connection */
#if defined(CONFIG_NET_BUF_LOG)
struct net_buf *bt_conn_create_pdu_timeout_debug(struct net_buf_pool *pool,
Expand Down
4 changes: 4 additions & 0 deletions subsys/bluetooth/host/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3088,6 +3088,10 @@ static const struct event_handler normal_events[] = {
sizeof(struct bt_hci_evt_remote_ext_features)),
EVENT_HANDLER(BT_HCI_EVT_ROLE_CHANGE, bt_hci_role_change,
sizeof(struct bt_hci_evt_role_change)),
#if defined(CONFIG_BT_POWER_MODE_CONTROL)
EVENT_HANDLER(BT_HCI_EVT_MODE_CHANGE, bt_hci_link_mode_change,
sizeof(struct bt_hci_evt_mode_change)),
#endif /* CONFIG_BT_POWER_MODE_CONTROL */
EVENT_HANDLER(BT_HCI_EVT_SYNC_CONN_COMPLETE, bt_hci_synchronous_conn_complete,
sizeof(struct bt_hci_evt_sync_conn_complete)),
#endif /* CONFIG_BT_CLASSIC */
Expand Down
3 changes: 3 additions & 0 deletions subsys/bluetooth/host/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,9 @@ void bt_hci_remote_name_request_complete(struct net_buf *buf);
void bt_hci_read_remote_features_complete(struct net_buf *buf);
void bt_hci_read_remote_ext_features_complete(struct net_buf *buf);
void bt_hci_role_change(struct net_buf *buf);
#if defined(CONFIG_BT_POWER_MODE_CONTROL)
void bt_hci_link_mode_change(struct net_buf *buf);
#endif /* CONFIG_BT_POWER_MODE_CONTROL */
void bt_hci_synchronous_conn_complete(struct net_buf *buf);

void bt_hci_le_df_connection_iq_report(struct net_buf *buf);
Expand Down