zephyr/tests/bluetooth/common/testlib/src/security.c
Aleksander Wasaznik bfff0f9f4c Bluetooth: Host: Tests: Move testlib to common
This is the first patch with the goal of establishing a common library
for Bluetooth utilities that are commonly used for setup in Bluetooth
Host tests. The ultimate goal is to remove redundant (near) copies of
these utilities and other more ad-hoc solutions.

This patch moves one instance of testlib (from
tests/bsim/bluetooth/host/att/long_read) to
tests/bluetooth/common/testlib and makes it a proper CMake library.

The long_read test is updated to link to the new CMake library.

Further changes and de-duplication will come in later patches.

Signed-off-by: Aleksander Wasaznik <aleksander.wasaznik@nordicsemi.no>
2023-11-07 10:09:33 +01:00

109 lines
3.1 KiB
C

/* Copyright (c) 2023 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_testlib_security, LOG_LEVEL_INF);
struct testlib_security_ctx {
enum bt_security_err result;
struct bt_conn *conn;
bt_security_t new_minimum;
struct k_condvar done;
};
/* Context pool (with capacity of one). */
static K_SEM_DEFINE(g_ctx_free, 1, 1);
static K_MUTEX_DEFINE(g_ctx_lock);
static struct testlib_security_ctx *g_ctx;
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
{
LOG_INF("conn %u level %d err %d", bt_conn_index(conn), level, err);
/* Mutex operations establish a happens-before relationship. This
* ensures variables have the expected values despite non-atomic
* accesses.
*/
k_mutex_lock(&g_ctx_lock, K_FOREVER);
if (g_ctx && (g_ctx->conn == conn)) {
g_ctx->result = err;
/* Assumption: A security error means there will be further
* security changes for this connection.
*/
if (err || level >= g_ctx->new_minimum) {
k_condvar_signal(&g_ctx->done);
}
}
k_mutex_unlock(&g_ctx_lock);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.security_changed = security_changed,
};
int bt_testlib_secure(struct bt_conn *conn, bt_security_t new_minimum)
{
int api_err = 0;
struct testlib_security_ctx ctx = {
.conn = conn,
.new_minimum = new_minimum,
};
k_condvar_init(&ctx.done);
/* The semaphore allocates `g_ctx` to this invocation of
* `bt_testlib_secure`, in case this function is called from multiple
* threads in parallel.
*/
k_sem_take(&g_ctx_free, K_FOREVER);
/* The mutex synchronizes this function with `security_changed()`. */
k_mutex_lock(&g_ctx_lock, K_FOREVER);
/* Do the thing. */
api_err = bt_conn_set_security(conn, new_minimum);
/* Holding the mutex will pause any thread entering
* `security_changed_cb`, delaying it until `k_condvar_wait`. This
* ensures that the condition variable is signaled while this thread is
* in `k_condvar_wait`, even if the event happens before, e.g. between
* `bt_conn_get_security` and `k_condvar_wait`.
*
* If the security level is already satisfied, there is no point in
* waiting, and it would deadlock if security was already satisfied
* before the mutex was taken, `bt_conn_set_security` will result in no
* operation.
*/
if (!api_err && bt_conn_get_security(conn) < new_minimum) {
/* Waiting on a condvar releases the mutex and waits for a
* signal on the condvar, atomically, without a gap between the
* release and wait. The mutex is locked again before returning.
*/
g_ctx = &ctx;
k_condvar_wait(&ctx.done, &g_ctx_lock, K_FOREVER);
g_ctx = NULL;
}
k_mutex_unlock(&g_ctx_lock);
k_sem_give(&g_ctx_free);
if (api_err) {
__ASSERT_NO_MSG(api_err < 0);
return api_err;
}
__ASSERT_NO_MSG(ctx.result >= 0);
return ctx.result;
}