text: /*
   * Copyright (c) 2022 Nordic Semiconductor ASA
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <zephyr/types.h>
  #include <stddef.h>
  #include <errno.h>
  #include <zephyr.h>
  #include <sys/printk.h>
  
  #include <bluetooth/bluetooth.h>
  #include <bluetooth/hci.h>
  #include <bluetooth/direction.h>
  #include <bluetooth/conn.h>
  #include <bluetooth/uuid.h>
  #include <bluetooth/gatt.h>
  #include <sys/byteorder.h>
  
  /* Latency set to zero, to enforce PDU exchange every connection event */
  #define CONN_LATENCY 0U
  /* Arbitrary selected timeout value */
  #define CONN_TIMEOUT 400U
  /* Inteval 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_CTLR_DF_ANT_SWITCH_RX)
  static const uint8_t ant_patterns[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA };
  #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_RX */
  
  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 *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_CTLR_DF_ANT_SWITCH_RX)
  		.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_CTLR_DF_ANT_SWITCH_RX */
  	};
  
  	const struct bt_df_conn_cte_req_params cte_req_params = {
  		.interval = CTE_REQ_INTERVAL,
  		.cte_length = CTE_LEN,
  #if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
  		.cte_type = BT_DF_CTE_TYPE_AOA,
  #else
  		.cte_type = BT_DF_CTE_TYPE_AOD_2US,
  #endif /* CONFIG_BT_CTLR_DF_ANT_SWITCH_TX */
  	};
  
  	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)\n", addr, 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 count %d, cte type %s, slot durations: %u [us], "
  		       "packet status %s, RSSI %i\n",
  		       addr, report->sample_count, cte_type2str(report->cte_type),
  		       report->slot_durations, packet_status2str(report->packet_status),
  		       report->rssi);
  	} 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,
  };
  
  void main(void)
  {
  	int err;
  
  	err = bt_enable(NULL);
  	if (err) {
  		printk("Bluetooth init failed (err %d)\n", err);
  		return;
  	}
  
  	printk("Bluetooth initialized\n");
  
  	start_scan();
  }
  