aboutsummaryrefslogtreecommitdiffstats
path: root/src/direct_bt
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-02-02 17:02:59 +0100
committerSven Gothel <[email protected]>2022-02-02 17:02:59 +0100
commit1443d3a084342fffacd405fae3c44ef290bd438e (patch)
tree6dc94eea45370dbd1df475532afe4557d6bfa26b /src/direct_bt
parent51395c131287958defc8c3521f807d8c12bd6ac8 (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.cpp102
-rw-r--r--src/direct_bt/BTGattHandler.cpp99
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) ) {