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