Commit e71b5122 authored by Mark Wang's avatar Mark Wang Committed by Alberto Escolar
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: default avatarMark Wang <yichang.wang@nxp.com>
parent 31fba83f
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -2702,6 +2702,29 @@ struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer);
 */
const bt_addr_t *bt_conn_get_dst_br(const struct bt_conn *conn);

/** @brief Change the role of the conn.
 *
 *  @param conn Connection object.
 *  @param role The role that want to switch as, the value is @ref BT_HCI_ROLE_CENTRAL and
 *              @ref BT_HCI_ROLE_PERIPHERAL from hci_types.h.
 *
 *  @return Zero on success or (negative) error code on failure.
 *  @return -ENOBUFS HCI command buffer is not available.
 *  @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection
 */
int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role);

/** @brief Enable/disable role switch of the connection by setting the connection's link policy.
 *
 *  @param conn Connection object.
 *  @param enable Value enable/disable role switch of controller.
 *
 *  @return Zero on success or (negative) error code on failure.
 *  @return -ENOBUFS HCI command buffer is not available.
 *  @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_BR connection.
 */
int bt_conn_br_set_role_switch_enable(const struct bt_conn *conn, bool enable);

#ifdef __cplusplus
}
#endif
+38 −0
Original line number Diff line number Diff line
@@ -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
@@ -558,6 +559,43 @@ struct bt_hci_cp_io_capability_neg_reply {
	uint8_t   reason;
} __packed;

#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_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH  BIT(0)
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_HOLD_MODE    BIT(1)
#define BT_HCI_LINK_POLICY_SETTINGS_ENABLE_SNIFF_SWITCH BIT(2)

#define BT_HCI_OP_READ_LINK_POLICY_SETTINGS     BT_OP(BT_OGF_LINK_POLICY, 0x000c)
struct bt_hci_cp_read_link_policy_settings {
	uint16_t handle;
} __packed;
struct bt_hci_rp_read_link_policy_settings {
	uint8_t  status;
	uint16_t handle;
	uint16_t link_policy_settings;
} __packed;

#define BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS    BT_OP(BT_OGF_LINK_POLICY, 0x000d)
struct bt_hci_cp_write_link_policy_settings {
	uint16_t handle;
	uint16_t link_policy_settings;
} __packed;

#define BT_HCI_OP_READ_DEFAULT_LINK_POLICY_SETTINGS  BT_OP(BT_OGF_LINK_POLICY, 0x000e)
struct bt_hci_rp_read_default_link_policy_settings {
	uint8_t  status;
	uint16_t default_link_policy_settings;
} __packed;

#define BT_HCI_OP_WRITE_DEFAULT_LINK_POLICY_SETTINGS BT_OP(BT_OGF_LINK_POLICY, 0x000f)
struct bt_hci_cp_write_default_link_policy_settings {
	uint16_t default_link_policy_settings;
} __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];
+6 −0
Original line number Diff line number Diff line
@@ -508,6 +508,12 @@ config BT_COD
	  consult the following link:
	  https://www.bluetooth.com/specifications/assigned-numbers

config BT_DEFAULT_ROLE_SWITCH_ENABLE
	bool "Default Bluetooth Role Switch Enable/Disable State"
	help
	  This option sets the controller's default link policy to
	  enable/disable the role switch.

endif # BT_CLASSIC

endmenu
+35 −0
Original line number Diff line number Diff line
@@ -800,7 +800,10 @@ int bt_br_init(void)
	struct bt_hci_cp_write_ssp_mode *ssp_cp;
	struct bt_hci_cp_write_inquiry_mode *inq_cp;
	struct bt_hci_write_local_name *name_cp;
	struct bt_hci_rp_read_default_link_policy_settings *rp;
	struct net_buf *rsp;
	int err;
	uint16_t default_link_policy_settings;

	/* Read extended local features */
	if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
@@ -906,6 +909,38 @@ int bt_br_init(void)
		}
	}

	err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_DEFAULT_LINK_POLICY_SETTINGS, NULL, &rsp);
	if (err) {
		return err;
	}

	rp = (void *)rsp->data;
	default_link_policy_settings = rp->default_link_policy_settings;

	bool should_enable = IS_ENABLED(CONFIG_BT_DEFAULT_ROLE_SWITCH_ENABLE);
	bool is_enabled = (default_link_policy_settings &
			   BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH);

	/* Enable/Disable the default role switch */
	if (should_enable != is_enabled) {
		struct bt_hci_cp_write_default_link_policy_settings *policy_cp;

		default_link_policy_settings ^= BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;

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

		policy_cp = net_buf_add(buf, sizeof(*policy_cp));
		policy_cp->default_link_policy_settings = default_link_policy_settings;

		err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_DEFAULT_LINK_POLICY_SETTINGS, buf, NULL);
		if (err) {
			return err;
		}
	}

	return 0;
}

+101 −0
Original line number Diff line number Diff line
@@ -170,3 +170,104 @@ void bt_br_acl_recv(struct bt_conn *conn, struct net_buf *buf, bool complete)

	net_buf_unref(buf);
}

int bt_conn_br_switch_role(const struct bt_conn *conn, uint8_t role)
{
	struct net_buf *buf;
	struct bt_hci_cp_switch_role *cp;

	CHECKIF(conn == NULL) {
		LOG_DBG("conn is NULL");
		return -EINVAL;
	}

	if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
		LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
		return -EINVAL;
	}

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

	cp = net_buf_add(buf, sizeof(*cp));
	bt_addr_copy(&cp->bdaddr, &conn->br.dst);
	cp->role = role;

	return bt_hci_cmd_send_sync(BT_HCI_OP_SWITCH_ROLE, buf, NULL);
}

static int bt_conn_br_read_link_policy_settings(const struct bt_conn *conn,
						uint16_t *link_policy_settings)
{
	int err;
	struct net_buf *buf;
	struct bt_hci_cp_read_link_policy_settings *cp;
	struct bt_hci_rp_read_link_policy_settings *rp;
	struct net_buf *rsp;

	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);

	err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LINK_POLICY_SETTINGS, buf, &rsp);
	if (!err) {
		rp = (void *)rsp->data;
		*link_policy_settings = rp->link_policy_settings;
	}

	return err;
}

static int bt_conn_br_write_link_policy_settings(const struct bt_conn *conn,
						 uint16_t link_policy_settings)
{
	struct net_buf *buf;
	struct bt_hci_cp_write_link_policy_settings *cp;

	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->link_policy_settings = link_policy_settings;

	return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LINK_POLICY_SETTINGS, buf, NULL);
}

int bt_conn_br_set_role_switch_enable(const struct bt_conn *conn, bool enable)
{
	int err;
	uint16_t link_policy_settings;
	bool is_enabled;

	CHECKIF(conn == NULL) {
		LOG_DBG("conn is NULL");
		return -EINVAL;
	}

	if (!bt_conn_is_type(conn, BT_CONN_TYPE_BR)) {
		LOG_DBG("Invalid connection type: %u for %p", conn->type, conn);
		return -EINVAL;
	}

	err = bt_conn_br_read_link_policy_settings(conn, &link_policy_settings);
	if (err) {
		return err;
	}

	is_enabled = (link_policy_settings & BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH);
	if (enable == is_enabled) {
		return 0;
	}

	link_policy_settings ^= BT_HCI_LINK_POLICY_SETTINGS_ENABLE_ROLE_SWITCH;
	return bt_conn_br_write_link_policy_settings(conn, link_policy_settings);
}
Loading