diff options
author | Sven Gothel <[email protected]> | 2022-02-02 17:02:59 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-02-02 17:02:59 +0100 |
commit | 1443d3a084342fffacd405fae3c44ef290bd438e (patch) | |
tree | 6dc94eea45370dbd1df475532afe4557d6bfa26b /src/direct_bt | |
parent | 51395c131287958defc8c3521f807d8c12bd6ac8 (diff) |
Custom GATT Processing: MTU and remote GATT Services shall be processed at request only via BTDevice::getGattServices()
This reverts MTU/GATT-Services 'push up' from getGattServices() -> connectGATT()
of commit ab9f3dec2d06b9e39241c497e8aa00aaa23966c3.
Objective of this revert and hence removal of the unconditional
MTU/GATT-Services query is to allow user to use custom operations.
Hence only getGattServices() will trigger the 'auto config'
mechanism on the user's request.
Diffstat (limited to 'src/direct_bt')
-rw-r--r-- | src/direct_bt/BTDevice.cpp | 102 | ||||
-rw-r--r-- | src/direct_bt/BTGattHandler.cpp | 99 |
2 files changed, 129 insertions, 72 deletions
diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index 39e01597..8a212ac9 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -654,7 +654,7 @@ void BTDevice::processDeviceReady(std::shared_ptr<BTDevice> sthis, const uint64_ std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } - const bool gatt_res = connectGATT(sthis); // may close connection and hence clear pairing_data + const bool gatt_res = connectGATT(sthis); if( !gatt_res && using_enc ) { // Need to repair as GATT communication failed @@ -1908,41 +1908,10 @@ bool BTDevice::connectGATT(std::shared_ptr<BTDevice> sthis) noexcept { return false; } else if ( BTRole::Master == btRole ) { DBG_PRINT("BTDevice::connectGATT: Local GATT Server: Done: %s", toString().c_str()); - return true; } else { - DBG_PRINT("BTDevice::connectGATT: Local GATT Client: Service Discovery Start: %s", toString().c_str()); - try { - // Service discovery may consume 500ms - 2000ms, depending on bandwidth - jau::darray<BTGattServiceRef>& gattServices = gattHandler->discoverCompletePrimaryServices(gattHandler); - if( gattServices.size() == 0 ) { // nothing discovered - ERR_PRINT2("No primary services discovered"); - gattHandler = nullptr; - return false; - } - DBG_PRINT("BTDevice::connectGATT: %zu Services Discovered: %s", gattServices.size(), toString().c_str()); - - // discovery success, parse GenericAccess - std::shared_ptr<GattGenericAccessSvc> gattGenericAccess = gattHandler->getGenericAccess(); - if( nullptr != gattGenericAccess ) { - const uint64_t ts = jau::getCurrentMilliseconds(); - EIRDataType updateMask = update(*gattGenericAccess, ts); - DBG_PRINT("BTDevice::connectGATT: GenericAccess updated %s:\n %s\n -> %s", - to_string(updateMask).c_str(), gattGenericAccess->toString().c_str(), toString().c_str()); - if( EIRDataType::NONE != updateMask ) { - adapter.sendDeviceUpdated("connectGATT", sthis, ts, updateMask); - } - } else { - // else: Actually an error w/o valid mandatory GenericAccess - WARN_PRINT("No GenericAccess: %s", toString().c_str()); - } - return true; - } catch (std::exception &e) { - WARN_PRINT("Caught exception: '%s' on %s", e.what(), toString().c_str()); - } + DBG_PRINT("BTDevice::connectGATT: Local GATT Client: Done: %s", toString().c_str()); } - ERR_PRINT2("Failed GATT Service Processing"); - gattHandler = nullptr; - return false; + return true; } std::shared_ptr<BTGattHandler> BTDevice::getGattHandler() noexcept { @@ -1953,10 +1922,62 @@ std::shared_ptr<BTGattHandler> BTDevice::getGattHandler() noexcept { jau::darray<BTGattServiceRef> BTDevice::getGattServices() noexcept { std::shared_ptr<BTGattHandler> gh = getGattHandler(); if( nullptr == gh ) { - ERR_PRINT("GATTHandler nullptr"); + ERR_PRINT("GATTHandler nullptr: %s", toString().c_str()); return jau::darray<std::shared_ptr<BTGattService>>(); } - return gh->getServices(); // copy previous discovery result + if( BTRole::Slave != getRole() ) { + // Remote device is not a slave (peripheral, responder) - hence no GATT services + ERR_PRINT("Remote device not a GATT server: ", toString().c_str()); + return jau::darray<std::shared_ptr<BTGattService>>(); + } + + bool gatt_already_init = false; + const bool gatt_client_init = gh->initClientGatt(gh, gatt_already_init); + jau::darray<BTGattServiceRef>& gattServices = gh->getServices(); + if( !gatt_client_init ) { + ERR_PRINT2("BTDevice::getGattServices: Client GATT Initialization failed"); + return gattServices; // copy previous discovery result (zero sized) + } + if( gatt_already_init ) { + return gattServices; // copy previous discovery result + } + if( gattServices.size() == 0 ) { // nothing discovered + ERR_PRINT2("BTDevice::getGattServices: No primary services discovered"); + return gattServices; + } + try { + // discovery success, parse GenericAccess + std::shared_ptr<GattGenericAccessSvc> gattGenericAccess = gh->getGenericAccess(); + if( nullptr != gattGenericAccess ) { + const uint64_t ts = jau::getCurrentMilliseconds(); + EIRDataType updateMask = update(*gattGenericAccess, ts); + DBG_PRINT("BTDevice::getGattServices: GenericAccess updated %s:\n %s\n -> %s", + to_string(updateMask).c_str(), gattGenericAccess->toString().c_str(), toString().c_str()); + if( EIRDataType::NONE != updateMask ) { + std::shared_ptr<BTDevice> sharedInstance = getSharedInstance(); + if( nullptr == sharedInstance ) { + ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str()); + } else { + adapter.sendDeviceUpdated("getGattServices", sharedInstance, ts, updateMask); + } + } + } else { + // else: Actually an error w/o valid mandatory GenericAccess + WARN_PRINT("No GenericAccess: %s", toString().c_str()); + } + } catch (std::exception &e) { + WARN_PRINT("Caught exception: '%s' on %s", e.what(), toString().c_str()); + } + return gattServices; // return copy +} + +std::shared_ptr<GattGenericAccessSvc> BTDevice::getGattGenericAccess() { + std::shared_ptr<BTGattHandler> gh = getGattHandler(); + if( nullptr == gh ) { + ERR_PRINT("GATTHandler nullptr"); + return nullptr; + } + return gh->getGenericAccess(); } BTGattServiceRef BTDevice::findGattService(const jau::uuid_t& service_uuid) noexcept { @@ -2024,15 +2045,6 @@ bool BTDevice::pingGATT() noexcept { return false; } -std::shared_ptr<GattGenericAccessSvc> BTDevice::getGattGenericAccess() { - std::shared_ptr<BTGattHandler> gh = getGattHandler(); - if( nullptr == gh ) { - ERR_PRINT("GATTHandler nullptr"); - return nullptr; - } - return gh->getGenericAccess(); -} - bool BTDevice::addCharListener(std::shared_ptr<BTGattCharListener> l) { std::shared_ptr<BTGattHandler> gatt = getGattHandler(); if( nullptr == gatt ) { diff --git a/src/direct_bt/BTGattHandler.cpp b/src/direct_bt/BTGattHandler.cpp index 066ea960..c58fe844 100644 --- a/src/direct_bt/BTGattHandler.cpp +++ b/src/direct_bt/BTGattHandler.cpp @@ -1198,8 +1198,8 @@ BTGattHandler::BTGattHandler(const BTDeviceRef &device, L2CAPComm& l2cap_att, co jau::bindMemberFunc(this, &BTGattHandler::l2capReaderEndLocked), jau::bindMemberFunc(this, &BTGattHandler::l2capReaderEndFinal)), attPDURing(env.ATTPDU_RING_CAPACITY), + serverMTU(number(Defaults::MIN_ATT_MTU)), usedMTU(number(Defaults::MIN_ATT_MTU)), clientMTUExchanged(false), gattServerData( GATTRole::Server == role ? device->getAdapter().getGATTServerData() : nullptr ), - serverMTU(number(Defaults::MIN_ATT_MTU)), usedMTU(number(Defaults::MIN_ATT_MTU)) { if( !validateConnected() ) { ERR_PRINT("GATTHandler.ctor: L2CAP could not connect"); @@ -1216,32 +1216,17 @@ BTGattHandler::BTGattHandler(const BTDeviceRef &device, L2CAPComm& l2cap_att, co l2cap_reader_service.start(); if( GATTRole::Client == getRole() ) { - // First point of failure if remote device exposes no GATT functionality. Allow a longer timeout! - const int32_t initial_command_reply_timeout = std::min<int32_t>(10000, std::max<int32_t>(env.GATT_INITIAL_COMMAND_REPLY_TIMEOUT, 2*supervision_timeout)); - uint16_t mtu = 0; - try { - mtu = exchangeMTUImpl(number(Defaults::MAX_ATT_MTU), initial_command_reply_timeout); - } catch (std::exception &e) { - ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", e.what()); - } catch (std::string &msg) { - ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", msg.c_str()); - } catch (const char *msg) { - ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", msg); - } - if( 0 == mtu ) { - ERR_PRINT2("GATTHandler::ctor: Zero serverMTU -> disconnect: %s", toString().c_str()); - disconnect(true /* disconnectDevice */, false /* ioErrorCause */); - } else { - serverMTU = mtu; - usedMTU = std::min(number(Defaults::MAX_ATT_MTU), serverMTU); - } + // MTU to be negotiated via initClientGatt() from this GATT client later + serverMTU = number(Defaults::MAX_ATT_MTU); + usedMTU = number(Defaults::MIN_ATT_MTU); } else { + // MTU to be negotiated via client request on this GATT server if( nullptr != gattServerData ) { serverMTU = std::max( std::min( gattServerData->getMaxAttMTU(), number(Defaults::MAX_ATT_MTU) ), number(Defaults::MIN_ATT_MTU) ); } else { serverMTU = number(Defaults::MAX_ATT_MTU); } - usedMTU = number(Defaults::MIN_ATT_MTU); // until negotiated! + usedMTU = number(Defaults::MIN_ATT_MTU); if( nullptr != gattServerData ) { int i=0; @@ -1316,6 +1301,8 @@ bool BTGattHandler::disconnect(const bool disconnectDevice, const bool ioErrorCa l2cap_reader_service.stop(); PERF3_TS_TD("GATTHandler::disconnect.2"); + clientMTUExchanged = false; + if( disconnectDevice ) { BTDeviceRef device = getDeviceUnchecked(); if( nullptr != device ) { @@ -1378,15 +1365,16 @@ std::unique_ptr<const AttPDUMsg> BTGattHandler::sendWithReply(const AttPDUMsg & return res; } -uint16_t BTGattHandler::exchangeMTUImpl(const uint16_t clientMaxMTU, const int32_t timeout) { +uint16_t BTGattHandler::clientMTUExchange(const int32_t timeout) { + if( GATTRole::Client != getRole() ) { + ERR_PRINT("GATT MTU exchange only allowed in client mode"); + return usedMTU; + } /*** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.3.1 Exchange MTU (Server configuration) */ - if( clientMaxMTU > number(Defaults::MAX_ATT_MTU) ) { - throw jau::IllegalArgumentException("clientMaxMTU "+std::to_string(clientMaxMTU)+" > ClientMaxMTU "+std::to_string(number(Defaults::MAX_ATT_MTU)), E_FILE_LINE); - } - const AttExchangeMTU req(AttPDUMsg::ReqRespType::REQUEST, clientMaxMTU); - // called by ctor only, no locking: const std::lock_guard<std::recursive_mutex> lock(mtx_command); + const AttExchangeMTU req(AttPDUMsg::ReqRespType::REQUEST, number(Defaults::MAX_ATT_MTU)); + const std::lock_guard<std::recursive_mutex> lock(mtx_command); PERF_TS_T0(); uint16_t mtu = 0; @@ -1485,6 +1473,63 @@ BTGattCharRef BTGattHandler::findCharacterisicsByValueHandle(const BTGattService return nullptr; } +bool BTGattHandler::initClientGatt(std::shared_ptr<BTGattHandler> shared_this, bool& already_init) noexcept { + const std::lock_guard<std::recursive_mutex> lock(mtx_command); + already_init = clientMTUExchanged && services.size() > 0; + if( already_init ) { + return true; + } + if( !isConnected() ) { + DBG_PRINT("GATTHandler::initClientGatt: Not connected: %s", toString().c_str()); + return false; + } + if( !clientMTUExchanged) { + // First point of failure if remote device exposes no GATT functionality. Allow a longer timeout! + const int32_t initial_command_reply_timeout = std::min<int32_t>(10000, std::max<int32_t>(env.GATT_INITIAL_COMMAND_REPLY_TIMEOUT, 2*supervision_timeout)); + uint16_t mtu = 0; + try { + DBG_PRINT("GATTHandler::initClientGatt: Local GATT Client: MTU Exchange Start: %s", toString().c_str()); + mtu = clientMTUExchange(initial_command_reply_timeout); + } catch (std::exception &e) { + ERR_PRINT2("GattHandler.initClientGatt: exchangeMTU failed: %s", e.what()); + } catch (std::string &msg) { + ERR_PRINT2("GattHandler.initClientGatt: exchangeMTU failed: %s", msg.c_str()); + } catch (const char *msg) { + ERR_PRINT2("GattHandler.initClientGatt: exchangeMTU failed: %s", msg); + } + if( 0 == mtu ) { + ERR_PRINT2("GATTHandler::initClientGatt: Local GATT Client: Zero serverMTU -> disconnect: %s", toString().c_str()); + disconnect(true /* disconnectDevice */, false /* ioErrorCause */); + return false; + } + serverMTU = mtu; + usedMTU = std::min(number(Defaults::MAX_ATT_MTU), serverMTU.load()); + clientMTUExchanged = true; + DBG_PRINT("GATTHandler::initClientGatt: Local GATT Client: MTU Exchanged: server %u -> used %u, %s", serverMTU.load(), usedMTU.load(), toString().c_str()); + } + + if( services.size() > 0 ) { + // already initialized + return true; + } + + try { + // Service discovery may consume 500ms - 2000ms, depending on bandwidth + DBG_PRINT("GATTHandler::initClientGatt: Local GATT Client: Service Discovery Start: %s", toString().c_str()); + jau::darray<BTGattServiceRef>& gattServices = discoverCompletePrimaryServices(shared_this); + if( gattServices.size() == 0 ) { // nothing discovered + ERR_PRINT2("GATTHandler::initClientGatt: No primary services discovered"); + disconnect(true /* disconnectDevice */, false /* ioErrorCause */); + return false; + } + DBG_PRINT("GATTHandler::initClientGatt: %zu Services Discovered: %s", gattServices.size(), toString().c_str()); + return true; + } catch (std::exception &e) { + WARN_PRINT("GATTHandler::initClientGatt: Caught exception: '%s' on %s", e.what(), toString().c_str()); + } + return false; +} + jau::darray<BTGattServiceRef> & BTGattHandler::discoverCompletePrimaryServices(std::shared_ptr<BTGattHandler> shared_this) { const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor if( !discoverPrimaryServices(shared_this, services) ) { |