summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-02-05 13:10:02 +0100
committerSven Gothel <[email protected]>2022-02-05 13:10:02 +0100
commite570552af8de36d78e369591384082aa0b59e88b (patch)
tree8c360a162c52860c25a8006b32b331b677609f00
parent49933e253b671c6017c925eb09c5359e86a4c154 (diff)
BTGattHandler::NativeGattCharListener: Add optional low- and high-level user notification callbacks, allowing better protocol tracking for DBGattServer::Mode:FWD (repeater)
-rw-r--r--api/direct_bt/BTGattHandler.hpp142
-rw-r--r--examples/dbt_repeater00.cpp113
-rw-r--r--src/direct_bt/BTGattHandler.cpp87
-rw-r--r--src/direct_bt/BTGattServerHandler.cpp164
4 files changed, 475 insertions, 31 deletions
diff --git a/api/direct_bt/BTGattHandler.hpp b/api/direct_bt/BTGattHandler.hpp
index 9f2c5c17..10b849e7 100644
--- a/api/direct_bt/BTGattHandler.hpp
+++ b/api/direct_bt/BTGattHandler.hpp
@@ -285,6 +285,17 @@ namespace direct_bt {
*/
class NativeGattCharListener {
public:
+ struct Section {
+ /** start point, inclusive */
+ uint16_t start;
+ /** end point, exclusive */
+ uint16_t end;
+
+ Section(uint16_t s, uint16_t e) : start(s), end(e) {}
+
+ std::string toString() { return "["+std::to_string(start)+".."+std::to_string(end-1)+"]"; }
+ };
+
/**
* Called from native BLE stack, initiated by a received notification.
* @param source BTDevice origin of this notification
@@ -307,6 +318,78 @@ namespace direct_bt {
const jau::TROOctets& charValue, const uint64_t timestamp,
const bool confirmationSent) = 0;
+ /**
+ * Informal low-level notification of AttPDUMsg requests to this GATTRole::Server, optional
+ *
+ * @param pduRequest the request
+ * @param serverDest the GATTRole::Server receiver device, never nullptr
+ * @param clientSource the GATTRole::Client source device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ virtual void requestSent([[maybe_unused]] const AttPDUMsg& pduRequest,
+ [[maybe_unused]] BTDeviceRef serverDest,
+ [[maybe_unused]] BTDeviceRef clientSource) { }
+
+ /**
+ * Informal low-level notification of AttPDUMsg responses from this GATTRole::Server, optional.
+ *
+ * @param pduReply the response
+ * @param serverSource the GATTRole::Server source device, never nullptr
+ * @param clientDest the GATTRole::Client receiver device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ virtual void replyReceived([[maybe_unused]] const AttPDUMsg& pduReply,
+ [[maybe_unused]] BTDeviceRef serverSource,
+ [[maybe_unused]] BTDeviceRef clientDest) { }
+
+ /**
+ * Informal notification about a completed write request sent to this GATTRole::Server, optional.
+ *
+ * @param handle the GATT characteristic or descriptor handle, requested to be written
+ * @param data the data requested to be written
+ * @param sections list of NativeGattCharListener::Section within given data, requested to be written. Overlapping consecutive sections have already been merged.
+ * @param with_response true if the write requests expects a response, i.e. via AttPDUMsg::Opcode::WRITE_REQ or AttPDUMsg::Opcode::EXECUTE_WRITE_REQ
+ * @param serverDest the GATTRole::Server receiver device, never nullptr
+ * @param clientSource the GATTRole::Client source device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ virtual void writeRequest([[maybe_unused]] const uint16_t handle,
+ [[maybe_unused]] const jau::TROOctets& data,
+ [[maybe_unused]] const jau::darray<Section>& sections,
+ [[maybe_unused]] const bool with_response,
+ [[maybe_unused]] BTDeviceRef serverDest,
+ [[maybe_unused]] BTDeviceRef clientSource) { }
+
+ /**
+ * Informal notification about a write response received from this GATTRole::Server, optional.
+ *
+ * @param pduReply the write response
+ * @param error_code in case of an AttErrorRsp reply, the AttErrorRsp::ErrorCode is passed convenience, otherwise AttErrorRsp::ErrorCode::NO_ERROR.
+ * @param serverSource the GATTRole::Server source device, never nullptr
+ * @param clientDest the GATTRole::Client receiver device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ virtual void writeResponse([[maybe_unused]] const AttPDUMsg& pduReply,
+ [[maybe_unused]] const AttErrorRsp::ErrorCode error_code,
+ [[maybe_unused]] BTDeviceRef serverSource,
+ [[maybe_unused]] BTDeviceRef clientDest) { }
+
+
+ /**
+ * Informal notification about a complete read request and response to and from this GATTRole::Server, optional.
+ *
+ * @param handle the GATT characteristic or descriptor handle, requested to be written
+ * @param value_offset the value offset of the data to be read
+ * @param pduReply the response
+ * @param error_reply in case of an AttErrorRsp reply, the AttErrorRsp::ErrorCode is passed convenience, otherwise AttErrorRsp::ErrorCode::NO_ERROR.
+ * @param data_reply the replied read data at given value_offset passed for convenience
+ * @param serverReplier the GATTRole::Server replier device, never nullptr
+ * @param clientRequester the GATTRole::Client requester device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ virtual void readResponse([[maybe_unused]] const uint16_t handle,
+ [[maybe_unused]] const uint16_t value_offset,
+ [[maybe_unused]] const AttPDUMsg& pduReply,
+ [[maybe_unused]] const AttErrorRsp::ErrorCode error_reply,
+ [[maybe_unused]] const jau::TROOctets& data_reply,
+ [[maybe_unused]] BTDeviceRef serverReplier,
+ [[maybe_unused]] BTDeviceRef clientRequester) { }
+
virtual ~NativeGattCharListener() noexcept {}
/** Return a simple description about this instance. */
@@ -327,6 +410,7 @@ namespace direct_bt {
{ return !(*this == rhs); }
};
typedef jau::cow_darray<std::shared_ptr<NativeGattCharListener>> NativeGattCharListenerList_t;
+ typedef jau::darray<NativeGattCharListener::Section> NativeGattCharSections_t;
typedef jau::cow_darray<std::shared_ptr<BTGattCharListener>> BTGattCharListenerList_t;
@@ -778,6 +862,64 @@ namespace direct_bt {
void printCharListener() noexcept;
/**
+ * Notify all NativeGattCharListener about a low-level AttPDUMsg request being sent to this GATTRole::Server.
+ *
+ * This functionality has an informal character only.
+ *
+ * @param pduRequest the request
+ * @param clientSource the GATTRole::Client source device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ void notifyNativeRequestSent(const AttPDUMsg& pduRequest, BTDeviceRef clientSource) noexcept;
+
+ /**
+ * Notify all NativeGattCharListener about a low-level AttPDUMsg reply being received from this GATTRole::Server.
+ *
+ * @param pduReply the response
+ * @param clientDest the GATTRole::Client receiver device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ void notifyNativeReplyReceived(const AttPDUMsg& pduReply, BTDeviceRef clientDest) noexcept;
+
+ /**
+ * Notify all NativeGattCharListener about a completed write request sent to this GATTRole::Server.
+ *
+ * This functionality has an informal character only.
+ *
+ * @param handle the GATT characteristic or descriptor handle, requested to be written
+ * @param data the data requested to be written
+ * @param sections list of NativeGattCharListener::Section within given data, requested to be written. Overlapping consecutive sections have already been merged.
+ * @param with_response true if the write requests expects a response, i.e. via AttPDUMsg::Opcode::WRITE_REQ or AttPDUMsg::Opcode::EXECUTE_WRITE_REQ
+ * @param clientSource the GATTRole::Client source device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ void notifyNativeWriteRequest(const uint16_t handle, const jau::TROOctets& data, const NativeGattCharSections_t& sections, const bool with_response, BTDeviceRef clientSource) noexcept;
+
+ /**
+ * Notify all NativeGattCharListener about a write response received from this GATTRole::Server.
+ *
+ * This functionality has an informal character only.
+ *
+ * @param pduReply the response
+ * @param error_code in case of an AttErrorRsp reply, the AttErrorRsp::ErrorCode is passed convenience, otherwise AttErrorRsp::ErrorCode::NO_ERROR.
+ * @param clientDest the GATTRole::Client receiver device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ void notifyNativeWriteResponse(const AttPDUMsg& pduReply, const AttErrorRsp::ErrorCode error_code, BTDeviceRef clientDest) noexcept;
+
+ /**
+ * Notify all NativeGattCharListener about a completed read request and response to and from this GATTRole::Server.
+ *
+ * This functionality has an informal character only.
+ *
+ * @param handle the GATT characteristic or descriptor handle, requested to be written
+ * @param value_offset the value offset of the data to be read
+ * @param pduReply the response
+ * @param error_reply in case of an AttErrorRsp reply, the AttErrorRsp::ErrorCode is passed convenience, otherwise AttErrorRsp::ErrorCode::NO_ERROR.
+ * @param data_reply the replied read data at given value_offset passed for convenience
+ * @param clientRequester the GATTRole::Client requester device, only known and not nullptr for DBGattServer::Mode:FWD GattServerHandler
+ */
+ void notifyNativeReadResponse(const uint16_t handle, const uint16_t value_offset,
+ const AttPDUMsg& pduReply, const AttErrorRsp::ErrorCode error_reply, const jau::TROOctets& data_reply,
+ BTDeviceRef clientRequester) noexcept;
+
+ /**
* Enable or disable sending an immediate confirmation for received indication events from the device.
* <p>
* Default value is true.
diff --git a/examples/dbt_repeater00.cpp b/examples/dbt_repeater00.cpp
index a8ffc53a..caa70968 100644
--- a/examples/dbt_repeater00.cpp
+++ b/examples/dbt_repeater00.cpp
@@ -281,24 +281,24 @@ class NativeGattToServerCharListener : public BTGattHandler::NativeGattCharListe
NativeGattToServerCharListener() {}
+ BTDeviceRef getToClient() noexcept {
+ jau::sc_atomic_critical sync(sync_data);
+ return connectedDeviceToClient;
+ }
+
void notificationReceived(BTDeviceRef source, const uint16_t char_handle,
const TROOctets& char_value, const uint64_t timestamp) override
{
- const uint64_t tR = jau::getCurrentMilliseconds();
- BTDeviceRef devToClient;
- std::string devToClientS;
- {
- jau::sc_atomic_critical sync(sync_data);
- devToClient = connectedDeviceToClient;
- if( nullptr != devToClient ) {
- devToClientS = devToClient->toString();
- }
- }
- fprintf_td(stderr, "** Server Characteristic-Notify: handle %s, td %" PRIu64 " from %s ******\n",
- jau::to_hexstring(char_handle).c_str(), (tR-timestamp), source->toString().c_str());
- fprintf_td(stderr, "** Fwd to client: %s ******\n", devToClientS.c_str());
- fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str());
- fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
+ (void)timestamp;
+ BTDeviceRef devToClient = getToClient();
+ std::string devToClientS = nullptr != devToClient ? devToClient->getAddressAndType().address.toString() : "nil";
+ std::string devFromServerS = source->getAddressAndType().address.toString();
+
+ fprintf_td(stderr, "%s* -> %s: Notify: handle %s\n",
+ devFromServerS.c_str(), devToClientS.c_str(), jau::to_hexstring(char_handle).c_str());
+ fprintf_td(stderr, " raw : %s\n", char_value.toString().c_str());
+ fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
+ fprintf_td(stderr, "\n");
std::shared_ptr<BTGattHandler> gh = nullptr != devToClient ? devToClient->getGattHandler() : nullptr;
if( nullptr != gh ) {
gh->sendNotification(char_handle, char_value);
@@ -309,26 +309,79 @@ class NativeGattToServerCharListener : public BTGattHandler::NativeGattCharListe
const TROOctets& char_value, const uint64_t timestamp,
const bool confirmationSent) override
{
- const uint64_t tR = jau::getCurrentMilliseconds();
- BTDeviceRef devToClient;
- std::string devToClientS;
- {
- jau::sc_atomic_critical sync(sync_data);
- devToClient = connectedDeviceToClient;
- if( nullptr != devToClient ) {
- devToClientS = devToClient->toString();
- }
- }
- fprintf_td(stderr, "** Server Characteristic-Indication: handle %s, td %" PRIu64 ", confirmed %d from %s ******\n",
- jau::to_hexstring(char_handle).c_str(), (tR-timestamp), confirmationSent, source->toString().c_str());
- fprintf_td(stderr, "** Fwd to client: %s ******\n", devToClientS.c_str());
- fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str());
- fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
+ (void)timestamp;
+ BTDeviceRef devToClient = getToClient();
+ std::string devToClientS = nullptr != devToClient ? devToClient->getAddressAndType().address.toString() : "nil";
+ std::string devFromServerS = source->getAddressAndType().address.toString();
+
+ fprintf_td(stderr, "%s* -> %s: Indication: handle %s, confirmed %d\n",
+ devFromServerS.c_str(), devToClientS.c_str(), jau::to_hexstring(char_handle).c_str(), confirmationSent);
+ fprintf_td(stderr, " raw : %s\n", char_value.toString().c_str());
+ fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
+ fprintf_td(stderr, "\n");
std::shared_ptr<BTGattHandler> gh = nullptr != devToClient ? devToClient->getGattHandler() : nullptr;
if( nullptr != gh ) {
gh->sendIndication(char_handle, char_value);
}
+ }
+
+ void writeRequest(const uint16_t handle,
+ const jau::TROOctets& data,
+ const jau::darray<Section>& sections,
+ const bool with_response,
+ BTDeviceRef serverDest,
+ BTDeviceRef clientSource) override {
+ std::string serverDestS = serverDest->getAddressAndType().address.toString();
+ std::string clientSourceS = nullptr != clientSource ? clientSource->getAddressAndType().address.toString() : "nil";
+
+ fprintf_td(stderr, "%s -> %s*: Write-Req: handle %s, with_response %d\n",
+ clientSourceS.c_str(), serverDestS.c_str(), jau::to_hexstring(handle).c_str(), with_response);
+ fprintf_td(stderr, " raw : %s\n", data.toString().c_str());
+ fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(data.get_ptr(), data.size()).c_str());
+ fprintf_td(stderr, " sections: ");
+ for(Section s : sections) {
+ fprintf(stderr, "%s, ", s.toString().c_str());
+ }
+ fprintf(stderr, "\n");
+ fprintf_td(stderr, "\n");
+ }
+
+ void writeResponse(const AttPDUMsg& pduReply,
+ const AttErrorRsp::ErrorCode error_code,
+ BTDeviceRef serverSource,
+ BTDeviceRef clientDest) override {
+ std::string serverSourceS = serverSource->getAddressAndType().address.toString();
+ std::string clientDestS = nullptr != clientDest ? clientDest->getAddressAndType().address.toString() : "nil";
+
+ fprintf_td(stderr, "%s* -> %s: Write-Rsp: %s\n",
+ serverSourceS.c_str(), clientDestS.c_str(), AttErrorRsp::getErrorCodeString(error_code).c_str());
+ fprintf_td(stderr, " pdu : %s\n", pduReply.toString().c_str());
+ fprintf_td(stderr, "\n");
+ }
+
+
+ void readResponse(const uint16_t handle,
+ const uint16_t value_offset,
+ const AttPDUMsg& pduReply,
+ const AttErrorRsp::ErrorCode error_reply,
+ const jau::TROOctets& data_reply,
+ BTDeviceRef serverReplier,
+ BTDeviceRef clientRequester) override {
+ std::string serverReplierS = serverReplier->getAddressAndType().address.toString();
+ std::string clientRequesterS = nullptr != clientRequester ? clientRequester->getAddressAndType().address.toString() : "nil";
+
+ fprintf_td(stderr, "%s <-> %s*: Read: handle %s, value_offset %d -> %s\n",
+ clientRequesterS.c_str(), serverReplierS.c_str(),
+ jau::to_hexstring(handle).c_str(), value_offset, AttErrorRsp::getErrorCodeString(error_reply).c_str());
+ if( 0 < data_reply.size() ) {
+ fprintf_td(stderr, " raw : %s\n", data_reply.toString().c_str());
+ fprintf_td(stderr, " utf8: %s\n", jau::dfa_utf8_decode(data_reply.get_ptr(), data_reply.size()).c_str());
+ } else {
+ fprintf_td(stderr, " pdu : %s\n", pduReply.toString().c_str());
+ }
+ fprintf_td(stderr, "\n");
}
+
};
static void connectToDiscoveredServer(BTDeviceRef device) {
diff --git a/src/direct_bt/BTGattHandler.cpp b/src/direct_bt/BTGattHandler.cpp
index 76a72ad6..8fbb57f1 100644
--- a/src/direct_bt/BTGattHandler.cpp
+++ b/src/direct_bt/BTGattHandler.cpp
@@ -208,6 +208,93 @@ int BTGattHandler::removeAllCharListener() noexcept {
return count;
}
+void BTGattHandler::notifyNativeRequestSent(const AttPDUMsg& pduRequest, BTDeviceRef clientSource) noexcept {
+ BTDeviceRef serverDest = getDeviceUnchecked();
+ if( nullptr != serverDest ) {
+ int i=0;
+ jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
+ try {
+ l->requestSent(pduRequest, serverDest, clientSource);
+ } catch (std::exception &e) {
+ ERR_PRINT("GATTHandler::requestSent-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
+ i+1, nativeGattCharListenerList.size(),
+ jau::to_hexstring((void*)l.get()).c_str(), e.what());
+ }
+ i++;
+ });
+ }
+}
+
+void BTGattHandler::notifyNativeReplyReceived(const AttPDUMsg& pduReply, BTDeviceRef clientDest) noexcept {
+ BTDeviceRef serverSource = getDeviceUnchecked();
+ if( nullptr != serverSource ) {
+ int i=0;
+ jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
+ try {
+ l->replyReceived(pduReply, serverSource, clientDest);
+ } catch (std::exception &e) {
+ ERR_PRINT("GATTHandler::replyReceived-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
+ i+1, nativeGattCharListenerList.size(),
+ jau::to_hexstring((void*)l.get()).c_str(), e.what());
+ }
+ i++;
+ });
+ }
+}
+
+void BTGattHandler::notifyNativeWriteRequest(const uint16_t handle, const jau::TROOctets& data, const NativeGattCharSections_t& sections, const bool with_response, BTDeviceRef clientSource) noexcept {
+ BTDeviceRef serverDest = getDeviceUnchecked();
+ if( nullptr != serverDest ) {
+ int i=0;
+ jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
+ try {
+ l->writeRequest(handle, data, sections, with_response, serverDest, clientSource);
+ } catch (std::exception &e) {
+ ERR_PRINT("GATTHandler::writeRequest-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
+ i+1, nativeGattCharListenerList.size(),
+ jau::to_hexstring((void*)l.get()).c_str(), e.what());
+ }
+ i++;
+ });
+ }
+}
+
+void BTGattHandler::notifyNativeWriteResponse(const AttPDUMsg& pduReply, const AttErrorRsp::ErrorCode error_code, BTDeviceRef clientDest) noexcept {
+ BTDeviceRef serverSource = getDeviceUnchecked();
+ if( nullptr != serverSource ) {
+ int i=0;
+ jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
+ try {
+ l->writeResponse(pduReply, error_code, serverSource, clientDest);
+ } catch (std::exception &e) {
+ ERR_PRINT("GATTHandler::writeResponse-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
+ i+1, nativeGattCharListenerList.size(),
+ jau::to_hexstring((void*)l.get()).c_str(), e.what());
+ }
+ i++;
+ });
+ }
+}
+
+void BTGattHandler::notifyNativeReadResponse(const uint16_t handle, const uint16_t value_offset,
+ const AttPDUMsg& pduReply, const AttErrorRsp::ErrorCode error_code, const jau::TROOctets& data_reply,
+ BTDeviceRef clientRequester) noexcept {
+ BTDeviceRef serverReplier = getDeviceUnchecked();
+ if( nullptr != serverReplier ) {
+ int i=0;
+ jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
+ try {
+ l->readResponse(handle, value_offset, pduReply, error_code, data_reply, serverReplier, clientRequester);
+ } catch (std::exception &e) {
+ ERR_PRINT("GATTHandler::readResponse-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
+ i+1, nativeGattCharListenerList.size(),
+ jau::to_hexstring((void*)l.get()).c_str(), e.what());
+ }
+ i++;
+ });
+ }
+}
+
void BTGattHandler::setSendIndicationConfirmation(const bool v) {
sendIndicationConfirmation = v;
}
diff --git a/src/direct_bt/BTGattServerHandler.cpp b/src/direct_bt/BTGattServerHandler.cpp
index 2361f7c5..4eb01670 100644
--- a/src/direct_bt/BTGattServerHandler.cpp
+++ b/src/direct_bt/BTGattServerHandler.cpp
@@ -415,7 +415,8 @@ class DBGattServerHandler : public BTGattHandler::GattServerHandler {
const AttPrepWrite &p = *iter_prep_write;
const uint16_t handle = p.getHandle();
if( handle == *iter_handle ) {
- jau::TROOctets p_val(p.getValue().get_ptr_nc(0), p.getValue().size(), p.getValue().byte_order());
+ const jau::TOctetSlice &p_value = p.getValue();
+ jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
res = applyWrite(device, handle, p_val, p.getValueOffset());
if( AttErrorRsp::ErrorCode::NO_ERROR != res ) {
@@ -455,6 +456,7 @@ class DBGattServerHandler : public BTGattHandler::GattServerHandler {
vslice = &req->getValue();
withResp = false;
} else {
+ // Actually an internal error, method should not have been called
AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
WARN_PRINT("GATT-Req: WRITE.20: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
gh.send(err);
@@ -943,6 +945,9 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
BTDeviceRef fwdServer;
std::shared_ptr<BTGattHandler> fwd_gh;
+ jau::darray<AttPrepWrite> writeDataQueue;
+ jau::darray<uint16_t> writeDataQueueHandles;
+
public:
FwdGattServerHandler(BTGattHandler& gh_, BTDeviceRef fwdServer_) noexcept
: gh(gh_), fwdServer(fwdServer_) {
@@ -951,6 +956,8 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
void close() noexcept override {
fwdServer->disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
+ writeDataQueue.clear();
+ writeDataQueueHandles.clear();
}
DBGattServer::Mode getMode() noexcept override { return DBGattServer::Mode::FWD; }
@@ -960,6 +967,8 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: MTU: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
if( AttPDUMsg::Opcode::EXCHANGE_MTU_RSP == rsp->getOpcode() ) {
@@ -968,6 +977,7 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
gh.setUsedMTU( std::min(gh.getServerMTU(), clientMTU) );
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: MTU: %u -> %u", clientMTU, gh.getUsedMTU());
}
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
gh.send(*rsp);
}
@@ -976,29 +986,126 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
if( AttPDUMsg::Opcode::PREPARE_WRITE_REQ == pdu->getOpcode() ) {
+ {
+ const AttPrepWrite * req = static_cast<const AttPrepWrite*>(pdu);
+ const uint16_t handle = req->getHandle();
+ writeDataQueue.push_back(*req);
+ if( writeDataQueueHandles.cend() == jau::find_if(writeDataQueueHandles.cbegin(), writeDataQueueHandles.cend(),
+ [&](const uint16_t it)->bool { return handle == it; }) )
+ {
+ // new entry
+ writeDataQueueHandles.push_back(handle);
+ }
+ }
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.11: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
+ {
+ const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
+ static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
+ fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
+ }
gh.send(*rsp);
return;
} else if( AttPDUMsg::Opcode::EXECUTE_WRITE_REQ == pdu->getOpcode() ) {
+ {
+ const AttExeWriteReq * req = static_cast<const AttExeWriteReq*>(pdu);
+ if( 0x01 == req->getFlags() ) { // immediately write all pending prepared values
+ for( auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
+ const jau::endian byte_order = writeDataQueue.size() > 0 ? writeDataQueue[0].getValue().byte_order() : jau::endian::little;
+ jau::POctets data(256, 0, byte_order); // same byte order across all requests
+ BTGattHandler::NativeGattCharSections_t sections;
+ for( auto iter_prep_write = writeDataQueue.cbegin(); iter_prep_write < writeDataQueue.cend(); ++iter_prep_write ) {
+ const AttPrepWrite &p = *iter_prep_write;
+ const uint16_t handle = p.getHandle();
+ if( handle == *iter_handle ) {
+ const jau::TOctetSlice &p_value = p.getValue();
+ jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
+ const jau::nsize_t p_end = p.getValueOffset() + p_value.size();
+ if( p_end > data.capacity() ) {
+ data.recapacity(p_end);
+ }
+ if( p_end > data.size() ) {
+ data.resize(p_end);
+ }
+ data.put_octets_nc(p.getValueOffset(), p_val);
+ BTGattHandler::NativeGattCharListener::Section section(p.getValueOffset(), (uint16_t)p_end);
+ if( sections.size() > 0 &&
+ section.start >= sections[sections.size()-1].start &&
+ section.start <= sections[sections.size()-1].end )
+ {
+ // quick merge of consecutive sections write requests
+ if( section.end > sections[sections.size()-1].end ) {
+ sections[sections.size()-1].end = section.end;
+ } // else section lies within last section
+ } else {
+ sections.push_back(section);
+ }
+ }
+ if( iter_prep_write + 1 == writeDataQueue.cend() ) {
+ // last entry
+ fwd_gh->notifyNativeWriteRequest(handle, data, sections, true /* with_response */, clientSource);
+ }
+ }
+ }
+ } // else 0x00 == req->getFlags() -> cancel all prepared writes
+ writeDataQueue.clear();
+ writeDataQueueHandles.clear();
+ }
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.13: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
+ {
+ const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
+ static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
+ fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
+ }
gh.send(*rsp);
return;
}
if( AttPDUMsg::Opcode::WRITE_REQ == pdu->getOpcode() ) {
+ {
+ const AttWriteReq &p = *static_cast<const AttWriteReq*>(pdu);
+ const jau::TOctetSlice &p_value = p.getValue();
+ BTGattHandler::NativeGattCharSections_t sections;
+ jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
+ sections.emplace_back( 0, (uint16_t)p_value.size() );
+ fwd_gh->notifyNativeWriteRequest(p.getHandle(), p_val, sections, true /* with_response */, clientSource);
+ }
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.22: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
+ {
+ const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
+ static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
+ fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
+ }
gh.send(*rsp);
} else if( AttPDUMsg::Opcode::WRITE_CMD == pdu->getOpcode() ) {
+ {
+ const AttWriteCmd &p = *static_cast<const AttWriteCmd*>(pdu);
+ const jau::TOctetSlice &p_value = p.getValue();
+ BTGattHandler::NativeGattCharSections_t sections;
+ jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
+ sections.emplace_back( 0, (uint16_t)p_value.size() );
+ fwd_gh->notifyNativeWriteRequest(p.getHandle(), p_val, sections, false /* with_response */, clientSource);
+ }
fwd_gh->send(*pdu);
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.21: %s to %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
} else {
+ // Actually an internal error, method should not have been called
AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
WARN_PRINT("GATT-Req: WRITE.20: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(err, clientSource);
+ {
+ fwd_gh->notifyNativeWriteResponse(err, err.getErrorCode(), clientSource);
+ }
gh.send(err);
}
}
@@ -1008,8 +1115,51 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
+ uint16_t handle;
+ uint16_t value_offset;
+ {
+
+ if( AttPDUMsg::Opcode::READ_REQ == pdu->getOpcode() ) {
+ const AttReadReq * req = static_cast<const AttReadReq*>(pdu);
+ handle = req->getHandle();
+ value_offset = 0;
+ } else if( AttPDUMsg::Opcode::READ_BLOB_REQ == pdu->getOpcode() ) {
+ /**
+ * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value
+ *
+ * If the Characteristic Value is not longer than (ATT_MTU – 1)
+ * an ATT_ERROR_RSP PDU with the error
+ * code set to Attribute Not Long shall be received on the first
+ * ATT_READ_BLOB_REQ PDU.
+ */
+ const AttReadBlobReq * req = static_cast<const AttReadBlobReq*>(pdu);
+ handle = req->getHandle();
+ value_offset = req->getValueOffset();
+ } else {
+ // Internal error
+ handle = 0;
+ value_offset = 0;
+ }
+ }
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
+ {
+ if( AttPDUMsg::Opcode::READ_RSP == rsp->getOpcode() ||
+ AttPDUMsg::Opcode::READ_BLOB_RSP == rsp->getOpcode() ) {
+ const AttReadNRsp * p = static_cast<const AttReadNRsp*>(rsp.get());
+ const jau::TOctetSlice& p_value = p->getValue();
+ jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
+ fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, AttErrorRsp::ErrorCode::NO_ERROR, p_val, clientSource);
+ } else {
+ const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
+ static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
+ jau::TROOctets p_val;
+ fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, error_code, p_val, clientSource);
+ }
+ }
gh.send(*rsp);
}
@@ -1018,8 +1168,11 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
gh.send(*rsp);
}
@@ -1028,8 +1181,11 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
gh.send(*rsp);
}
@@ -1038,8 +1194,11 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
gh.send(*rsp);
}
@@ -1048,8 +1207,11 @@ class FwdGattServerHandler : public BTGattHandler::GattServerHandler {
close();
return;
}
+ BTDeviceRef clientSource = gh.getDeviceUnchecked();
+ fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
+ fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
gh.send(*rsp);
}
};