2021-03-09 17:31:37 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017-2021 Nordic Semiconductor ASA
|
|
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/sys/byteorder.h>
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
|
|
#include <zephyr/bluetooth/hci.h>
|
|
|
|
#include <zephyr/bluetooth/buf.h>
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
|
|
|
#define LOG_MODULE_NAME bt_br
|
|
|
|
#include "common/log.h"
|
2022-10-25 08:48:54 +02:00
|
|
|
#include "common/bt_str.h"
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
#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);
|
2021-09-16 10:43:02 +02:00
|
|
|
cp->role = BT_HCI_ROLE_PERIPHERAL;
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-16 10:43:02 +02:00
|
|
|
sco_conn->role = BT_HCI_ROLE_PERIPHERAL;
|
2022-02-22 15:16:34 +01:00
|
|
|
bt_conn_set_state(sco_conn, BT_CONN_CONNECTING);
|
2021-03-09 17:31:37 +01:00
|
|
|
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);
|
2021-09-16 10:43:02 +02:00
|
|
|
conn->role = BT_HCI_ROLE_PERIPHERAL;
|
2022-02-22 15:16:34 +01:00
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTING);
|
2021-03-09 17:31:37 +01:00
|
|
|
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;
|
2022-03-16 21:07:43 +00:00
|
|
|
cp->reserved = 0x00; /* reserved, should be set to 0x00 */
|
2021-03-09 17:31:37 +01:00
|
|
|
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;
|
2021-03-24 16:39:15 -07:00
|
|
|
}
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
/* 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;
|
2021-03-24 16:39:15 -07:00
|
|
|
}
|
2021-03-09 17:31:37 +01:00
|
|
|
|
|
|
|
/* 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) {
|
2021-09-14 12:28:45 +02:00
|
|
|
conn->role = BT_CONN_ROLE_PERIPHERAL;
|
2021-03-09 17:31:37 +01:00
|
|
|
} else {
|
2021-09-14 12:28:45 +02:00
|
|
|
conn->role = BT_CONN_ROLE_CENTRAL;
|
2021-03-09 17:31:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|