Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/NimBLEDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ std::array<NimBLEClient*, MYNEWT_VAL(BLE_MAX_CONNECTIONS)> NimBLEDevice::m_pClie
std::atomic<bool> NimBLEDevice::m_initialized{false};
uint32_t NimBLEDevice::m_passkey{123456};
std::atomic<bool> NimBLEDevice::m_synced{false};
std::atomic<void*> NimBLEDevice::m_hostTask{nullptr};
bool NimBLEDevice::m_hostStopping{false};
std::atomic_flag NimBLEDevice::m_hostStopLock = ATOMIC_FLAG_INIT;
ble_gap_event_listener NimBLEDevice::m_listener{};
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList{};
uint8_t NimBLEDevice::m_ownAddrType{BLE_OWN_ADDR_PUBLIC};
Expand Down Expand Up @@ -885,7 +888,13 @@ void NimBLEDevice::onSync(void) {
*/
void NimBLEDevice::host_task(void* param) {
NIMBLE_LOGI(LOG_TAG, "NimBLE Started!");
{
NimBLEUtils::SpinGuard guard(m_hostStopLock);
m_hostStopping = false;
}
m_hostTask = ble_npl_get_current_task_id();
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
m_hostTask = nullptr;
nimble_port_freertos_deinit();
} // host_task

Expand Down Expand Up @@ -1037,6 +1046,11 @@ bool NimBLEDevice::init(const std::string& deviceName) {
bool NimBLEDevice::deinit(bool clearAll) {
int rc = 0;
if (isInitialized()) {
{
// Set before the stop event is queued: work queued after it never runs.
NimBLEUtils::SpinGuard guard(m_hostStopLock);
m_hostStopping = true;
}
rc = nimble_port_stop();
if (rc == 0) {
m_initialized.store(false);
Expand All @@ -1059,6 +1073,8 @@ bool NimBLEDevice::deinit(bool clearAll) {
# endif
} else {
NIMBLE_LOGE(LOG_TAG, "nimble_port_stop() failed with error: %d", rc);
NimBLEUtils::SpinGuard guard(m_hostStopLock);
m_hostStopping = false;
return false;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/NimBLEDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ class NimBLEDevice {

private:
static std::atomic<bool> m_synced;
static std::atomic<void*> m_hostTask;
static bool m_hostStopping;
static std::atomic_flag m_hostStopLock;
static std::atomic<bool> m_initialized;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
Expand Down
64 changes: 63 additions & 1 deletion src/NimBLEServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,24 @@
# ifdef USING_NIMBLE_ARDUINO_HEADERS
# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
# include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
# include "nimble/porting/nimble/include/nimble/nimble_port.h"
# else
# include "services/gap/ble_svc_gap.h"
# include "services/gatt/ble_svc_gatt.h"
# include "nimble/nimble_port.h"
# endif

namespace {
// A visibility change waiting for the host task; lives on the waiting caller's stack.
struct VisibilityRequest {
uint16_t handle;
bool visible;
int rc;
ble_npl_sem done;
ble_npl_event ev;
};
} // namespace

# define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0
# define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1

Expand Down Expand Up @@ -837,7 +850,7 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
return;
}

int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0);
int rc = setServiceVisibility(service->getHandle(), false);
if (rc != 0) {
return;
}
Expand All @@ -849,6 +862,55 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
# endif
} // removeService

/**
* @brief Show or hide a service on the host task.
* @details ble_gatts_svc_set_visibility() relinks the service's attributes without a lock while the
* host task walks the same list to answer ATT requests; a caller on another task waits for the host.
* @param [in] svcHandle The handle of the service.
* @param [in] visible True to show the service, false to hide it.
* @return 0 on success, BLE_HS_ENOENT if no service has that handle.
*/
int NimBLEServer::setServiceVisibility(uint16_t svcHandle, bool visible) {
// Before sync no peer can be connected, so nothing walks the list.
if (!NimBLEDevice::m_synced.load(std::memory_order_acquire) ||
ble_npl_get_current_task_id() == NimBLEDevice::m_hostTask) {
return ble_gatts_svc_set_visibility(svcHandle, visible);
}

VisibilityRequest req{svcHandle, visible, 0, {}, {}};
if (ble_npl_sem_init(&req.done, 0) != BLE_NPL_OK) {
NIMBLE_LOGE(LOG_TAG, "Failed to initialize semaphore for service visibility");
return BLE_HS_ENOMEM;
}
ble_npl_event_init(&req.ev, setServiceVisibilityEvent, &req);

{
// Queued ahead of the host's stop event, or not at all.
NimBLEUtils::SpinGuard guard(NimBLEDevice::m_hostStopLock);
if (NimBLEDevice::m_hostStopping) {
ble_npl_event_deinit(&req.ev);
ble_npl_sem_deinit(&req.done);
return BLE_HS_EOS;
}
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &req.ev);
}

ble_npl_sem_pend(&req.done, BLE_NPL_TIME_FOREVER);
ble_npl_event_deinit(&req.ev);
ble_npl_sem_deinit(&req.done);
return req.rc;
} // setServiceVisibility

/**
* @brief Host task handler for a visibility change queued by setServiceVisibility().
* @param [in] ev The event; its argument is the waiting caller's VisibilityRequest.
*/
void NimBLEServer::setServiceVisibilityEvent(ble_npl_event* ev) {
auto* req = static_cast<VisibilityRequest*>(ble_npl_event_get_arg(ev));
req->rc = ble_gatts_svc_set_visibility(req->handle, req->visible);
ble_npl_sem_release(&req->done);
} // setServiceVisibilityEvent

/**
* @brief Adds a service which was either already created but removed from availability,\n
* or created and later added to services list.
Expand Down
2 changes: 2 additions & 0 deletions src/NimBLEServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ class NimBLEServer {
static void gattRegisterCallback(struct ble_gatt_register_ctxt* ctxt, void* arg);
void setServiceChanged();
bool resetGATT();
static int setServiceVisibility(uint16_t svcHandle, bool visible);
static void setServiceVisibilityEvent(struct ble_npl_event* ev);

bool m_gattsStarted : 1;
bool m_svcChanged : 1;
Expand Down
Loading