1 /* Copyright (c) 2023-2024 Nordic Semiconductor ASA
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <errno.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <testlib/conn.h>
9 #include <zephyr/bluetooth/addr.h>
10 #include <zephyr/bluetooth/bluetooth.h>
11 #include <zephyr/bluetooth/conn.h>
12 #include <zephyr/bluetooth/gatt.h>
13 #include <zephyr/bluetooth/hci_types.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log_core.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/sys/__assert.h>
18 
19 LOG_MODULE_REGISTER(bt_testlib_connect, LOG_LEVEL_INF);
20 
21 struct bt_testlib_connect_closure {
22 	uint8_t conn_cb_connected_err;
23 	struct bt_conn **connp;
24 	struct k_mutex lock;
25 	struct k_condvar conn_cb_connected_match;
26 };
27 
28 /* Context pool (with capacity of one). */
29 static K_SEM_DEFINE(g_ctx_free, 1, 1);
30 static K_MUTEX_DEFINE(g_ctx_lock);
31 static struct bt_testlib_connect_closure *g_ctx;
32 
on_conn_cb_connected(struct bt_conn * conn,uint8_t conn_err)33 static void on_conn_cb_connected(struct bt_conn *conn, uint8_t conn_err)
34 {
35 	/* Loop over each (allocated) item in pool. */
36 
37 	k_mutex_lock(&g_ctx_lock, K_FOREVER);
38 
39 	if (g_ctx && conn == *g_ctx->connp) {
40 		g_ctx->conn_cb_connected_err = conn_err;
41 		k_condvar_signal(&g_ctx->conn_cb_connected_match);
42 	}
43 
44 	k_mutex_unlock(&g_ctx_lock);
45 }
46 
47 BT_CONN_CB_DEFINE(conn_cb) = {
48 	.connected = on_conn_cb_connected,
49 };
50 
bt_testlib_connect(const bt_addr_le_t * peer,struct bt_conn ** connp)51 int bt_testlib_connect(const bt_addr_le_t *peer, struct bt_conn **connp)
52 {
53 	int err;
54 	struct bt_testlib_connect_closure ctx = {
55 		.connp = connp,
56 	};
57 	uint8_t conn_index;
58 
59 	__ASSERT_NO_MSG(connp);
60 	__ASSERT_NO_MSG(*connp == NULL);
61 
62 	k_condvar_init(&ctx.conn_cb_connected_match);
63 
64 	/* If multiple threads call into this funciton, they will wait
65 	 * for their turn here. The Zephyr host does not support
66 	 * concurrent connection creation.
67 	 */
68 	k_sem_take(&g_ctx_free, K_FOREVER);
69 	k_mutex_lock(&g_ctx_lock, K_FOREVER);
70 	g_ctx = &ctx;
71 
72 	err = bt_conn_le_create(peer, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, connp);
73 
74 	if (!err) {
75 		conn_index = bt_conn_index(*connp);
76 		LOG_INF("bt_conn_le_create ok conn %u", conn_index);
77 
78 		k_condvar_wait(&ctx.conn_cb_connected_match, &g_ctx_lock, K_FOREVER);
79 	}
80 
81 	g_ctx = NULL;
82 	k_mutex_unlock(&g_ctx_lock);
83 	k_sem_give(&g_ctx_free);
84 
85 	/* Merge the error codes. The errors from `bt_conn_le_create`
86 	 * are negative, leaving the positive space for the HCI errors
87 	 * from `conn_cb_connected`.
88 	 */
89 	__ASSERT_NO_MSG(err <= 0);
90 	__ASSERT_NO_MSG(0 <= ctx.conn_cb_connected_err);
91 	__ASSERT_NO_MSG(!err || !ctx.conn_cb_connected_err);
92 	err = err + ctx.conn_cb_connected_err;
93 
94 	/* This is just logging. */
95 	switch (err) {
96 	case -ENOMEM:
97 		LOG_INF("bt_conn_le_create -ENOMEM: No free connection objects available.");
98 		break;
99 	case 0:
100 		LOG_INF("conn %u: connected", conn_index);
101 		break;
102 	case BT_HCI_ERR_UNKNOWN_CONN_ID:
103 		LOG_INF("conn %u: timed out", conn_index);
104 		break;
105 	default:
106 		if (err < 0) {
107 			LOG_ERR("bt_conn_le_create err %d", err);
108 		} else {
109 			LOG_ERR("conn %u: BT_HCI_ERR_ 0x%02x", conn_index, err);
110 		}
111 	}
112 
113 	/* Note: `connp` is never unrefed in this funciton, even in case
114 	 * of errors. This is as documented.
115 	 */
116 
117 	return err;
118 }
119