Skip to content

BLE: Introduce ChainableEventHandler and subclasses #13734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 19, 2020
Merged
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
6 changes: 6 additions & 0 deletions connectivity/FEATURE_BLE/include/ble/Gap.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,12 @@ class Gap {
* module to signal events back to the application.
*
* @param handler Application implementation of an EventHandler.
*
* @note Multiple discrete EventHandler instances may be used by adding them
* to a ChainableGapEventHandler and then setting the chain as the primary
* Gap EventHandler using this function.
*
* @see ChainableGapEventHandler
*/
void setEventHandler(EventHandler *handler);

Expand Down
116 changes: 116 additions & 0 deletions connectivity/FEATURE_BLE/include/ble/GattServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#ifndef MBED_GATT_SERVER_H__
#define MBED_GATT_SERVER_H__

#include "platform/mbed_toolchain.h"

#include "ble/common/CallChainOfFunctionPointersWithContext.h"
#include "ble/common/blecommon.h"

Expand Down Expand Up @@ -118,6 +120,84 @@ class GattServer {
(void)connectionHandle;
(void)attMtuSize;
}

/**
* Function invoked when the server has sent data to a client as
* part of a notification/indication.
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*/
virtual void onDataSent(const GattDataSentCallbackParams &params) {
(void)params;
}

/**
* Function invoked when a client writes an attribute
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*/
virtual void onDataWritten(const GattWriteCallbackParams &params) {
(void)params;
}

/**
* Function invoked when a client reads an attribute
*
* @note This functionality may not be available on all underlying stacks.
* Application code may work around that limitation by monitoring read
* requests instead of read events.
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*
* @see GattCharacteristic::setReadAuthorizationCallback()
* @see isOnDataReadAvailable().
*/
virtual void onDataRead(const GattReadCallbackParams &params) {
(void)params;
}

/**
* Function invoked when the GattServer instance is about
* to be shut down. This can result in a call to reset() or BLE::reset().
*/
virtual void onShutdown(const GattServer &server) {
(void)server;
}

/**
* Function invoked when the client has subscribed to characteristic updates
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*/
virtual void onUpdatesEnabled(const GattUpdatesEnabledCallbackParams &params) {
(void)params;
}

/**
* Function invoked when the client has unsubscribed to characteristic updates
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*/
virtual void onUpdatesDisabled(const GattUpdatesDisabledCallbackParams &params) {
(void)params;
}

/**
* Function invoked when an ACK has been received for an
* indication sent to the client.
*
* @note params has a temporary scope and should be copied by the
* application if needed later
*/
virtual void onConfirmationReceived(const GattConfirmationReceivedCallbackParams &params) {
(void)params;
}

protected:
/**
* Prevent polymorphic deletion and avoid unnecessary virtual destructor
Expand Down Expand Up @@ -204,6 +284,12 @@ class GattServer {
* module to signal events back to the application.
*
* @param handler Application implementation of an EventHandler.
*
* @note Multiple discrete EventHandler instances may be used by adding them
* to a ChainableGattServerEventHandler and then setting the chain as the primary
* GattServer EventHandler using this function.
*
* @see ChainableGattServerEventHandler
*/
void setEventHandler(EventHandler *handler);

Expand Down Expand Up @@ -401,6 +487,8 @@ class GattServer {
* @note It is possible to chain together multiple onDataSent callbacks
* (potentially from different modules of an application).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onDataSent(const DataSentCallback_t &callback);

/**
Expand All @@ -413,6 +501,8 @@ class GattServer {
* function.
*/
template <typename T>
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onDataSent(T *objPtr, void (T::*memberPtr)(unsigned count))
{
onDataSent({objPtr, memberPtr});
Expand All @@ -423,6 +513,8 @@ class GattServer {
*
* @return A reference to the DATA_SENT event callback chain.
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
DataSentCallbackChain_t &onDataSent();

/**
Expand All @@ -434,6 +526,8 @@ class GattServer {
* @attention It is possible to set multiple event handlers. Registered
* handlers may be removed with onDataWritten().detach(callback).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onDataWritten(const DataWrittenCallback_t &callback);

/**
Expand All @@ -446,6 +540,8 @@ class GattServer {
* function.
*/
template <typename T>
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onDataWritten(
T *objPtr,
void (T::*memberPtr)(const GattWriteCallbackParams *context)
Expand All @@ -465,6 +561,8 @@ class GattServer {
* @note It is possible to unregister callbacks using
* onDataWritten().detach(callback).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
DataWrittenCallbackChain_t &onDataWritten();

/**
Expand All @@ -485,6 +583,8 @@ class GattServer {
* @attention It is possible to set multiple event handlers. Registered
* handlers may be removed with onDataRead().detach(callback).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
ble_error_t onDataRead(const DataReadCallback_t &callback);

/**
Expand All @@ -496,6 +596,8 @@ class GattServer {
* function.
*/
template <typename T>
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
ble_error_t onDataRead(
T *objPtr,
void (T::*memberPtr)(const GattReadCallbackParams *context)
Expand All @@ -515,6 +617,8 @@ class GattServer {
* @note It is possible to unregister callbacks using
* onDataRead().detach(callback).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
DataReadCallbackChain_t &onDataRead();

/**
Expand All @@ -530,6 +634,8 @@ class GattServer {
* @note It is possible to unregister a callback using
* onShutdown().detach(callback)
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onShutdown(const GattServerShutdownCallback_t &callback);

/**
Expand All @@ -544,6 +650,8 @@ class GattServer {
* function.
*/
template <typename T>
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onShutdown(T *objPtr, void (T::*memberPtr)(const GattServer *))
{
onShutdown({objPtr, memberPtr});
Expand All @@ -560,6 +668,8 @@ class GattServer {
* @note It is possible to unregister callbacks using
* onShutdown().detach(callback).
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
GattServerShutdownCallbackChain_t& onShutdown();

/**
Expand All @@ -568,6 +678,8 @@ class GattServer {
*
* @param[in] callback Event handler being registered.
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onUpdatesEnabled(EventCallback_t callback);

/**
Expand All @@ -576,6 +688,8 @@ class GattServer {
*
* @param[in] callback Event handler being registered.
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onUpdatesDisabled(EventCallback_t callback);

/**
Expand All @@ -586,6 +700,8 @@ class GattServer {
*
* @param[in] callback Event handler being registered.
*/
MBED_DEPRECATED_SINCE("mbed-os-6.3.0", "Individual callback-registering functions have"
"been replaced by GattServer::setEventHandler. Use that function instead.")
void onConfirmationReceived(EventCallback_t callback);

#if !defined(DOXYGEN_ONLY)
Expand Down
121 changes: 121 additions & 0 deletions connectivity/FEATURE_BLE/include/ble/common/ChainableEventHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* mbed Microcontroller Library
* Copyright (c) 2020 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef MBED_CHAINABLEEVENTHANDLER_H_
#define MBED_CHAINABLEEVENTHANDLER_H_

#include <new>

/**
* Base class for chainable EventHandlers. Acts as a collection
* of discrete EventHandlers that can be linked together and
* notified when relevant events happen
*/
template<typename T>
class ChainableEventHandler
{

public:

ChainableEventHandler() { }

~ChainableEventHandler() {
// Clean up all nodes
auto it = head;
while(it) {
node_t* temp = it;
it = it->next;
delete temp;
}
}

/**
* Add an EventHandler to be notified of events sent to
* this ChainableEventHandler
*
* @param[in] event_handler Handler to add to chain
*
* @retval true if adding EventHandler was successful, false otherwise
*/
bool addEventHandler(T* event_handler) {
auto eh = new (std::nothrow) node_t { event_handler, nullptr };
if(!eh) { return false; }
if(!head) {
head = eh;
} else {
auto *it = head;
while(it->next) {
it = it->next;
}
it->next = eh;
}
return true;
}

/**
* Remove an EventHandler previously added with addEventHandler.
*
* @param[in] event_handler Pointer to event handler to remove
*/
void removeEventHandler(T* target) {
node_t* to_remove = head;
if(head->eh == target) {
head = head->next;
} else {
auto* it = head;
while(it->next) {
if(it->next->eh == target) {
to_remove = it->next;
break;
}
it = it->next;
}
if(it->next) {
it->next = to_remove->next;
} else {
to_remove = nullptr;
}
}

delete to_remove;
}

protected:

template<typename... FnArgs, typename... Args>
void execute_on_all(void (T::*fn)(FnArgs...), Args&&... args) {
auto it = head;
while(it) {
// we do not use std::forward, args have to remain lvalues
// as they are passed to multiple handlers
(it->eh->*fn)(args...);
it = it->next;
}
}

private:

struct node_t {
T* eh;
node_t* next = nullptr;
};

node_t *head = nullptr;

};

#endif /* MBED_CHAINABLEEVENTHANDLER_H_ */
Loading