aboutsummaryrefslogtreecommitdiffstats
path: root/src/direct_bt/BTGattHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/direct_bt/BTGattHandler.cpp')
-rw-r--r--src/direct_bt/BTGattHandler.cpp99
1 files changed, 72 insertions, 27 deletions
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) ) {