zephyr/subsys/bluetooth/host/br.c
Carles Cufi 1325edff48 Bluetooth: hci: Align terms with the Bluetooth v5.3 spec
The new inclusive naming terminology changes in v5.3 of the Bluetooth
specification affect the HCI layer, so apply all relevant changes to
align with it.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
2021-09-17 16:05:01 +02:00

1091 lines
23 KiB
C

/*
* Copyright (c) 2017-2021 Nordic Semiconductor ASA
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/byteorder.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/buf.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_MODULE_NAME bt_br
#include "common/log.h"
#include "hci_core.h"
#include "conn_internal.h"
#include "keys.h"
static bt_br_discovery_cb_t *discovery_cb;
struct bt_br_discovery_result *discovery_results;
static size_t discovery_results_size;
static size_t discovery_results_count;
static int reject_conn(const bt_addr_t *bdaddr, uint8_t reason)
{
struct bt_hci_cp_reject_conn_req *cp;
struct net_buf *buf;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_REJECT_CONN_REQ, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
cp->reason = reason;
err = bt_hci_cmd_send_sync(BT_HCI_OP_REJECT_CONN_REQ, buf, NULL);
if (err) {
return err;
}
return 0;
}
static int accept_sco_conn(const bt_addr_t *bdaddr, struct bt_conn *sco_conn)
{
struct bt_hci_cp_accept_sync_conn_req *cp;
struct net_buf *buf;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
cp->pkt_type = sco_conn->sco.pkt_type;
cp->tx_bandwidth = 0x00001f40;
cp->rx_bandwidth = 0x00001f40;
cp->max_latency = 0x0007;
cp->retrans_effort = 0x01;
cp->content_format = BT_VOICE_CVSD_16BIT;
err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, buf, NULL);
if (err) {
return err;
}
return 0;
}
static int accept_conn(const bt_addr_t *bdaddr)
{
struct bt_hci_cp_accept_conn_req *cp;
struct net_buf *buf;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_CONN_REQ, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, bdaddr);
cp->role = BT_HCI_ROLE_PERIPHERAL;
err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_CONN_REQ, buf, NULL);
if (err) {
return err;
}
return 0;
}
static void bt_esco_conn_req(struct bt_hci_evt_conn_request *evt)
{
struct bt_conn *sco_conn;
sco_conn = bt_conn_add_sco(&evt->bdaddr, evt->link_type);
if (!sco_conn) {
reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES);
return;
}
if (accept_sco_conn(&evt->bdaddr, sco_conn)) {
BT_ERR("Error accepting connection from %s",
bt_addr_str(&evt->bdaddr));
reject_conn(&evt->bdaddr, BT_HCI_ERR_UNSPECIFIED);
bt_sco_cleanup(sco_conn);
return;
}
sco_conn->role = BT_HCI_ROLE_PERIPHERAL;
bt_conn_set_state(sco_conn, BT_CONN_CONNECT);
bt_conn_unref(sco_conn);
}
void bt_hci_conn_req(struct net_buf *buf)
{
struct bt_hci_evt_conn_request *evt = (void *)buf->data;
struct bt_conn *conn;
BT_DBG("conn req from %s, type 0x%02x", bt_addr_str(&evt->bdaddr),
evt->link_type);
if (evt->link_type != BT_HCI_ACL) {
bt_esco_conn_req(evt);
return;
}
conn = bt_conn_add_br(&evt->bdaddr);
if (!conn) {
reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES);
return;
}
accept_conn(&evt->bdaddr);
conn->role = BT_HCI_ROLE_PERIPHERAL;
bt_conn_set_state(conn, BT_CONN_CONNECT);
bt_conn_unref(conn);
}
static bool br_sufficient_key_size(struct bt_conn *conn)
{
struct bt_hci_cp_read_encryption_key_size *cp;
struct bt_hci_rp_read_encryption_key_size *rp;
struct net_buf *buf, *rsp;
uint8_t key_size;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE,
sizeof(*cp));
if (!buf) {
BT_ERR("Failed to allocate command buffer");
return false;
}
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_ENCRYPTION_KEY_SIZE,
buf, &rsp);
if (err) {
BT_ERR("Failed to read encryption key size (err %d)", err);
return false;
}
if (rsp->len < sizeof(*rp)) {
BT_ERR("Too small command complete for encryption key size");
net_buf_unref(rsp);
return false;
}
rp = (void *)rsp->data;
key_size = rp->key_size;
net_buf_unref(rsp);
BT_DBG("Encryption key size is %u", key_size);
if (conn->sec_level == BT_SECURITY_L4) {
return key_size == BT_HCI_ENCRYPTION_KEY_SIZE_MAX;
}
return key_size >= BT_HCI_ENCRYPTION_KEY_SIZE_MIN;
}
bool bt_br_update_sec_level(struct bt_conn *conn)
{
if (!conn->encrypt) {
conn->sec_level = BT_SECURITY_L1;
return true;
}
if (conn->br.link_key) {
if (conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) {
if (conn->encrypt == 0x02) {
conn->sec_level = BT_SECURITY_L4;
} else {
conn->sec_level = BT_SECURITY_L3;
}
} else {
conn->sec_level = BT_SECURITY_L2;
}
} else {
BT_WARN("No BR/EDR link key found");
conn->sec_level = BT_SECURITY_L2;
}
if (!br_sufficient_key_size(conn)) {
BT_ERR("Encryption key size is not sufficient");
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
return false;
}
if (conn->required_sec_level > conn->sec_level) {
BT_ERR("Failed to set required security level");
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
return false;
}
return true;
}
void bt_hci_synchronous_conn_complete(struct net_buf *buf)
{
struct bt_hci_evt_sync_conn_complete *evt = (void *)buf->data;
struct bt_conn *sco_conn;
uint16_t handle = sys_le16_to_cpu(evt->handle);
BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle,
evt->link_type);
sco_conn = bt_conn_lookup_addr_sco(&evt->bdaddr);
if (!sco_conn) {
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
if (evt->status) {
sco_conn->err = evt->status;
bt_conn_set_state(sco_conn, BT_CONN_DISCONNECTED);
bt_conn_unref(sco_conn);
return;
}
sco_conn->handle = handle;
bt_conn_set_state(sco_conn, BT_CONN_CONNECTED);
bt_conn_unref(sco_conn);
}
void bt_hci_conn_complete(struct net_buf *buf)
{
struct bt_hci_evt_conn_complete *evt = (void *)buf->data;
struct bt_conn *conn;
struct bt_hci_cp_read_remote_features *cp;
uint16_t handle = sys_le16_to_cpu(evt->handle);
BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle,
evt->link_type);
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
if (!conn) {
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
return;
}
if (evt->status) {
conn->err = evt->status;
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
bt_conn_unref(conn);
return;
}
conn->handle = handle;
conn->err = 0U;
conn->encrypt = evt->encr_enabled;
if (!bt_br_update_sec_level(conn)) {
bt_conn_unref(conn);
return;
}
bt_conn_set_state(conn, BT_CONN_CONNECTED);
bt_conn_unref(conn);
buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_FEATURES, sizeof(*cp));
if (!buf) {
return;
}
cp = net_buf_add(buf, sizeof(*cp));
cp->handle = evt->handle;
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL);
}
struct discovery_priv {
uint16_t clock_offset;
uint8_t pscan_rep_mode;
uint8_t resolving;
} __packed;
static int request_name(const bt_addr_t *addr, uint8_t pscan, uint16_t offset)
{
struct bt_hci_cp_remote_name_request *cp;
struct net_buf *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_REQUEST, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, addr);
cp->pscan_rep_mode = pscan;
cp->reserved = 0x00; /* reserver, should be set to 0x00 */
cp->clock_offset = offset;
return bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_REQUEST, buf, NULL);
}
#define EIR_SHORT_NAME 0x08
#define EIR_COMPLETE_NAME 0x09
static bool eir_has_name(const uint8_t *eir)
{
int len = 240;
while (len) {
if (len < 2) {
break;
}
/* Look for early termination */
if (!eir[0]) {
break;
}
/* Check if field length is correct */
if (eir[0] > len - 1) {
break;
}
switch (eir[1]) {
case EIR_SHORT_NAME:
case EIR_COMPLETE_NAME:
if (eir[0] > 1) {
return true;
}
break;
default:
break;
}
/* Parse next AD Structure */
len -= eir[0] + 1;
eir += eir[0] + 1;
}
return false;
}
void bt_br_discovery_reset(void)
{
discovery_cb = NULL;
discovery_results = NULL;
discovery_results_size = 0;
discovery_results_count = 0;
}
static void report_discovery_results(void)
{
bool resolving_names = false;
int i;
for (i = 0; i < discovery_results_count; i++) {
struct discovery_priv *priv;
priv = (struct discovery_priv *)&discovery_results[i]._priv;
if (eir_has_name(discovery_results[i].eir)) {
continue;
}
if (request_name(&discovery_results[i].addr,
priv->pscan_rep_mode, priv->clock_offset)) {
continue;
}
priv->resolving = 1U;
resolving_names = true;
}
if (resolving_names) {
return;
}
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
discovery_cb(discovery_results, discovery_results_count);
bt_br_discovery_reset();
}
void bt_hci_inquiry_complete(struct net_buf *buf)
{
struct bt_hci_evt_inquiry_complete *evt = (void *)buf->data;
if (evt->status) {
BT_ERR("Failed to complete inquiry");
}
report_discovery_results();
}
static struct bt_br_discovery_result *get_result_slot(const bt_addr_t *addr,
int8_t rssi)
{
struct bt_br_discovery_result *result = NULL;
size_t i;
/* check if already present in results */
for (i = 0; i < discovery_results_count; i++) {
if (!bt_addr_cmp(addr, &discovery_results[i].addr)) {
return &discovery_results[i];
}
}
/* Pick a new slot (if available) */
if (discovery_results_count < discovery_results_size) {
bt_addr_copy(&discovery_results[discovery_results_count].addr,
addr);
return &discovery_results[discovery_results_count++];
}
/* ignore if invalid RSSI */
if (rssi == 0xff) {
return NULL;
}
/*
* Pick slot with smallest RSSI that is smaller then passed RSSI
* TODO handle TX if present
*/
for (i = 0; i < discovery_results_size; i++) {
if (discovery_results[i].rssi > rssi) {
continue;
}
if (!result || result->rssi > discovery_results[i].rssi) {
result = &discovery_results[i];
}
}
if (result) {
BT_DBG("Reusing slot (old %s rssi %d dBm)",
bt_addr_str(&result->addr), result->rssi);
bt_addr_copy(&result->addr, addr);
}
return result;
}
void bt_hci_inquiry_result_with_rssi(struct net_buf *buf)
{
uint8_t num_reports = net_buf_pull_u8(buf);
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
return;
}
BT_DBG("number of results: %u", num_reports);
while (num_reports--) {
struct bt_hci_evt_inquiry_result_with_rssi *evt;
struct bt_br_discovery_result *result;
struct discovery_priv *priv;
if (buf->len < sizeof(*evt)) {
BT_ERR("Unexpected end to buffer");
return;
}
evt = net_buf_pull_mem(buf, sizeof(*evt));
BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi);
result = get_result_slot(&evt->addr, evt->rssi);
if (!result) {
return;
}
priv = (struct discovery_priv *)&result->_priv;
priv->pscan_rep_mode = evt->pscan_rep_mode;
priv->clock_offset = evt->clock_offset;
memcpy(result->cod, evt->cod, 3);
result->rssi = evt->rssi;
/* we could reuse slot so make sure EIR is cleared */
(void)memset(result->eir, 0, sizeof(result->eir));
}
}
void bt_hci_extended_inquiry_result(struct net_buf *buf)
{
struct bt_hci_evt_extended_inquiry_result *evt = (void *)buf->data;
struct bt_br_discovery_result *result;
struct discovery_priv *priv;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
return;
}
BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi);
result = get_result_slot(&evt->addr, evt->rssi);
if (!result) {
return;
}
priv = (struct discovery_priv *)&result->_priv;
priv->pscan_rep_mode = evt->pscan_rep_mode;
priv->clock_offset = evt->clock_offset;
result->rssi = evt->rssi;
memcpy(result->cod, evt->cod, 3);
memcpy(result->eir, evt->eir, sizeof(result->eir));
}
void bt_hci_remote_name_request_complete(struct net_buf *buf)
{
struct bt_hci_evt_remote_name_req_complete *evt = (void *)buf->data;
struct bt_br_discovery_result *result;
struct discovery_priv *priv;
int eir_len = 240;
uint8_t *eir;
int i;
result = get_result_slot(&evt->bdaddr, 0xff);
if (!result) {
return;
}
priv = (struct discovery_priv *)&result->_priv;
priv->resolving = 0U;
if (evt->status) {
goto check_names;
}
eir = result->eir;
while (eir_len) {
if (eir_len < 2) {
break;
}
/* Look for early termination */
if (!eir[0]) {
size_t name_len;
eir_len -= 2;
/* name is null terminated */
name_len = strlen((const char *)evt->name);
if (name_len > eir_len) {
eir[0] = eir_len + 1;
eir[1] = EIR_SHORT_NAME;
} else {
eir[0] = name_len + 1;
eir[1] = EIR_SHORT_NAME;
}
memcpy(&eir[2], evt->name, eir[0] - 1);
break;
}
/* Check if field length is correct */
if (eir[0] > eir_len - 1) {
break;
}
/* next EIR Structure */
eir_len -= eir[0] + 1;
eir += eir[0] + 1;
}
check_names:
/* if still waiting for names */
for (i = 0; i < discovery_results_count; i++) {
struct discovery_priv *priv;
priv = (struct discovery_priv *)&discovery_results[i]._priv;
if (priv->resolving) {
return;
}
}
/* all names resolved, report discovery results */
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
discovery_cb(discovery_results, discovery_results_count);
}
void bt_hci_read_remote_features_complete(struct net_buf *buf)
{
struct bt_hci_evt_remote_features *evt = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(evt->handle);
struct bt_hci_cp_read_remote_ext_features *cp;
struct bt_conn *conn;
BT_DBG("status 0x%02x handle %u", evt->status, handle);
conn = bt_conn_lookup_handle(handle);
if (!conn) {
BT_ERR("Can't find conn for handle %u", handle);
return;
}
if (evt->status) {
goto done;
}
memcpy(conn->br.features[0], evt->features, sizeof(evt->features));
if (!BT_FEAT_EXT_FEATURES(conn->br.features)) {
goto done;
}
buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_EXT_FEATURES,
sizeof(*cp));
if (!buf) {
goto done;
}
/* Read remote host features (page 1) */
cp = net_buf_add(buf, sizeof(*cp));
cp->handle = evt->handle;
cp->page = 0x01;
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_EXT_FEATURES, buf, NULL);
done:
bt_conn_unref(conn);
}
void bt_hci_read_remote_ext_features_complete(struct net_buf *buf)
{
struct bt_hci_evt_remote_ext_features *evt = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(evt->handle);
struct bt_conn *conn;
BT_DBG("status 0x%02x handle %u", evt->status, handle);
conn = bt_conn_lookup_handle(handle);
if (!conn) {
BT_ERR("Can't find conn for handle %u", handle);
return;
}
if (!evt->status && evt->page == 0x01) {
memcpy(conn->br.features[1], evt->features,
sizeof(conn->br.features[1]));
}
bt_conn_unref(conn);
}
void bt_hci_role_change(struct net_buf *buf)
{
struct bt_hci_evt_role_change *evt = (void *)buf->data;
struct bt_conn *conn;
BT_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) {
BT_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;
}
bt_conn_unref(conn);
}
static int read_ext_features(void)
{
int i;
/* Read Local Supported Extended Features */
for (i = 1; i < LMP_FEAT_PAGES_COUNT; i++) {
struct bt_hci_cp_read_local_ext_features *cp;
struct bt_hci_rp_read_local_ext_features *rp;
struct net_buf *buf, *rsp;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
cp->page = i;
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_EXT_FEATURES,
buf, &rsp);
if (err) {
return err;
}
rp = (void *)rsp->data;
memcpy(&bt_dev.features[i], rp->ext_features,
sizeof(bt_dev.features[i]));
if (rp->max_page <= i) {
net_buf_unref(rsp);
break;
}
net_buf_unref(rsp);
}
return 0;
}
void device_supported_pkt_type(void)
{
/* Device supported features and sco packet types */
if (BT_FEAT_HV2_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV2);
}
if (BT_FEAT_HV3_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV3);
}
if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV3);
}
if (BT_FEAT_EV4_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV4);
}
if (BT_FEAT_EV5_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV5);
}
if (BT_FEAT_2EV3_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV3);
}
if (BT_FEAT_3EV3_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_3EV3);
}
if (BT_FEAT_3SLOT_PKT(bt_dev.features)) {
bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV5 |
HCI_PKT_TYPE_ESCO_3EV5);
}
}
static void read_buffer_size_complete(struct net_buf *buf)
{
struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data;
uint16_t pkts;
BT_DBG("status 0x%02x", rp->status);
bt_dev.br.mtu = sys_le16_to_cpu(rp->acl_max_len);
pkts = sys_le16_to_cpu(rp->acl_max_num);
BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.br.mtu);
k_sem_init(&bt_dev.br.pkts, pkts, pkts);
}
int bt_br_init(void)
{
struct net_buf *buf;
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;
int err;
/* Read extended local features */
if (BT_FEAT_EXT_FEATURES(bt_dev.features)) {
err = read_ext_features();
if (err) {
return err;
}
}
/* Add local supported packet types to bt_dev */
device_supported_pkt_type();
/* Get BR/EDR buffer size */
err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &buf);
if (err) {
return err;
}
read_buffer_size_complete(buf);
net_buf_unref(buf);
/* Set SSP mode */
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SSP_MODE, sizeof(*ssp_cp));
if (!buf) {
return -ENOBUFS;
}
ssp_cp = net_buf_add(buf, sizeof(*ssp_cp));
ssp_cp->mode = 0x01;
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SSP_MODE, buf, NULL);
if (err) {
return err;
}
/* Enable Inquiry results with RSSI or extended Inquiry */
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_INQUIRY_MODE, sizeof(*inq_cp));
if (!buf) {
return -ENOBUFS;
}
inq_cp = net_buf_add(buf, sizeof(*inq_cp));
inq_cp->mode = 0x02;
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_INQUIRY_MODE, buf, NULL);
if (err) {
return err;
}
/* Set local name */
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp));
if (!buf) {
return -ENOBUFS;
}
name_cp = net_buf_add(buf, sizeof(*name_cp));
strncpy((char *)name_cp->local_name, CONFIG_BT_DEVICE_NAME,
sizeof(name_cp->local_name));
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL);
if (err) {
return err;
}
/* Set page timeout*/
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_PAGE_TIMEOUT, sizeof(uint16_t));
if (!buf) {
return -ENOBUFS;
}
net_buf_add_le16(buf, CONFIG_BT_PAGE_TIMEOUT);
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_PAGE_TIMEOUT, buf, NULL);
if (err) {
return err;
}
/* Enable BR/EDR SC if supported */
if (BT_FEAT_SC(bt_dev.features)) {
struct bt_hci_cp_write_sc_host_supp *sc_cp;
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP,
sizeof(*sc_cp));
if (!buf) {
return -ENOBUFS;
}
sc_cp = net_buf_add(buf, sizeof(*sc_cp));
sc_cp->sc_support = 0x01;
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf,
NULL);
if (err) {
return err;
}
}
return 0;
}
static int br_start_inquiry(const struct bt_br_discovery_param *param)
{
const uint8_t iac[3] = { 0x33, 0x8b, 0x9e };
struct bt_hci_op_inquiry *cp;
struct net_buf *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_INQUIRY, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
cp->length = param->length;
cp->num_rsp = 0xff; /* we limit discovery only by time */
memcpy(cp->lap, iac, 3);
if (param->limited) {
cp->lap[0] = 0x00;
}
return bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY, buf, NULL);
}
static bool valid_br_discov_param(const struct bt_br_discovery_param *param,
size_t num_results)
{
if (!num_results || num_results > 255) {
return false;
}
if (!param->length || param->length > 0x30) {
return false;
}
return true;
}
int bt_br_discovery_start(const struct bt_br_discovery_param *param,
struct bt_br_discovery_result *results, size_t cnt,
bt_br_discovery_cb_t cb)
{
int err;
BT_DBG("");
if (!valid_br_discov_param(param, cnt)) {
return -EINVAL;
}
if (atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
return -EALREADY;
}
err = br_start_inquiry(param);
if (err) {
return err;
}
atomic_set_bit(bt_dev.flags, BT_DEV_INQUIRY);
(void)memset(results, 0, sizeof(*results) * cnt);
discovery_cb = cb;
discovery_results = results;
discovery_results_size = cnt;
discovery_results_count = 0;
return 0;
}
int bt_br_discovery_stop(void)
{
int err;
int i;
BT_DBG("");
if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) {
return -EALREADY;
}
err = bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY_CANCEL, NULL, NULL);
if (err) {
return err;
}
for (i = 0; i < discovery_results_count; i++) {
struct discovery_priv *priv;
struct bt_hci_cp_remote_name_cancel *cp;
struct net_buf *buf;
priv = (struct discovery_priv *)&discovery_results[i]._priv;
if (!priv->resolving) {
continue;
}
buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_CANCEL,
sizeof(*cp));
if (!buf) {
continue;
}
cp = net_buf_add(buf, sizeof(*cp));
bt_addr_copy(&cp->bdaddr, &discovery_results[i].addr);
bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_CANCEL, buf, NULL);
}
atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY);
discovery_cb = NULL;
discovery_results = NULL;
discovery_results_size = 0;
discovery_results_count = 0;
return 0;
}
static int write_scan_enable(uint8_t scan)
{
struct net_buf *buf;
int err;
BT_DBG("type %u", scan);
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SCAN_ENABLE, 1);
if (!buf) {
return -ENOBUFS;
}
net_buf_add_u8(buf, scan);
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SCAN_ENABLE, buf, NULL);
if (err) {
return err;
}
atomic_set_bit_to(bt_dev.flags, BT_DEV_ISCAN,
(scan & BT_BREDR_SCAN_INQUIRY));
atomic_set_bit_to(bt_dev.flags, BT_DEV_PSCAN,
(scan & BT_BREDR_SCAN_PAGE));
return 0;
}
int bt_br_set_connectable(bool enable)
{
if (enable) {
if (atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
return -EALREADY;
} else {
return write_scan_enable(BT_BREDR_SCAN_PAGE);
}
} else {
if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
return -EALREADY;
} else {
return write_scan_enable(BT_BREDR_SCAN_DISABLED);
}
}
}
int bt_br_set_discoverable(bool enable)
{
if (enable) {
if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) {
return -EALREADY;
}
if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) {
return -EPERM;
}
return write_scan_enable(BT_BREDR_SCAN_INQUIRY |
BT_BREDR_SCAN_PAGE);
} else {
if (!atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) {
return -EALREADY;
}
return write_scan_enable(BT_BREDR_SCAN_PAGE);
}
}