diff options
author | Sven Gothel <[email protected]> | 2022-02-05 13:10:02 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-02-05 13:10:02 +0100 |
commit | e570552af8de36d78e369591384082aa0b59e88b (patch) | |
tree | 8c360a162c52860c25a8006b32b331b677609f00 | |
parent | 49933e253b671c6017c925eb09c5359e86a4c154 (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.hpp | 142 | ||||
-rw-r--r-- | examples/dbt_repeater00.cpp | 113 | ||||
-rw-r--r-- | src/direct_bt/BTGattHandler.cpp | 87 | ||||
-rw-r--r-- | src/direct_bt/BTGattServerHandler.cpp | 164 |
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); } }; |