| /* gap.c - Bluetooth GAP Tester */ |
| |
| /* |
| * Copyright (c) 2015-2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <stdint.h> |
| |
| #include <zephyr/bluetooth/addr.h> |
| #include <zephyr/bluetooth/gap.h> |
| #include <zephyr/sys/atomic.h> |
| #include <zephyr/types.h> |
| #include <string.h> |
| |
| #include <zephyr/toolchain.h> |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/conn.h> |
| #include <zephyr/bluetooth/gatt.h> |
| #include <zephyr/bluetooth/hci.h> |
| |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/net_buf.h> |
| |
| #include <hci_core.h> |
| |
| #include <zephyr/logging/log.h> |
| #define LOG_MODULE_NAME bttester_gap |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL); |
| |
| #include "btp/btp.h" |
| |
| #define CONTROLLER_NAME "btp_tester" |
| |
| #define BT_LE_AD_DISCOV_MASK (BT_LE_AD_LIMITED | BT_LE_AD_GENERAL) |
| #if defined(CONFIG_BT_EXT_ADV) |
| #define ADV_BUF_LEN (sizeof(struct btp_gap_device_found_ev) + 2 * CONFIG_BT_EXT_SCAN_BUF_SIZE) |
| #else |
| #define ADV_BUF_LEN (sizeof(struct btp_gap_device_found_ev) + 2 * 31) |
| #endif |
| |
| static atomic_t current_settings; |
| struct bt_conn_auth_cb cb; |
| static uint8_t oob_legacy_tk[16] = { 0 }; |
| |
| static bool filter_list_in_use; |
| |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| static struct bt_le_oob oob_sc_local = { 0 }; |
| static struct bt_le_oob oob_sc_remote = { 0 }; |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| |
| /* connection parameters for rejection test */ |
| #define REJECT_INTERVAL_MIN 0x0C80 |
| #define REJECT_INTERVAL_MAX 0x0C80 |
| #define REJECT_LATENCY 0x0000 |
| #define REJECT_SUPERVISION_TIMEOUT 0x0C80 |
| |
| #if defined(CONFIG_BT_PRIVACY) |
| static struct { |
| bt_addr_le_t addr; |
| bool supported; |
| } cars[CONFIG_BT_MAX_PAIRED]; |
| |
| static uint8_t read_car_cb(struct bt_conn *conn, uint8_t err, |
| struct bt_gatt_read_params *params, const void *data, |
| uint16_t length); |
| |
| static struct bt_gatt_read_params read_car_params = { |
| .func = read_car_cb, |
| .by_uuid.uuid = BT_UUID_CENTRAL_ADDR_RES, |
| .by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE, |
| .by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE, |
| }; |
| |
| static uint8_t read_car_cb(struct bt_conn *conn, uint8_t err, |
| struct bt_gatt_read_params *params, const void *data, |
| uint16_t length) |
| { |
| struct bt_conn_info info; |
| bool supported = false; |
| |
| if (!err && data && length == 1) { |
| const uint8_t *tmp = data; |
| |
| /* only 0 or 1 are valid values */ |
| if (tmp[0] == 1) { |
| supported = true; |
| } |
| } |
| |
| bt_conn_get_info(conn, &info); |
| |
| for (int i = 0; i < CONFIG_BT_MAX_PAIRED; i++) { |
| if (bt_addr_le_eq(info.le.dst, &cars[i].addr)) { |
| cars[i].supported = supported; |
| break; |
| } |
| } |
| |
| return BT_GATT_ITER_STOP; |
| } |
| #endif |
| |
| static void le_connected(struct bt_conn *conn, uint8_t err) |
| { |
| struct btp_gap_device_connected_ev ev; |
| char addr_str[BT_ADDR_LE_STR_LEN]; |
| struct bt_conn_info info; |
| |
| (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str)); |
| LOG_DBG("%s: 0x%02x", addr_str, err); |
| |
| if (err) { |
| return; |
| } |
| |
| bt_conn_get_info(conn, &info); |
| |
| bt_addr_le_copy(&ev.address, info.le.dst); |
| ev.interval = sys_cpu_to_le16(info.le.interval); |
| ev.latency = sys_cpu_to_le16(info.le.latency); |
| ev.timeout = sys_cpu_to_le16(info.le.timeout); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_DEVICE_CONNECTED, &ev, sizeof(ev)); |
| |
| if (info.role == BT_CONN_ROLE_PERIPHERAL) { |
| struct btp_gap_new_settings_ev sev; |
| |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| sev.current_settings = sys_cpu_to_le32(current_settings); |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_NEW_SETTINGS, &sev, sizeof(sev)); |
| } |
| } |
| |
| static void le_disconnected(struct bt_conn *conn, uint8_t reason) |
| { |
| struct btp_gap_device_disconnected_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| char addr_str[BT_ADDR_LE_STR_LEN]; |
| |
| (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str)); |
| LOG_DBG("%s: 0x%02x", addr_str, reason); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_DEVICE_DISCONNECTED, &ev, sizeof(ev)); |
| } |
| |
| static void le_identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa, |
| const bt_addr_le_t *identity) |
| { |
| struct btp_gap_identity_resolved_ev ev; |
| |
| bt_addr_le_copy(&ev.address, rpa); |
| bt_addr_le_copy(&ev.identity_address, identity); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_IDENTITY_RESOLVED, &ev, sizeof(ev)); |
| } |
| |
| static void le_param_updated(struct bt_conn *conn, uint16_t interval, |
| uint16_t latency, uint16_t timeout) |
| { |
| struct btp_gap_conn_param_update_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| ev.interval = sys_cpu_to_le16(interval); |
| ev.latency = sys_cpu_to_le16(latency); |
| ev.timeout = sys_cpu_to_le16(timeout); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_CONN_PARAM_UPDATE, &ev, sizeof(ev)); |
| } |
| |
| static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) |
| { |
| /* reject update if all parameters match reject pattern */ |
| if ((param->interval_min == REJECT_INTERVAL_MIN) && |
| (param->interval_max == REJECT_INTERVAL_MAX) && |
| (param->latency == REJECT_LATENCY) && |
| (param->timeout == REJECT_SUPERVISION_TIMEOUT)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void le_security_changed(struct bt_conn *conn, bt_security_t level, |
| enum bt_security_err err) |
| { |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| struct btp_gap_sec_level_changed_ev sec_ev; |
| struct btp_gap_bond_lost_ev bond_ev; |
| struct bt_conn_info info; |
| |
| switch (err) { |
| case BT_SECURITY_ERR_SUCCESS: |
| bt_addr_le_copy(&sec_ev.address, addr); |
| /* enum matches BTP values */ |
| sec_ev.sec_level = level; |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_SEC_LEVEL_CHANGED, |
| &sec_ev, sizeof(sec_ev)); |
| break; |
| case BT_SECURITY_ERR_PIN_OR_KEY_MISSING: |
| /* for central role this means that peer have no LTK when we |
| * started encryption procedure |
| * |
| * This means bond is lost and we restart pairing to re-bond |
| */ |
| if (bt_conn_get_info(conn, &info) == 0 && |
| info.role == BT_CONN_ROLE_CENTRAL) { |
| LOG_DBG("Bond lost"); |
| |
| bt_addr_le_copy(&bond_ev.address, addr); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_BOND_LOST, |
| &bond_ev, sizeof(bond_ev)); |
| |
| (void)bt_conn_set_security(conn, BT_SECURITY_L2 | BT_SECURITY_FORCE_PAIR); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static struct bt_conn_cb conn_callbacks = { |
| .connected = le_connected, |
| .disconnected = le_disconnected, |
| .identity_resolved = le_identity_resolved, |
| .le_param_updated = le_param_updated, |
| .le_param_req = le_param_req, |
| .security_changed = le_security_changed, |
| }; |
| |
| static uint8_t supported_commands(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| struct btp_gap_read_supported_commands_rp *rp = rsp; |
| |
| /* octet 0 */ |
| tester_set_bit(rp->data, BTP_GAP_READ_SUPPORTED_COMMANDS); |
| tester_set_bit(rp->data, BTP_GAP_READ_CONTROLLER_INDEX_LIST); |
| tester_set_bit(rp->data, BTP_GAP_READ_CONTROLLER_INFO); |
| tester_set_bit(rp->data, BTP_GAP_SET_POWERED); |
| tester_set_bit(rp->data, BTP_GAP_SET_CONNECTABLE); |
| |
| /* octet 1 */ |
| tester_set_bit(rp->data, BTP_GAP_SET_DISCOVERABLE); |
| tester_set_bit(rp->data, BTP_GAP_SET_BONDABLE); |
| tester_set_bit(rp->data, BTP_GAP_START_ADVERTISING); |
| tester_set_bit(rp->data, BTP_GAP_STOP_ADVERTISING); |
| tester_set_bit(rp->data, BTP_GAP_START_DISCOVERY); |
| tester_set_bit(rp->data, BTP_GAP_STOP_DISCOVERY); |
| tester_set_bit(rp->data, BTP_GAP_CONNECT); |
| tester_set_bit(rp->data, BTP_GAP_DISCONNECT); |
| |
| /* octet 2 */ |
| tester_set_bit(rp->data, BTP_GAP_SET_IO_CAP); |
| tester_set_bit(rp->data, BTP_GAP_PAIR); |
| tester_set_bit(rp->data, BTP_GAP_PASSKEY_ENTRY); |
| tester_set_bit(rp->data, BTP_GAP_PASSKEY_CONFIRM); |
| tester_set_bit(rp->data, BTP_GAP_START_DIRECTED_ADV); |
| tester_set_bit(rp->data, BTP_GAP_CONN_PARAM_UPDATE); |
| |
| /* octet 3 */ |
| tester_set_bit(rp->data, BTP_GAP_OOB_LEGACY_SET_DATA); |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| tester_set_bit(rp->data, BTP_GAP_OOB_SC_GET_LOCAL_DATA); |
| tester_set_bit(rp->data, BTP_GAP_OOB_SC_SET_REMOTE_DATA); |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| tester_set_bit(rp->data, BTP_GAP_SET_MITM); |
| tester_set_bit(rp->data, BTP_GAP_SET_FILTER_LIST); |
| #if defined(CONFIG_BT_EXT_ADV) |
| tester_set_bit(rp->data, BTP_GAP_SET_EXTENDED_ADVERTISING); |
| #endif |
| |
| *rsp_len = sizeof(*rp) + 4; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t controller_index_list(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| struct btp_gap_read_controller_index_list_rp *rp = rsp; |
| |
| rp->num = 1U; |
| rp->index[0] = BTP_INDEX; |
| |
| *rsp_len = sizeof(*rp) + 1; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t controller_info(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| struct btp_gap_read_controller_info_rp *rp = rsp; |
| uint32_t supported_settings; |
| struct bt_le_oob oob_local = { 0 }; |
| |
| bt_le_oob_get_local(BT_ID_DEFAULT, &oob_local); |
| |
| bt_addr_copy(&rp->address, &oob_local.addr.a); |
| |
| /* |
| * Re-use the oob data read here in get_oob_sc_local_data() |
| */ |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| oob_sc_local = oob_local; |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| |
| /* |
| * If privacy is used, the device uses random type address, otherwise |
| * static random or public type address is used. |
| */ |
| #if !defined(CONFIG_BT_PRIVACY) |
| if (oob_local.addr.type == BT_ADDR_LE_RANDOM) { |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_STATIC_ADDRESS); |
| } |
| #endif /* CONFIG_BT_PRIVACY */ |
| |
| supported_settings = BIT(BTP_GAP_SETTINGS_POWERED); |
| supported_settings |= BIT(BTP_GAP_SETTINGS_CONNECTABLE); |
| supported_settings |= BIT(BTP_GAP_SETTINGS_BONDABLE); |
| supported_settings |= BIT(BTP_GAP_SETTINGS_LE); |
| supported_settings |= BIT(BTP_GAP_SETTINGS_ADVERTISING); |
| supported_settings |= BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING); |
| |
| rp->supported_settings = sys_cpu_to_le32(supported_settings); |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| memcpy(rp->name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME)); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| static const char *oob_config_str(int oob_config) |
| { |
| switch (oob_config) { |
| case BT_CONN_OOB_LOCAL_ONLY: |
| return "Local"; |
| case BT_CONN_OOB_REMOTE_ONLY: |
| return "Remote"; |
| case BT_CONN_OOB_BOTH_PEERS: |
| return "Local and Remote"; |
| case BT_CONN_OOB_NO_DATA: |
| default: |
| return "no"; |
| } |
| } |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| |
| static void oob_data_request(struct bt_conn *conn, |
| struct bt_conn_oob_info *oob_info) |
| { |
| struct bt_conn_info info; |
| int err = bt_conn_get_info(conn, &info); |
| |
| if (err) { |
| return; |
| } |
| |
| char addr[BT_ADDR_LE_STR_LEN]; |
| |
| bt_addr_le_to_str(info.le.dst, addr, sizeof(addr)); |
| |
| switch (oob_info->type) { |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| case BT_CONN_OOB_LE_SC: |
| { |
| LOG_DBG("Set %s OOB SC data for %s, ", |
| oob_config_str(oob_info->lesc.oob_config), |
| addr); |
| |
| struct bt_le_oob_sc_data *oobd_local = |
| oob_info->lesc.oob_config != BT_CONN_OOB_REMOTE_ONLY ? |
| &oob_sc_local.le_sc_data : |
| NULL; |
| |
| struct bt_le_oob_sc_data *oobd_remote = |
| oob_info->lesc.oob_config != BT_CONN_OOB_LOCAL_ONLY ? |
| &oob_sc_remote.le_sc_data : |
| NULL; |
| |
| if (oobd_remote) { |
| /* Assume that oob_sc_remote |
| * corresponds to the currently connected peer |
| */ |
| bt_addr_le_copy(&oob_sc_remote.addr, info.le.remote); |
| } |
| |
| if (oobd_local && |
| !bt_addr_le_eq(info.le.local, &oob_sc_local.addr)) { |
| bt_addr_le_to_str(info.le.local, addr, sizeof(addr)); |
| LOG_DBG("No OOB data available for local %s", |
| addr); |
| bt_conn_auth_cancel(conn); |
| return; |
| } |
| |
| err = bt_le_oob_set_sc_data(conn, oobd_local, oobd_remote); |
| if (err) { |
| LOG_DBG("bt_le_oob_set_sc_data failed with: %d", err); |
| } |
| |
| break; |
| } |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| |
| #if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) |
| case BT_CONN_OOB_LE_LEGACY: |
| LOG_DBG("Legacy OOB TK requested from remote %s", addr); |
| |
| err = bt_le_oob_set_legacy_tk(conn, oob_legacy_tk); |
| if (err < 0) { |
| LOG_ERR("Failed to set OOB TK: %d", err); |
| } |
| |
| break; |
| #endif /* !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) */ |
| default: |
| LOG_ERR("Unhandled OOB type %d", oob_info->type); |
| break; |
| } |
| } |
| |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| static uint8_t get_oob_sc_local_data(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| struct btp_gap_oob_sc_get_local_data_rp *rp = rsp; |
| |
| cb.oob_data_request = oob_data_request; |
| |
| memcpy(rp->conf, &oob_sc_local.le_sc_data.c[0], sizeof(rp->conf)); |
| memcpy(rp->rand, &oob_sc_local.le_sc_data.r[0], sizeof(rp->rand)); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_oob_sc_remote_data(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_oob_sc_set_remote_data_cmd *cp = cmd; |
| |
| cb.oob_data_request = oob_data_request; |
| bt_le_oob_set_sc_flag(true); |
| |
| /* Note that the .addr field |
| * will be set by the oob_data_request callback |
| */ |
| memcpy(&oob_sc_remote.le_sc_data.r[0], cp->rand, |
| sizeof(oob_sc_remote.le_sc_data.r)); |
| memcpy(&oob_sc_remote.le_sc_data.c[0], cp->conf, |
| sizeof(oob_sc_remote.le_sc_data.c)); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| |
| static uint8_t set_powered(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_powered_cmd *cp = cmd; |
| struct btp_gap_set_powered_rp *rp = rsp; |
| int err; |
| |
| if (cp->powered) { |
| err = bt_enable(NULL); |
| if (err < 0) { |
| LOG_ERR("Unable to enable Bluetooth: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| bt_conn_cb_register(&conn_callbacks); |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_POWERED); |
| } else { |
| err = bt_disable(); |
| if (err < 0) { |
| LOG_ERR("Unable to disable Bluetooth: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| bt_conn_cb_unregister(&conn_callbacks); |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_POWERED); |
| } |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_connectable(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_connectable_cmd *cp = cmd; |
| struct btp_gap_set_connectable_rp *rp = rsp; |
| |
| if (cp->connectable) { |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE); |
| } else { |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE); |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t ad_flags = BT_LE_AD_NO_BREDR; |
| static struct bt_data ad[10] = { |
| BT_DATA(BT_DATA_FLAGS, &ad_flags, sizeof(ad_flags)), |
| }; |
| static struct bt_data sd[10]; |
| |
| #if defined(CONFIG_BT_EXT_ADV) |
| static struct bt_le_ext_adv *ext_adv; |
| |
| struct bt_le_ext_adv *tester_gap_ext_adv_get(void) |
| { |
| return ext_adv; |
| } |
| |
| int tester_gap_start_ext_adv(void) |
| { |
| int err; |
| |
| err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT); |
| if (err != 0) { |
| LOG_ERR("Failed to start advertising"); |
| |
| return -EINVAL; |
| } |
| |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| |
| return 0; |
| } |
| |
| int tester_gap_stop_ext_adv(void) |
| { |
| int err; |
| |
| err = bt_le_ext_adv_stop(ext_adv); |
| if (err != 0) { |
| LOG_ERR("Failed to stop advertising"); |
| |
| return -EINVAL; |
| } |
| |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| |
| return 0; |
| } |
| #endif /* defined(CONFIG_BT_EXT_ADV) */ |
| |
| static uint8_t set_discoverable(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_discoverable_cmd *cp = cmd; |
| struct btp_gap_set_discoverable_rp *rp = rsp; |
| |
| switch (cp->discoverable) { |
| case BTP_GAP_NON_DISCOVERABLE: |
| ad_flags &= ~(BT_LE_AD_GENERAL | BT_LE_AD_LIMITED); |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_DISCOVERABLE); |
| break; |
| case BTP_GAP_GENERAL_DISCOVERABLE: |
| ad_flags &= ~BT_LE_AD_LIMITED; |
| ad_flags |= BT_LE_AD_GENERAL; |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_DISCOVERABLE); |
| break; |
| case BTP_GAP_LIMITED_DISCOVERABLE: |
| ad_flags &= ~BT_LE_AD_GENERAL; |
| ad_flags |= BT_LE_AD_LIMITED; |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_DISCOVERABLE); |
| break; |
| default: |
| return BTP_STATUS_FAILED; |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_bondable(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_bondable_cmd *cp = cmd; |
| struct btp_gap_set_bondable_rp *rp = rsp; |
| |
| LOG_DBG("bondable: %d", cp->bondable); |
| |
| if (cp->bondable) { |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_BONDABLE); |
| } else { |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_BONDABLE); |
| } |
| |
| bt_set_bondable(cp->bondable); |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| int tester_gap_create_adv_instance(struct bt_le_adv_param *param, uint8_t own_addr_type, |
| const struct bt_data *ad, size_t ad_len, |
| const struct bt_data *sd, size_t sd_len, |
| uint32_t *settings) |
| { |
| int err = 0; |
| |
| if (settings != NULL) { |
| atomic_set(¤t_settings, *settings); |
| } |
| |
| if (atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE)) { |
| param->options |= BT_LE_ADV_OPT_CONN; |
| |
| if (filter_list_in_use) { |
| param->options |= BT_LE_ADV_OPT_FILTER_CONN; |
| } |
| } |
| |
| if (filter_list_in_use) { |
| param->options |= BT_LE_ADV_OPT_FILTER_SCAN_REQ; |
| } |
| |
| switch (own_addr_type) { |
| case BTP_GAP_ADDR_TYPE_IDENTITY: |
| param->options |= BT_LE_ADV_OPT_USE_IDENTITY; |
| break; |
| #if defined(CONFIG_BT_PRIVACY) |
| case BTP_GAP_ADDR_TYPE_RESOLVABLE_PRIVATE: |
| /* RPA usage is controlled via privacy settings */ |
| if (!atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_PRIVACY)) { |
| return -EINVAL; |
| } |
| break; |
| case BTP_GAP_ADDR_TYPE_NON_RESOLVABLE_PRIVATE: |
| /* NRPA is used only for non-connectable advertising */ |
| if (atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE)) { |
| return -EINVAL; |
| } |
| break; |
| #endif |
| default: |
| return -EINVAL; |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_EXT_ADV) && atomic_test_bit(¤t_settings, |
| BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) { |
| param->options |= BT_LE_ADV_OPT_EXT_ADV; |
| if (ext_adv != NULL) { |
| err = bt_le_ext_adv_stop(ext_adv); |
| if (err != 0) { |
| return err; |
| } |
| |
| err = bt_le_ext_adv_delete(ext_adv); |
| if (err != 0) { |
| return err; |
| } |
| |
| ext_adv = NULL; |
| } |
| |
| err = bt_le_ext_adv_create(param, NULL, &ext_adv); |
| if (err != 0) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_le_ext_adv_set_data(ext_adv, ad, ad_len, sd_len ? sd : NULL, sd_len); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t start_advertising(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_start_advertising_cmd *cp = cmd; |
| struct btp_gap_start_advertising_rp *rp = rsp; |
| struct bt_le_adv_param param = |
| BT_LE_ADV_PARAM_INIT(0, BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL); |
| uint8_t own_addr_type; |
| uint32_t duration; |
| uint8_t adv_len; |
| uint8_t sd_len; |
| int err; |
| int i; |
| |
| /* This command is very unfortunate since after variable data there is |
| * additional 5 bytes (4 bytes for duration, 1 byte for own address |
| * type. |
| */ |
| if ((cmd_len < sizeof(*cp)) || |
| (cmd_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len + |
| sizeof(duration) + sizeof(own_addr_type))) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| /* currently ignored */ |
| duration = sys_get_le32(cp->adv_sr_data + cp->adv_data_len + cp->scan_rsp_len); |
| (void)duration; |
| own_addr_type = cp->adv_sr_data[cp->adv_data_len + cp->scan_rsp_len + sizeof(duration)]; |
| |
| for (i = 0, adv_len = 1U; i < cp->adv_data_len; adv_len++) { |
| if (adv_len >= ARRAY_SIZE(ad)) { |
| LOG_ERR("ad[] Out of memory"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| ad[adv_len].type = cp->adv_sr_data[i++]; |
| ad[adv_len].data_len = cp->adv_sr_data[i++]; |
| ad[adv_len].data = &cp->adv_sr_data[i]; |
| i += ad[adv_len].data_len; |
| } |
| |
| for (sd_len = 0U; i < cp->adv_data_len + cp->scan_rsp_len; sd_len++) { |
| if (sd_len >= ARRAY_SIZE(sd)) { |
| LOG_ERR("sd[] Out of memory"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| sd[sd_len].type = cp->adv_sr_data[i++]; |
| sd[sd_len].data_len = cp->adv_sr_data[i++]; |
| sd[sd_len].data = &cp->adv_sr_data[i]; |
| i += sd[sd_len].data_len; |
| } |
| |
| err = tester_gap_create_adv_instance(¶m, own_addr_type, ad, adv_len, sd, sd_len, NULL); |
| if (err != 0) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| #if defined(CONFIG_BT_EXT_ADV) |
| if (atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) { |
| err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT); |
| #else |
| if (0) { |
| #endif |
| } else { |
| err = bt_le_adv_start(¶m, ad, adv_len, sd_len ? sd : NULL, sd_len); |
| } |
| |
| /* BTP API don't allow to set empty scan response data. */ |
| if (err < 0) { |
| LOG_ERR("Failed to start advertising"); |
| |
| return BTP_STATUS_FAILED; |
| } |
| |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t start_directed_advertising(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_start_directed_adv_cmd *cp = cmd; |
| struct btp_gap_start_directed_adv_rp *rp = rsp; |
| struct bt_le_adv_param adv_param; |
| uint16_t options = sys_le16_to_cpu(cp->options); |
| |
| adv_param = *BT_LE_ADV_CONN_DIR(&cp->address); |
| |
| if (!(options & BTP_GAP_START_DIRECTED_ADV_HD)) { |
| adv_param.options |= BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY; |
| adv_param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; |
| adv_param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; |
| } |
| |
| if (options & BTP_GAP_START_DIRECTED_ADV_PEER_RPA) { |
| #if defined(CONFIG_BT_PRIVACY) |
| /* check if peer supports Central Address Resolution */ |
| for (int i = 0; i < CONFIG_BT_MAX_PAIRED; i++) { |
| if (bt_addr_le_eq(&cp->address, &cars[i].addr)) { |
| if (cars[i].supported) { |
| adv_param.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA; |
| } |
| } |
| } |
| #endif |
| } |
| |
| if (bt_le_adv_start(&adv_param, NULL, 0, NULL, 0) < 0) { |
| LOG_ERR("Failed to start advertising"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t stop_advertising(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| struct btp_gap_stop_advertising_rp *rp = rsp; |
| int err; |
| |
| err = bt_le_adv_stop(); |
| if (err < 0) { |
| tester_rsp(BTP_SERVICE_ID_GAP, BTP_GAP_STOP_ADVERTISING, BTP_STATUS_FAILED); |
| LOG_ERR("Failed to stop advertising: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| atomic_clear_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING); |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t get_ad_flags(struct net_buf_simple *buf_ad) |
| { |
| uint8_t len, i; |
| |
| /* Parse advertisement to get flags */ |
| for (i = 0U; i < buf_ad->len; i += len - 1) { |
| len = buf_ad->data[i++]; |
| if (!len) { |
| break; |
| } |
| |
| /* Check if field length is correct */ |
| if (len > (buf_ad->len - i) || (buf_ad->len - i) < 1) { |
| break; |
| } |
| |
| switch (buf_ad->data[i++]) { |
| case BT_DATA_FLAGS: |
| return buf_ad->data[i]; |
| default: |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static uint8_t discovery_flags; |
| static struct net_buf_simple *adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN); |
| |
| static void store_adv(const bt_addr_le_t *addr, int8_t rssi, |
| struct net_buf_simple *buf_ad) |
| { |
| struct btp_gap_device_found_ev *ev; |
| |
| /* cleanup */ |
| net_buf_simple_init(adv_buf, 0); |
| |
| ev = net_buf_simple_add(adv_buf, sizeof(*ev)); |
| |
| bt_addr_le_copy(&ev->address, addr); |
| ev->rssi = rssi; |
| ev->flags = BTP_GAP_DEVICE_FOUND_FLAG_AD | BTP_GAP_DEVICE_FOUND_FLAG_RSSI; |
| ev->eir_data_len = buf_ad->len; |
| memcpy(net_buf_simple_add(adv_buf, buf_ad->len), buf_ad->data, buf_ad->len); |
| } |
| |
| static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t evtype, |
| struct net_buf_simple *buf_ad) |
| { |
| /* if General/Limited Discovery - parse Advertising data to get flags */ |
| if (!(discovery_flags & BTP_GAP_DISCOVERY_FLAG_LE_OBSERVE) && |
| (evtype != BT_GAP_ADV_TYPE_SCAN_RSP)) { |
| uint8_t flags = get_ad_flags(buf_ad); |
| |
| /* ignore non-discoverable devices */ |
| if (!(flags & BT_LE_AD_DISCOV_MASK)) { |
| LOG_DBG("Non discoverable, skipping"); |
| return; |
| } |
| |
| /* if Limited Discovery - ignore general discoverable devices */ |
| if ((discovery_flags & BTP_GAP_DISCOVERY_FLAG_LIMITED) && |
| !(flags & BT_LE_AD_LIMITED)) { |
| LOG_DBG("General discoverable, skipping"); |
| return; |
| } |
| } |
| |
| /* attach Scan Response data */ |
| if (evtype == BT_GAP_ADV_TYPE_SCAN_RSP) { |
| struct btp_gap_device_found_ev *ev; |
| bt_addr_le_t a; |
| |
| /* skip if there is no pending advertisement */ |
| if (!adv_buf->len) { |
| LOG_INF("No pending advertisement, skipping"); |
| return; |
| } |
| |
| ev = (void *) adv_buf->data; |
| |
| bt_addr_le_copy(&a, &ev->address); |
| |
| /* |
| * in general, the Scan Response comes right after the |
| * Advertisement, but if not if send stored event and ignore |
| * this one |
| */ |
| if (!bt_addr_le_eq(addr, &a)) { |
| LOG_INF("Address does not match, skipping"); |
| goto done; |
| } |
| |
| ev->eir_data_len += buf_ad->len; |
| ev->flags |= BTP_GAP_DEVICE_FOUND_FLAG_SD; |
| |
| memcpy(net_buf_simple_add(adv_buf, buf_ad->len), buf_ad->data, buf_ad->len); |
| |
| goto done; |
| } |
| |
| /* |
| * if there is another pending advertisement, send it and store the |
| * current one |
| */ |
| if (adv_buf->len) { |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_DEVICE_FOUND, |
| adv_buf->data, adv_buf->len); |
| net_buf_simple_reset(adv_buf); |
| } |
| |
| store_adv(addr, rssi, buf_ad); |
| |
| /* if Active Scan and scannable event - wait for Scan Response */ |
| if ((discovery_flags & BTP_GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) && |
| (evtype == BT_GAP_ADV_TYPE_ADV_IND || |
| evtype == BT_GAP_ADV_TYPE_ADV_SCAN_IND)) { |
| LOG_DBG("Waiting for scan response"); |
| return; |
| } |
| done: |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_DEVICE_FOUND, |
| adv_buf->data, adv_buf->len); |
| net_buf_simple_reset(adv_buf); |
| } |
| |
| static uint8_t start_discovery(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_start_discovery_cmd *cp = cmd; |
| |
| /* only LE scan is supported */ |
| if (cp->flags & BTP_GAP_DISCOVERY_FLAG_BREDR) { |
| LOG_WRN("BR/EDR not supported"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| if (bt_le_scan_start(cp->flags & BTP_GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN ? |
| BT_LE_SCAN_ACTIVE : BT_LE_SCAN_PASSIVE, |
| device_found) < 0) { |
| LOG_ERR("Failed to start scanning"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| net_buf_simple_init(adv_buf, 0); |
| discovery_flags = cp->flags; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t stop_discovery(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| |
| err = bt_le_scan_stop(); |
| if (err < 0) { |
| LOG_ERR("Failed to stop scanning: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t connect(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| /* The conn interval is set to 60ms (0x30). This is to better support test cases where we |
| * need to connect to multiple peripherals (up to 3). The connection interval should also be |
| * a multiple of 30ms, as that is ideal to support both 7.5ms and 10ms ISO intervals |
| */ |
| const uint16_t interval = BT_GAP_MS_TO_CONN_INTERVAL(60U); |
| const struct bt_le_conn_param *conn_param = |
| BT_LE_CONN_PARAM(interval, interval, 0U, BT_GAP_MS_TO_CONN_TIMEOUT(4000U)); |
| const struct btp_gap_connect_cmd *cp = cmd; |
| int err; |
| |
| if (!bt_addr_le_eq(&cp->address, BT_ADDR_LE_ANY)) { |
| struct bt_conn *conn = NULL; |
| |
| err = bt_conn_le_create(&cp->address, BT_CONN_LE_CREATE_CONN, conn_param, &conn); |
| if (err) { |
| LOG_ERR("Failed to create connection (%d)", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| bt_conn_unref(conn); |
| } else { |
| err = bt_conn_le_create_auto(BT_CONN_LE_CREATE_CONN, conn_param); |
| if (err) { |
| LOG_ERR("Failed to create auto connection (%d)", err); |
| return BTP_STATUS_FAILED; |
| } |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t disconnect(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_disconnect_cmd *cp = cmd; |
| struct bt_conn *conn; |
| uint8_t status; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| if (bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) { |
| LOG_ERR("Failed to disconnect"); |
| status = BTP_STATUS_FAILED; |
| } else { |
| status = BTP_STATUS_SUCCESS; |
| } |
| |
| bt_conn_unref(conn); |
| |
| return status; |
| } |
| |
| static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) |
| { |
| struct btp_gap_passkey_display_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| ev.passkey = sys_cpu_to_le32(passkey); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PASSKEY_DISPLAY, &ev, sizeof(ev)); |
| } |
| |
| static void auth_passkey_entry(struct bt_conn *conn) |
| { |
| struct btp_gap_passkey_entry_req_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PASSKEY_ENTRY_REQ, &ev, sizeof(ev)); |
| } |
| |
| static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) |
| { |
| struct btp_gap_passkey_confirm_req_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| ev.passkey = sys_cpu_to_le32(passkey); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PASSKEY_CONFIRM_REQ, &ev, sizeof(ev)); |
| } |
| |
| static void auth_cancel(struct bt_conn *conn) |
| { |
| /* TODO */ |
| } |
| |
| enum bt_security_err auth_pairing_accept(struct bt_conn *conn, |
| const struct bt_conn_pairing_feat *const feat) |
| { |
| struct btp_gap_bond_lost_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| if (!bt_addr_le_is_bonded(BT_ID_DEFAULT, addr)) { |
| return BT_SECURITY_ERR_SUCCESS; |
| } |
| |
| /* If a peer is already bonded and tries to pair again then it means that |
| * the it has lost its bond information. |
| */ |
| LOG_DBG("Bond lost"); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_BOND_LOST, &ev, sizeof(ev)); |
| |
| return BT_SECURITY_ERR_SUCCESS; |
| } |
| |
| void auth_pairing_failed(struct bt_conn *conn, enum bt_security_err reason) |
| { |
| struct btp_gap_bond_pairing_failed_ev ev; |
| const bt_addr_le_t *addr = bt_conn_get_dst(conn); |
| |
| bt_addr_le_copy(&ev.address, addr); |
| ev.reason = reason; |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PAIRING_FAILED, &ev, sizeof(ev)); |
| } |
| |
| static void auth_pairing_complete(struct bt_conn *conn, bool bonded) |
| { |
| #if defined(CONFIG_BT_PRIVACY) |
| /* Read peer's Central Address Resolution if bonded */ |
| if (bonded) { |
| bt_gatt_read(conn, &read_car_params); |
| } |
| #endif |
| } |
| |
| static struct bt_conn_auth_info_cb auth_info_cb = { |
| .pairing_failed = auth_pairing_failed, |
| .pairing_complete = auth_pairing_complete, |
| }; |
| |
| static uint8_t set_io_cap(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_io_cap_cmd *cp = cmd; |
| |
| /* Reset io cap requirements */ |
| (void)memset(&cb, 0, sizeof(cb)); |
| bt_conn_auth_cb_register(NULL); |
| |
| LOG_DBG("io_cap: %d", cp->io_cap); |
| |
| switch (cp->io_cap) { |
| case BTP_GAP_IO_CAP_DISPLAY_ONLY: |
| cb.cancel = auth_cancel; |
| cb.passkey_display = auth_passkey_display; |
| break; |
| case BTP_GAP_IO_CAP_KEYBOARD_DISPLAY: |
| cb.cancel = auth_cancel; |
| cb.passkey_display = auth_passkey_display; |
| cb.passkey_entry = auth_passkey_entry; |
| cb.passkey_confirm = auth_passkey_confirm; |
| break; |
| case BTP_GAP_IO_CAP_NO_INPUT_OUTPUT: |
| cb.cancel = auth_cancel; |
| break; |
| case BTP_GAP_IO_CAP_KEYBOARD_ONLY: |
| cb.cancel = auth_cancel; |
| cb.passkey_entry = auth_passkey_entry; |
| break; |
| case BTP_GAP_IO_CAP_DISPLAY_YESNO: |
| cb.cancel = auth_cancel; |
| cb.passkey_display = auth_passkey_display; |
| cb.passkey_confirm = auth_passkey_confirm; |
| break; |
| default: |
| LOG_WRN("Unhandled io_cap: 0x%x", cp->io_cap); |
| return BTP_STATUS_FAILED; |
| } |
| |
| cb.pairing_accept = auth_pairing_accept; |
| |
| if (bt_conn_auth_cb_register(&cb)) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t pair(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_pair_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_conn_set_security(conn, BT_SECURITY_L2); |
| if (err < 0) { |
| LOG_ERR("Failed to set security: %d", err); |
| bt_conn_unref(conn); |
| return BTP_STATUS_FAILED; |
| } |
| |
| bt_conn_unref(conn); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t unpair(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_unpair_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_INF("Unknown connection"); |
| goto keys; |
| } |
| |
| err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); |
| |
| bt_conn_unref(conn); |
| |
| if (err < 0) { |
| LOG_ERR("Failed to disconnect: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| keys: |
| err = bt_unpair(BT_ID_DEFAULT, &cp->address); |
| if (err < 0) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t passkey_entry(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_passkey_entry_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_conn_auth_passkey_entry(conn, sys_le32_to_cpu(cp->passkey)); |
| bt_conn_unref(conn); |
| |
| if (err < 0) { |
| LOG_ERR("Failed to enter passkey: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t passkey_confirm(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_passkey_confirm_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| if (cp->match) { |
| err = bt_conn_auth_passkey_confirm(conn); |
| if (err < 0) { |
| LOG_ERR("Failed to confirm passkey: %d", err); |
| } |
| } else { |
| err = bt_conn_auth_cancel(conn); |
| if (err < 0) { |
| LOG_ERR("Failed to cancel auth: %d", err); |
| } |
| } |
| |
| bt_conn_unref(conn); |
| |
| if (err < 0) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t conn_param_update(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_conn_param_update_cmd *cp = cmd; |
| struct bt_le_conn_param param = { |
| .interval_min = sys_le16_to_cpu(cp->interval_min), |
| .interval_max = sys_le16_to_cpu(cp->interval_max), |
| .latency = sys_le16_to_cpu(cp->latency), |
| .timeout = sys_le16_to_cpu(cp->timeout), |
| }; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_conn_le_param_update(conn, ¶m); |
| bt_conn_unref(conn); |
| |
| if (err < 0) { |
| LOG_ERR("Failed to update params: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_mitm(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| /* TODO verify if can be done in runtime */ |
| LOG_WRN("Use CONFIG_BT_SMP_ENFORCE_MITM instead"); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_oob_legacy_data(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_oob_legacy_set_data_cmd *cp = cmd; |
| |
| memcpy(oob_legacy_tk, cp->oob_data, 16); |
| |
| bt_le_oob_set_legacy_flag(true); |
| cb.oob_data_request = oob_data_request; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_filter_list(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_filter_list *cp = cmd; |
| int err; |
| |
| if ((cmd_len < sizeof(*cp)) || |
| (cmd_len != sizeof(*cp) + (cp->cnt * sizeof(cp->addr[0])))) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| (void)bt_le_filter_accept_list_clear(); |
| |
| for (int i = 0; i < cp->cnt; i++) { |
| err = bt_le_filter_accept_list_add(&cp->addr[i]); |
| if (err < 0) { |
| return BTP_STATUS_FAILED; |
| } |
| } |
| |
| filter_list_in_use = cp->cnt != 0; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t set_extended_advertising(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_set_extended_advertising_cmd *cp = cmd; |
| struct btp_gap_set_extended_advertising_rp *rp = rsp; |
| |
| LOG_DBG("ext adv settings: %u", cp->settings); |
| |
| if (cp->settings != 0) { |
| atomic_set_bit(¤t_settings, |
| BTP_GAP_SETTINGS_EXTENDED_ADVERTISING); |
| } else { |
| atomic_clear_bit(¤t_settings, |
| BTP_GAP_SETTINGS_EXTENDED_ADVERTISING); |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| #if defined(CONFIG_BT_PER_ADV) |
| static struct bt_data padv[10]; |
| static struct bt_le_per_adv_sync *pa_sync; |
| |
| struct bt_le_per_adv_sync *tester_gap_padv_get(void) |
| { |
| return pa_sync; |
| } |
| |
| static void pa_sync_synced_cb(struct bt_le_per_adv_sync *sync, |
| struct bt_le_per_adv_sync_synced_info *info) |
| { |
| LOG_DBG(""); |
| |
| if (sync == pa_sync) { |
| struct btp_gap_ev_periodic_sync_established_ev ev; |
| |
| bt_addr_le_copy(&ev.address, info->addr); |
| ev.sync_handle = sys_cpu_to_le16(sync->handle); |
| ev.status = 0; |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PERIODIC_SYNC_ESTABLISHED, |
| &ev, sizeof(ev)); |
| } |
| } |
| |
| static void pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync, |
| const struct bt_le_per_adv_sync_term_info *info) |
| { |
| LOG_DBG(""); |
| |
| if (sync == pa_sync) { |
| struct btp_gap_ev_periodic_sync_lost_ev ev; |
| |
| LOG_DBG("PA sync lost with reason %u", info->reason); |
| pa_sync = NULL; |
| |
| ev.sync_handle = sys_cpu_to_le16(sync->handle); |
| ev.reason = info->reason; |
| |
| tester_event(BTP_SERVICE_ID_GAP, BTP_GAP_EV_PERIODIC_SYNC_LOST, |
| &ev, sizeof(ev)); |
| } |
| } |
| |
| static struct bt_le_per_adv_sync_cb pa_sync_cb = { |
| .synced = pa_sync_synced_cb, |
| .term = pa_sync_terminated_cb, |
| }; |
| |
| int tester_gap_padv_configure(const struct bt_le_per_adv_param *param) |
| { |
| int err; |
| struct bt_le_adv_param ext_adv_param = |
| BT_LE_ADV_PARAM_INIT(0, param->interval_min, param->interval_max, NULL); |
| |
| if (ext_adv == NULL) { |
| current_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) | |
| BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING); |
| err = tester_gap_create_adv_instance(&ext_adv_param, BTP_GAP_ADDR_TYPE_IDENTITY, ad, |
| 1, NULL, 0, NULL); |
| if (err != 0) { |
| return -EINVAL; |
| } |
| } |
| |
| /* Set periodic advertising parameters and the required |
| * bit in AD Flags of extended advertising. |
| */ |
| err = bt_le_per_adv_set_param(ext_adv, param); |
| if (err != 0) { |
| LOG_DBG("Failed to set periodic advertising parameters (err %d)\n", err); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t padv_configure(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| uint32_t options = BT_LE_PER_ADV_OPT_NONE; |
| const struct btp_gap_padv_configure_cmd *cp = cmd; |
| struct btp_gap_padv_configure_rp *rp = rsp; |
| |
| if (cp->flags & BTP_GAP_PADV_INCLUDE_TX_POWER) { |
| options |= BT_LE_PER_ADV_OPT_USE_TX_POWER; |
| } |
| |
| err = tester_gap_padv_configure(BT_LE_PER_ADV_PARAM(sys_le16_to_cpu(cp->interval_min), |
| sys_le16_to_cpu(cp->interval_max), |
| options)); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| int tester_gap_padv_start(void) |
| { |
| int err; |
| |
| if (ext_adv == NULL) { |
| return -EINVAL; |
| } |
| |
| if (!atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_ADVERTISING)) { |
| err = tester_gap_start_ext_adv(); |
| if (err != 0) { |
| return -EINVAL; |
| } |
| } |
| |
| /* Enable Periodic Advertising */ |
| err = bt_le_per_adv_start(ext_adv); |
| if (err != 0) { |
| LOG_DBG("Failed to start periodic advertising data: %d", err); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t padv_start(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| struct btp_gap_padv_start_rp *rp = rsp; |
| |
| err = tester_gap_padv_start(); |
| |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| int tester_gap_padv_stop(void) |
| { |
| int err; |
| |
| if (ext_adv == NULL) { |
| return -EINVAL; |
| } |
| |
| /* Enable Periodic Advertising */ |
| err = bt_le_per_adv_stop(ext_adv); |
| if (err != 0) { |
| LOG_DBG("Failed to stop periodic advertising data: %d", err); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t padv_stop(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| struct btp_gap_padv_stop_rp *rp = rsp; |
| |
| err = tester_gap_padv_stop(); |
| |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| rp->current_settings = sys_cpu_to_le32(current_settings); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| int tester_gap_padv_set_data(struct bt_data *per_ad, uint8_t ad_len) |
| { |
| int err; |
| |
| if (ext_adv == NULL) { |
| return -EINVAL; |
| } |
| |
| /* Set Periodic Advertising data */ |
| err = bt_le_per_adv_set_data(ext_adv, per_ad, ad_len); |
| if (err != 0) { |
| LOG_DBG("Failed to set periodic advertising data: %d", err); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t padv_set_data(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| uint8_t padv_len = 0U; |
| const struct btp_gap_padv_set_data_cmd *cp = cmd; |
| |
| for (uint8_t i = 0; i < cp->data_len; padv_len++) { |
| if (padv_len >= ARRAY_SIZE(padv)) { |
| LOG_ERR("padv[] Out of memory"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| padv[padv_len].data_len = cp->data[i++] - 1; |
| padv[padv_len].type = cp->data[i++]; |
| padv[padv_len].data = &cp->data[i]; |
| i += padv[padv_len].data_len; |
| } |
| |
| err = tester_gap_padv_set_data(padv, padv_len); |
| |
| return BTP_STATUS_VAL(err); |
| } |
| |
| int tester_gap_padv_create_sync(struct bt_le_per_adv_sync_param *create_params) |
| { |
| int err; |
| |
| if (pa_sync != NULL) { |
| return -EBUSY; |
| } |
| |
| err = bt_le_per_adv_sync_create(create_params, &pa_sync); |
| |
| if (err != 0) { |
| LOG_DBG("Unable to sync to PA: %d", err); |
| } |
| |
| return err; |
| } |
| |
| int tester_gap_padv_stop_sync(void) |
| { |
| int err; |
| |
| if (pa_sync == NULL) { |
| return -EALREADY; |
| } |
| |
| err = bt_le_per_adv_sync_delete(pa_sync); |
| if (err != 0) { |
| LOG_DBG("Unable to stop sync to PA: %d", err); |
| } |
| |
| return err; |
| } |
| |
| static uint8_t padv_create_sync(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| int err; |
| const struct btp_gap_padv_create_sync_cmd *cp = cmd; |
| struct bt_le_per_adv_sync_param create_params = {0}; |
| |
| bt_addr_le_copy(&create_params.addr, &cp->address); |
| create_params.options = BT_LE_PER_ADV_SYNC_OPT_NONE; |
| create_params.sid = cp->advertiser_sid; |
| create_params.skip = sys_le16_to_cpu(cp->skip); |
| create_params.timeout = sys_le16_to_cpu(cp->sync_timeout); |
| |
| if (cp->flags & BTP_GAP_PADV_CREATE_SYNC_FLAG_REPORTS_DISABLED) { |
| create_params.options |= BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED; |
| } |
| |
| if (cp->flags & BTP_GAP_PADV_CREATE_SYNC_FLAG_FILTER_DUPLICATES) { |
| create_params.options |= BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE; |
| } |
| |
| err = tester_gap_padv_create_sync(&create_params); |
| |
| return BTP_STATUS_VAL(err); |
| } |
| |
| static uint8_t padv_sync_transfer_set_info(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_padv_sync_transfer_set_info_cmd *cp = cmd; |
| (void)cp; |
| |
| /* TODO */ |
| |
| return BTP_STATUS_FAILED; |
| } |
| |
| static uint8_t padv_sync_transfer_start(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_padv_sync_transfer_start_cmd *cp = cmd; |
| (void)cp; |
| |
| /* TODO */ |
| |
| return BTP_STATUS_FAILED; |
| } |
| |
| static uint8_t padv_sync_transfer_recv(const void *cmd, uint16_t cmd_len, |
| void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_gap_padv_sync_transfer_recv_cmd *cp = cmd; |
| (void)cp; |
| |
| /* TODO */ |
| |
| return BTP_STATUS_FAILED; |
| } |
| #endif /* defined(CONFIG_BT_PER_ADV) */ |
| |
| static const struct btp_handler handlers[] = { |
| { |
| .opcode = BTP_GAP_READ_SUPPORTED_COMMANDS, |
| .index = BTP_INDEX_NONE, |
| .expect_len = 0, |
| .func = supported_commands, |
| }, |
| { |
| .opcode = BTP_GAP_READ_CONTROLLER_INDEX_LIST, |
| .index = BTP_INDEX_NONE, |
| .expect_len = 0, |
| .func = controller_index_list, |
| }, |
| { |
| .opcode = BTP_GAP_READ_CONTROLLER_INFO, |
| .expect_len = 0, |
| .func = controller_info, |
| }, |
| { |
| .opcode = BTP_GAP_SET_POWERED, |
| .expect_len = sizeof(struct btp_gap_set_powered_cmd), |
| .func = set_powered, |
| }, |
| { |
| .opcode = BTP_GAP_SET_CONNECTABLE, |
| .expect_len = sizeof(struct btp_gap_set_connectable_cmd), |
| .func = set_connectable, |
| }, |
| { |
| .opcode = BTP_GAP_SET_DISCOVERABLE, |
| .expect_len = sizeof(struct btp_gap_set_discoverable_cmd), |
| .func = set_discoverable, |
| }, |
| { |
| .opcode = BTP_GAP_SET_BONDABLE, |
| .expect_len = sizeof(struct btp_gap_set_bondable_cmd), |
| .func = set_bondable, |
| }, |
| { |
| .opcode = BTP_GAP_START_ADVERTISING, |
| .expect_len = BTP_HANDLER_LENGTH_VARIABLE, |
| .func = start_advertising, |
| }, |
| { |
| .opcode = BTP_GAP_START_DIRECTED_ADV, |
| .expect_len = sizeof(struct btp_gap_start_directed_adv_cmd), |
| .func = start_directed_advertising, |
| }, |
| { |
| .opcode = BTP_GAP_STOP_ADVERTISING, |
| .expect_len = 0, |
| .func = stop_advertising, |
| }, |
| { |
| .opcode = BTP_GAP_START_DISCOVERY, |
| .expect_len = sizeof(struct btp_gap_start_discovery_cmd), |
| .func = start_discovery, |
| }, |
| { |
| .opcode = BTP_GAP_STOP_DISCOVERY, |
| .expect_len = 0, |
| .func = stop_discovery, |
| }, |
| { |
| .opcode = BTP_GAP_CONNECT, |
| .expect_len = sizeof(struct btp_gap_connect_cmd), |
| .func = connect, |
| }, |
| { |
| .opcode = BTP_GAP_DISCONNECT, |
| .expect_len = sizeof(struct btp_gap_disconnect_cmd), |
| .func = disconnect, |
| }, |
| { |
| .opcode = BTP_GAP_SET_IO_CAP, |
| .expect_len = sizeof(struct btp_gap_set_io_cap_cmd), |
| .func = set_io_cap, |
| }, |
| { |
| .opcode = BTP_GAP_PAIR, |
| .expect_len = sizeof(struct btp_gap_pair_cmd), |
| .func = pair, |
| }, |
| { |
| .opcode = BTP_GAP_UNPAIR, |
| .expect_len = sizeof(struct btp_gap_unpair_cmd), |
| .func = unpair, |
| }, |
| { |
| .opcode = BTP_GAP_PASSKEY_ENTRY, |
| .expect_len = sizeof(struct btp_gap_passkey_entry_cmd), |
| .func = passkey_entry, |
| }, |
| { |
| .opcode = BTP_GAP_PASSKEY_CONFIRM, |
| .expect_len = sizeof(struct btp_gap_passkey_confirm_cmd), |
| .func = passkey_confirm, |
| }, |
| { |
| .opcode = BTP_GAP_CONN_PARAM_UPDATE, |
| .expect_len = sizeof(struct btp_gap_conn_param_update_cmd), |
| .func = conn_param_update, |
| }, |
| { |
| .opcode = BTP_GAP_SET_MITM, |
| .expect_len = sizeof(struct btp_gap_set_mitm), |
| .func = set_mitm, |
| }, |
| { |
| .opcode = BTP_GAP_OOB_LEGACY_SET_DATA, |
| .expect_len = sizeof(struct btp_gap_oob_legacy_set_data_cmd), |
| .func = set_oob_legacy_data, |
| }, |
| #if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) |
| { |
| .opcode = BTP_GAP_OOB_SC_GET_LOCAL_DATA, |
| .expect_len = 0, |
| .func = get_oob_sc_local_data, |
| }, |
| { |
| .opcode = BTP_GAP_OOB_SC_SET_REMOTE_DATA, |
| .expect_len = sizeof(struct btp_gap_oob_sc_set_remote_data_cmd), |
| .func = set_oob_sc_remote_data, |
| }, |
| #endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */ |
| { |
| .opcode = BTP_GAP_SET_FILTER_LIST, |
| .expect_len = BTP_HANDLER_LENGTH_VARIABLE, |
| .func = set_filter_list, |
| }, |
| #if defined(CONFIG_BT_EXT_ADV) |
| { |
| .opcode = BTP_GAP_SET_EXTENDED_ADVERTISING, |
| .expect_len = sizeof(struct btp_gap_set_extended_advertising_cmd), |
| .func = set_extended_advertising, |
| }, |
| #if defined(CONFIG_BT_PER_ADV) |
| { |
| .opcode = BTP_GAP_PADV_CONFIGURE, |
| .expect_len = sizeof(struct btp_gap_padv_configure_cmd), |
| .func = padv_configure, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_START, |
| .expect_len = sizeof(struct btp_gap_padv_start_cmd), |
| .func = padv_start, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_STOP, |
| .expect_len = sizeof(struct btp_gap_padv_stop_cmd), |
| .func = padv_stop, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_SET_DATA, |
| .expect_len = BTP_HANDLER_LENGTH_VARIABLE, |
| .func = padv_set_data, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_CREATE_SYNC, |
| .expect_len = sizeof(struct btp_gap_padv_create_sync_cmd), |
| .func = padv_create_sync, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_SYNC_TRANSFER_SET_INFO, |
| .expect_len = sizeof(struct btp_gap_padv_sync_transfer_set_info_cmd), |
| .func = padv_sync_transfer_set_info, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_SYNC_TRANSFER_START, |
| .expect_len = sizeof(struct btp_gap_padv_sync_transfer_start_cmd), |
| .func = padv_sync_transfer_start, |
| }, |
| { |
| .opcode = BTP_GAP_PADV_SYNC_TRANSFER_RECV, |
| .expect_len = sizeof(struct btp_gap_padv_sync_transfer_recv_cmd), |
| .func = padv_sync_transfer_recv, |
| }, |
| #endif /* defined(CONFIG_BT_PER_ADV) */ |
| #endif /* defined(CONFIG_BT_EXT_ADV) */ |
| }; |
| |
| uint8_t tester_init_gap(void) |
| { |
| int err; |
| |
| (void)memset(&cb, 0, sizeof(cb)); |
| bt_conn_auth_cb_register(NULL); |
| cb.pairing_accept = auth_pairing_accept; |
| if (bt_conn_auth_cb_register(&cb)) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_enable(NULL); |
| if (err < 0) { |
| LOG_ERR("Unable to enable Bluetooth: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| atomic_clear(¤t_settings); |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_POWERED); |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE); |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_BONDABLE); |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_LE); |
| #if defined(CONFIG_BT_PRIVACY) |
| atomic_set_bit(¤t_settings, BTP_GAP_SETTINGS_PRIVACY); |
| #endif /* CONFIG_BT_PRIVACY */ |
| |
| bt_conn_cb_register(&conn_callbacks); |
| bt_conn_auth_info_cb_register(&auth_info_cb); |
| |
| #if defined(CONFIG_BT_PER_ADV) |
| bt_le_per_adv_sync_cb_register(&pa_sync_cb); |
| #endif /* defined(CONFIG_BT_PER_ADV) */ |
| |
| tester_register_command_handlers(BTP_SERVICE_ID_GAP, handlers, |
| ARRAY_SIZE(handlers)); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| uint8_t tester_unregister_gap(void) |
| { |
| return BTP_STATUS_SUCCESS; |
| } |