Adds implementation of tcat advertisement API. Signed-off-by: Przemyslaw Bida <przemyslaw.bida@nordicsemi.no>
505 lines
14 KiB
C
505 lines
14 KiB
C
/*
|
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/types.h>
|
|
#include <zephyr/sys/ring_buffer.h>
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/hci.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/uuid.h>
|
|
#include <zephyr/bluetooth/gatt.h>
|
|
|
|
/* Zephyr OpenThread integration Library */
|
|
#include <zephyr/net/openthread.h>
|
|
|
|
/* OpenThread BLE driver API */
|
|
#include <openthread/error.h>
|
|
#include <openthread/platform/ble.h>
|
|
#include <openthread/tcat.h>
|
|
|
|
/* Zephyr Logging */
|
|
|
|
#define LOG_MODULE_NAME net_openthread_tcat
|
|
#define LOG_LEVEL CONFIG_OPENTHREAD_LOG_LEVEL
|
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
|
|
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
|
|
|
|
/* BLE connection constants as defined in thread specification. */
|
|
#define TOBLE_SERVICE_UUID 0xfffb
|
|
#define RX_CHARACTERISTIC_UUID \
|
|
BT_UUID_128_ENCODE(0x6bd10d8b, 0x85a7, 0x4e5a, 0xba2d, 0xc83558a5f220)
|
|
#define TX_CHARACTERISTIC_UUID \
|
|
BT_UUID_128_ENCODE(0x7fddf61f, 0x280a, 0x4773, 0xb448, 0xba1b8fe0dd69)
|
|
|
|
#define BT_UUID_TCAT_SERVICE BT_UUID_DECLARE_16(TOBLE_SERVICE_UUID)
|
|
#define BT_UUID_TCAT_SERVICE_RX BT_UUID_DECLARE_128(RX_CHARACTERISTIC_UUID)
|
|
#define BT_UUID_TCAT_SERVICE_TX BT_UUID_DECLARE_128(TX_CHARACTERISTIC_UUID)
|
|
|
|
#define PLAT_BLE_THREAD_DEALY 500
|
|
#define PLAT_BLE_MSG_DATA_MAX CONFIG_BT_L2CAP_TX_MTU /* must match the maximum MTU size used */
|
|
|
|
#define PLAT_BLE_MSG_CONNECT (PLAT_BLE_MSG_DATA_MAX + 1U)
|
|
#define PLAT_BLE_MSG_DISCONNECT (PLAT_BLE_MSG_CONNECT + 1U)
|
|
|
|
/* Zephyr Kernel Objects */
|
|
|
|
static void ot_plat_ble_thread(void *, void *, void *);
|
|
static uint8_t ot_plat_ble_msg_buf[PLAT_BLE_MSG_DATA_MAX];
|
|
|
|
static K_SEM_DEFINE(ot_plat_ble_init_semaphore, 0, 1);
|
|
static K_SEM_DEFINE(ot_plat_ble_event_semaphore, 0, K_SEM_MAX_LIMIT);
|
|
RING_BUF_DECLARE(ot_plat_ble_ring_buf, CONFIG_OPENTHREAD_BLE_TCAT_RING_BUF_SIZE);
|
|
static K_THREAD_DEFINE(ot_plat_ble_tid, CONFIG_OPENTHREAD_BLE_TCAT_THREAD_STACK_SIZE,
|
|
ot_plat_ble_thread, NULL, NULL, NULL, 5, 0, PLAT_BLE_THREAD_DEALY);
|
|
|
|
/* OpenThread Objects */
|
|
|
|
static otInstance *ble_openthread_instance;
|
|
|
|
/* BLE service Objects */
|
|
|
|
/* forward declaration for callback functions */
|
|
static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
|
|
uint16_t len, uint16_t offset, uint8_t flags);
|
|
static void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value);
|
|
|
|
/* Service Declaration and Registration */
|
|
BT_GATT_SERVICE_DEFINE(my_service, BT_GATT_PRIMARY_SERVICE(BT_UUID_TCAT_SERVICE),
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_RX,
|
|
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, NULL,
|
|
on_receive, NULL),
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_TX, BT_GATT_CHRC_NOTIFY,
|
|
BT_GATT_PERM_READ, NULL, NULL, NULL),
|
|
BT_GATT_CCC(on_cccd_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),);
|
|
|
|
/* Zephyr BLE Objects */
|
|
|
|
/* forward declaration for callback functions */
|
|
static void connected(struct bt_conn *conn, uint8_t err);
|
|
static void disconnected(struct bt_conn *conn, uint8_t reason);
|
|
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param);
|
|
static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency,
|
|
uint16_t timeout);
|
|
|
|
static struct bt_conn *ot_plat_ble_connection;
|
|
|
|
static struct bt_conn_cb conn_callbacks = {.connected = connected,
|
|
.disconnected = disconnected,
|
|
.le_param_req = le_param_req,
|
|
.le_param_updated = le_param_updated};
|
|
|
|
static uint8_t service_data[OT_TCAT_ADVERTISEMENT_MAX_LEN] = {0};
|
|
static const uint8_t service_data_size = ARRAY_SIZE(service_data);
|
|
|
|
static struct bt_data ad[] = {
|
|
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
|
BT_DATA(BT_DATA_SVC_DATA16, service_data, service_data_size),
|
|
};
|
|
|
|
static struct bt_data sd[] = {
|
|
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(TOBLE_SERVICE_UUID)),
|
|
BT_DATA(BT_DATA_SVC_DATA16, service_data, service_data_size),
|
|
};
|
|
|
|
/* Zephyr BLE Message Queue and Thread */
|
|
|
|
static bool ot_plat_ble_queue_msg(const uint8_t *aData, uint16_t aLen, int8_t aRssi)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
uint16_t len = 0;
|
|
|
|
if (aLen <= PLAT_BLE_MSG_DATA_MAX && aData == NULL) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
k_sched_lock();
|
|
|
|
len = sizeof(aLen) + sizeof(aRssi) + ((aLen <= PLAT_BLE_MSG_DATA_MAX) ? aLen : 0);
|
|
|
|
if (ring_buf_space_get(&ot_plat_ble_ring_buf) >= len) {
|
|
ring_buf_put(&ot_plat_ble_ring_buf, (uint8_t *)&aLen, sizeof(aLen));
|
|
ring_buf_put(&ot_plat_ble_ring_buf, &aRssi, sizeof(aRssi));
|
|
if (aLen <= PLAT_BLE_MSG_DATA_MAX) {
|
|
ring_buf_put(&ot_plat_ble_ring_buf, aData, aLen);
|
|
}
|
|
k_sem_give(&ot_plat_ble_event_semaphore);
|
|
} else {
|
|
error = OT_ERROR_NO_BUFS;
|
|
}
|
|
|
|
k_sched_unlock();
|
|
|
|
return error;
|
|
}
|
|
|
|
static void ot_plat_ble_thread(void *unused1, void *unused2, void *unused3)
|
|
{
|
|
ARG_UNUSED(unused1);
|
|
ARG_UNUSED(unused2);
|
|
ARG_UNUSED(unused3);
|
|
|
|
uint16_t len;
|
|
int8_t rssi;
|
|
otBleRadioPacket my_packet;
|
|
|
|
LOG_INF("%s started", __func__);
|
|
|
|
while (1) {
|
|
k_sem_take(&ot_plat_ble_event_semaphore, K_FOREVER);
|
|
ring_buf_get(&ot_plat_ble_ring_buf, (uint8_t *)&len, sizeof(len));
|
|
ring_buf_get(&ot_plat_ble_ring_buf, &rssi, sizeof(rssi));
|
|
if (len <= PLAT_BLE_MSG_DATA_MAX) {
|
|
ring_buf_get(&ot_plat_ble_ring_buf, ot_plat_ble_msg_buf, len);
|
|
}
|
|
|
|
openthread_api_mutex_lock(openthread_get_default_context());
|
|
|
|
if (len <= PLAT_BLE_MSG_DATA_MAX) {
|
|
/* The packet parameter in otPlatBleGattServerOnWriteRequest is not const.
|
|
* Re-write all members.
|
|
*/
|
|
my_packet.mValue = ot_plat_ble_msg_buf;
|
|
my_packet.mPower = rssi;
|
|
my_packet.mLength = len;
|
|
otPlatBleGattServerOnWriteRequest(ble_openthread_instance, 0, &my_packet);
|
|
} else if (len == PLAT_BLE_MSG_CONNECT) {
|
|
otPlatBleGapOnConnected(ble_openthread_instance, 0);
|
|
} else if (len == PLAT_BLE_MSG_DISCONNECT) {
|
|
otPlatBleGapOnDisconnected(ble_openthread_instance, 0);
|
|
}
|
|
openthread_api_mutex_unlock(openthread_get_default_context());
|
|
}
|
|
}
|
|
|
|
/* Zephyr BLE service callbacks */
|
|
|
|
/* This function is called whenever the RX Characteristic has been written to by a Client */
|
|
static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
|
|
uint16_t len, uint16_t offset, uint8_t flags)
|
|
{
|
|
LOG_DBG("Received data, handle %" PRIu16 ", len %" PRIu16, attr->handle, len);
|
|
|
|
otError error = ot_plat_ble_queue_msg(buf, len, 0);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error queuing message: %s", otThreadErrorToString(error));
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* This function is called whenever a Notification has been sent by the TX Characteristic */
|
|
static void on_sent(struct bt_conn *conn, void *user_data)
|
|
{
|
|
ARG_UNUSED(conn);
|
|
ARG_UNUSED(user_data);
|
|
|
|
LOG_DBG("Data sent");
|
|
}
|
|
|
|
/* This function is called whenever the CCCD register has been changed by the client */
|
|
void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
|
{
|
|
uint16_t mtu;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
ARG_UNUSED(attr);
|
|
|
|
switch (value) {
|
|
case BT_GATT_CCC_NOTIFY:
|
|
|
|
error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_CONNECT, 0);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error queuing message: %s", otThreadErrorToString(error));
|
|
}
|
|
|
|
error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error));
|
|
}
|
|
|
|
LOG_INF("CCCD update (mtu=%" PRIu16 ")!", mtu);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle,
|
|
const otBleRadioPacket *aPacket)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
/* TO DO change to indications. */
|
|
const struct bt_gatt_attr *attr = &my_service.attrs[3];
|
|
|
|
struct bt_gatt_notify_params params = {.uuid = BT_UUID_TCAT_SERVICE_TX,
|
|
.attr = attr,
|
|
.data = aPacket->mValue,
|
|
.len = aPacket->mLength,
|
|
.func = on_sent};
|
|
|
|
LOG_DBG("Send data, handle %d, len %d", attr->handle, aPacket->mLength);
|
|
|
|
/* Only one connection supported */
|
|
if (aHandle != 0) {
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
if (ot_plat_ble_connection == NULL) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
/* Check whether notifications are enabled or not */
|
|
if (bt_gatt_is_subscribed(ot_plat_ble_connection, attr, BT_GATT_CCC_NOTIFY)) {
|
|
if (bt_gatt_notify_cb(ot_plat_ble_connection, ¶ms)) {
|
|
LOG_WRN("Error, unable to send notification");
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
} else {
|
|
LOG_WRN("Warning, notification not enabled on the selected attribute");
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
if (ot_plat_ble_connection == NULL) {
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
if (aMtu != NULL) {
|
|
*aMtu = bt_gatt_get_mtu(ot_plat_ble_connection);
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapDisconnect(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
if (ot_plat_ble_connection == NULL) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
if (bt_conn_disconnect(ot_plat_ble_connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) {
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
/* Zephyr BLE callbacks */
|
|
|
|
static void connected(struct bt_conn *conn, uint8_t err)
|
|
{
|
|
struct bt_conn_info info;
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
uint16_t mtu;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
ot_plat_ble_connection = bt_conn_ref(conn);
|
|
|
|
if (err) {
|
|
LOG_WRN("Connection failed (err %u)", err);
|
|
return;
|
|
} else if (bt_conn_get_info(conn, &info)) {
|
|
LOG_WRN("Could not parse connection info");
|
|
} else {
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error));
|
|
}
|
|
|
|
LOG_INF("Connection established (mtu=%" PRIu16 ")!", mtu);
|
|
}
|
|
}
|
|
|
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
LOG_INF("Disconnected (reason %" PRIu8 ")", reason);
|
|
|
|
if (ot_plat_ble_connection) {
|
|
bt_conn_unref(ot_plat_ble_connection);
|
|
ot_plat_ble_connection = NULL;
|
|
|
|
error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_DISCONNECT, 0);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error queuing message: %s", otThreadErrorToString(error));
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency,
|
|
uint16_t timeout)
|
|
{
|
|
struct bt_conn_info info;
|
|
char addr[BT_ADDR_LE_STR_LEN];
|
|
uint16_t mtu;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (bt_conn_get_info(conn, &info)) {
|
|
LOG_INF("Could not parse connection info");
|
|
} else {
|
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
|
|
|
error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error));
|
|
}
|
|
|
|
LOG_INF("Connection parameters updated (mtu=%" PRIu16 ")!", mtu);
|
|
}
|
|
}
|
|
|
|
static void bt_ready(int err)
|
|
{
|
|
if (err) {
|
|
LOG_WRN("BLE init failed with error code %d", err);
|
|
return;
|
|
}
|
|
|
|
bt_conn_cb_register(&conn_callbacks);
|
|
k_sem_give(&ot_plat_ble_init_semaphore); /* BLE stack up an running */
|
|
}
|
|
|
|
void otPlatBleGetLinkCapabilities(otInstance *aInstance,
|
|
otBleLinkCapabilities *aBleLinkCapabilities)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
aBleLinkCapabilities->mGattNotifications = 1;
|
|
aBleLinkCapabilities->mL2CapDirect = 0;
|
|
aBleLinkCapabilities->mRsv = 0;
|
|
}
|
|
|
|
bool otPlatBleSupportsMultiRadio(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
return false;
|
|
}
|
|
|
|
otError otPlatBleGetAdvertisementBuffer(otInstance *aInstance, uint8_t **aAdvertisementBuffer)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
*aAdvertisementBuffer = service_data;
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData,
|
|
uint16_t aAdvertisementLen)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
if (aAdvertisementLen > OT_TCAT_ADVERTISEMENT_MAX_LEN || aAdvertisementData == NULL) {
|
|
LOG_ERR("Invalid TCAT Advertisement parameters advlen: %d", aAdvertisementLen);
|
|
return OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
ad[1].data_len = (uint8_t)aAdvertisementLen;
|
|
sd[1].data_len = (uint8_t)aAdvertisementLen;
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
ARG_UNUSED(aInterval);
|
|
|
|
int err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
|
|
|
if (err != 0 && err != -EALREADY) {
|
|
LOG_WRN("Advertising failed to start (err %d)", err);
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
LOG_INF("Advertising successfully started");
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvStop(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
int err = bt_le_adv_stop();
|
|
|
|
if (err != 0 && err != -EALREADY) {
|
|
LOG_WRN("Advertisement failed to stop (err %d)", err);
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
/* Zephyr BLE initialization */
|
|
|
|
otError otPlatBleEnable(otInstance *aInstance)
|
|
{
|
|
int err;
|
|
|
|
ble_openthread_instance = aInstance;
|
|
err = bt_enable(bt_ready);
|
|
|
|
if (err != 0 && err != -EALREADY) {
|
|
LOG_WRN("BLE enable failed with error code %d", err);
|
|
return OT_ERROR_FAILED;
|
|
} else if (err == -EALREADY) {
|
|
err = k_sem_take(&ot_plat_ble_init_semaphore, K_MSEC(500));
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
err = k_sem_take(&ot_plat_ble_init_semaphore, K_MSEC(500));
|
|
|
|
if (!err) {
|
|
LOG_INF("Bluetooth initialized");
|
|
} else {
|
|
LOG_INF("BLE initialization did not complete in time");
|
|
return OT_ERROR_FAILED;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleDisable(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
/* This function intentionally does nothing since disabling advertisement disables BLE
|
|
* stack.
|
|
*/
|
|
return OT_ERROR_NONE;
|
|
}
|