/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Latency set to zero, to enforce PDU exchange every connection event */ #define CONN_LATENCY 0U /* Arbitrary selected timeout value */ #define CONN_TIMEOUT 400U /* Interval used to run CTE request procedure periodically. * Value is a number of connection events. */ #define CTE_REQ_INTERVAL (CONN_LATENCY + 10U) /* Length of CTE in unit of 8 us */ #define CTE_LEN (0x14U) #define DF_FEAT_ENABLED BIT64(BT_LE_FEAT_BIT_CONN_CTE_RESP) static struct bt_conn *default_conn; static const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MAX, CONN_LATENCY, CONN_TIMEOUT); #if defined(CONFIG_BT_DF_CTE_RX_AOA) static const uint8_t ant_patterns[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA }; #endif /* CONFIG_BT_DF_CTE_RX_AOA */ static void start_scan(void); static const char *cte_type2str(uint8_t type) { switch (type) { case BT_DF_CTE_TYPE_AOA: return "AOA"; case BT_DF_CTE_TYPE_AOD_1US: return "AOD 1 [us]"; case BT_DF_CTE_TYPE_AOD_2US: return "AOD 2 [us]"; default: return "Unknown"; } } static const char *sample_type2str(enum bt_df_iq_sample type) { switch (type) { case BT_DF_IQ_SAMPLE_8_BITS_INT: return "8 bits int"; case BT_DF_IQ_SAMPLE_16_BITS_INT: return "16 bits int"; default: return "Unknown"; } } static const char *packet_status2str(uint8_t status) { switch (status) { case BT_DF_CTE_CRC_OK: return "CRC OK"; case BT_DF_CTE_CRC_ERR_CTE_BASED_TIME: return "CRC not OK, CTE Info OK"; case BT_DF_CTE_CRC_ERR_CTE_BASED_OTHER: return "CRC not OK, Sampled other way"; case BT_DF_CTE_INSUFFICIENT_RESOURCES: return "No resources"; default: return "Unknown"; } } static bool eir_found(struct bt_data *data, void *user_data) { bt_addr_le_t *addr = user_data; uint64_t u64 = 0U; int err; printk("[AD]: %u data_len %u\n", data->type, data->data_len); switch (data->type) { case BT_DATA_LE_SUPPORTED_FEATURES: if (data->data_len > sizeof(u64)) { return true; } (void)memcpy(&u64, data->data, data->data_len); u64 = sys_le64_to_cpu(u64); if (!(u64 & DF_FEAT_ENABLED)) { return true; } err = bt_le_scan_stop(); if (err) { printk("Stop LE scan failed (err %d)\n", err); return true; } err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, &conn_params, &default_conn); if (err) { printk("Create conn failed (err %d)\n", err); start_scan(); } return false; } return true; } static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { char dev[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, dev, sizeof(dev)); printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", dev, type, ad->len, rssi); /* We're only interested in connectable events */ if (type == BT_GAP_ADV_TYPE_ADV_IND || type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) { bt_data_parse(ad, eir_found, (void *)addr); } } static void enable_cte_reqest(void) { int err; const struct bt_df_conn_cte_rx_param cte_rx_params = { #if defined(CONFIG_BT_DF_CTE_RX_AOA) .cte_types = BT_DF_CTE_TYPE_ALL, .slot_durations = 0x2, .num_ant_ids = ARRAY_SIZE(ant_patterns), .ant_ids = ant_patterns, #else .cte_types = BT_DF_CTE_TYPE_AOD_1US | BT_DF_CTE_TYPE_AOD_2US, #endif /* CONFIG_BT_DF_CTE_RX_AOA */ }; const struct bt_df_conn_cte_req_params cte_req_params = { .interval = CTE_REQ_INTERVAL, .cte_length = CTE_LEN, #if defined(CONFIG_BT_DF_CTE_RX_AOA) .cte_type = BT_DF_CTE_TYPE_AOA, #else .cte_type = BT_DF_CTE_TYPE_AOD_2US, #endif /* CONFIG_BT_DF_CTE_RX_AOA */ }; printk("Enable receiving of CTE...\n"); err = bt_df_conn_cte_rx_enable(default_conn, &cte_rx_params); if (err) { printk("failed (err %d)\n", err); return; } printk("success. CTE receive enabled.\n"); printk("Request CTE from peer device...\n"); err = bt_df_conn_cte_req_enable(default_conn, &cte_req_params); if (err) { printk("failed (err %d)\n", err); return; } printk("success. CTE request enabled.\n"); } static void start_scan(void) { int err; /* Use active scanning and disable duplicate filtering to handle any * devices that might update their advertising data at runtime. */ struct bt_le_scan_param scan_param = { .type = BT_LE_SCAN_TYPE_ACTIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = BT_GAP_SCAN_FAST_INTERVAL, .window = BT_GAP_SCAN_FAST_WINDOW, }; err = bt_le_scan_start(&scan_param, device_found); if (err) { printk("Scanning failed to start (err %d)\n", err); return; } printk("Scanning successfully started\n"); } static void connected(struct bt_conn *conn, uint8_t conn_err) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (conn_err) { printk("Failed to connect to %s (%u)\n", addr, conn_err); bt_conn_unref(default_conn); default_conn = NULL; start_scan(); return; } printk("Connected: %s\n", addr); if (conn == default_conn) { enable_cte_reqest(); } } static void disconnected(struct bt_conn *conn, uint8_t reason) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason)); if (default_conn != conn) { return; } bt_conn_unref(default_conn); default_conn = NULL; start_scan(); } static void cte_recv_cb(struct bt_conn *conn, struct bt_df_conn_iq_samples_report const *report) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (report->err == BT_DF_IQ_REPORT_ERR_SUCCESS) { printk("CTE[%s]: samples type: %s, samples count %d, cte type %s, slot durations: " "%u [us], packet status %s, RSSI %i\n", addr, sample_type2str(report->sample_type), report->sample_count, cte_type2str(report->cte_type), report->slot_durations, packet_status2str(report->packet_status), report->rssi); if (IS_ENABLED(CONFIG_DF_CENTRAL_APP_IQ_REPORT_PRINT_IQ_SAMPLES)) { for (uint8_t idx = 0; idx < report->sample_count; idx++) { if (report->sample_type == BT_DF_IQ_SAMPLE_8_BITS_INT) { printk(" IQ[%d]: %d, %d\n", idx, report->sample[idx].i, report->sample[idx].q); } else if (IS_ENABLED( CONFIG_BT_DF_VS_CONN_IQ_REPORT_16_BITS_IQ_SAMPLES)) { printk(" IQ[%" PRIu8 "]: %d, %d\n", idx, report->sample16[idx].i, report->sample16[idx].q); } else { printk("Unhandled vendor specific IQ samples type\n"); break; } } } } else { printk("CTE[%s]: request failed, err %u\n", addr, report->err); } } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, .cte_report_cb = cte_recv_cb, }; int main(void) { int err; err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return 0; } printk("Bluetooth initialized\n"); start_scan(); return 0; }