/* * Copyright (c) 2017-2021 Nordic Semiconductor ASA * Copyright (c) 2015-2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "common/bt_str.h" #include "hci_core.h" #include "conn_internal.h" #include "keys.h" #define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_br); 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)) { LOG_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_CONNECTING); 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; LOG_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_CONNECTING); 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) { LOG_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) { LOG_ERR("Failed to read encryption key size (err %d)", err); return false; } if (rsp->len < sizeof(*rp)) { LOG_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); LOG_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 { LOG_WRN("No BR/EDR link key found"); conn->sec_level = BT_SECURITY_L2; } if (!br_sufficient_key_size(conn)) { LOG_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) { LOG_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); LOG_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) { LOG_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); LOG_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) { LOG_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; /* reserved, 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); if (discovery_cb) { 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) { LOG_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_eq(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) { LOG_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; } LOG_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)) { LOG_ERR("Unexpected end to buffer"); return; } evt = net_buf_pull_mem(buf, sizeof(*evt)); LOG_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; } LOG_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); if (discovery_cb) { 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; LOG_DBG("status 0x%02x handle %u", evt->status, handle); conn = bt_conn_lookup_handle(handle); if (!conn) { LOG_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; LOG_DBG("status 0x%02x handle %u", evt->status, handle); conn = bt_conn_lookup_handle(handle); if (!conn) { LOG_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; 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; } 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; LOG_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); LOG_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; LOG_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; LOG_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; LOG_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); } }