|  | /* | 
|  | * 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_PLATFORM_LOG_LEVEL | 
|  |  | 
|  | LOG_MODULE_REGISTER(LOG_MODULE_NAME); | 
|  |  | 
|  | /* 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 %s", | 
|  | err, bt_hci_err_to_str(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 0x%02x %s", reason, bt_hci_err_to_str(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 IS_ENABLED(CONFIG_OPENTHREAD_TCAT_MULTIRADIO_CAPABILITIES); | 
|  | } | 
|  |  | 
|  | 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_FAST_2, 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) { | 
|  | bt_conn_cb_register(&conn_callbacks); | 
|  | 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; | 
|  | } |