/* * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include /* Zephyr OpenThread integration Library */ #include /* OpenThread BLE driver API */ #include #include #include /* 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; }