summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-12-03 21:14:51 +0100
committerSven Gothel <[email protected]>2020-12-03 21:14:51 +0100
commit14cdab4ec36416b24e67c9bd8f3c9d979237f9f0 (patch)
treefbb9040b2d136388bf9de85606c7cf4998ae4c43 /src
parent23ce122d27eb4be942ec3918fee6a1cc1c3a7b0a (diff)
DBTAdapter::[un]lockConnect[Any](..): New single device connect-command impl using mutex + condition-wait; Lock-free DBTDevice::setConn*Security*(..) ..
DBTAdapter::[un]lockConnect[Any](..): New single device connect-command impl using mutex + condition-wait; - previous atomic use was too complicated - only provide lockConnect(.., SMPIOCapability) and unlockConnect[Any](), the former gets called at connect with cached SMPIOCapability user value and the latter gets called ASAP after successful connection and L2CAP config or at failure / power-off. - to debug locking, set direct_bt.debug.adapter.lock environment variable, e.g. setenv("direct_bt.debug", "true,adapter.lock", 1 /* overwrite */); Lock-free DBTDevice::setConn*Security*(..) .. - check constrains (already connected etc) - cache SMPIOCapability for lockConnect() @ connect*() command - cache BTSecurityLevel for L2CAP config when connected Provide [DBT|Bluetooth]Device::setConnSecurityBest(..) for convenience.
Diffstat (limited to 'src')
-rw-r--r--src/direct_bt/DBTAdapter.cpp127
-rw-r--r--src/direct_bt/DBTDevice.cpp64
2 files changed, 121 insertions, 70 deletions
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index d3f19347..04d7e9fb 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -222,6 +222,7 @@ errout0:
DBTAdapter::DBTAdapter() noexcept
: debug_event(jau::environment::getBooleanProperty("direct_bt.debug.adapter.event", false)),
+ debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)),
mgmt( DBTManager::get(BTMode::NONE /* use env default */) ),
dev_id( mgmt.getDefaultAdapterDevID() ),
hci( dev_id )
@@ -231,6 +232,7 @@ DBTAdapter::DBTAdapter() noexcept
DBTAdapter::DBTAdapter(EUI48 &mac) noexcept
: debug_event(jau::environment::getBooleanProperty("direct_bt.debug.adapter.event", false)),
+ debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)),
mgmt( DBTManager::get(BTMode::NONE /* use env default */) ),
dev_id( mgmt.findAdapterInfoDevId(mac) ),
hci( dev_id )
@@ -240,6 +242,7 @@ DBTAdapter::DBTAdapter(EUI48 &mac) noexcept
DBTAdapter::DBTAdapter(const int _dev_id) noexcept
: debug_event(jau::environment::getBooleanProperty("direct_bt.debug.adapter.event", false)),
+ debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)),
mgmt( DBTManager::get(BTMode::NONE /* use env default */) ),
dev_id( 0 <= _dev_id ? _dev_id : mgmt.getDefaultAdapterDevID() ),
hci( dev_id )
@@ -315,8 +318,7 @@ void DBTAdapter::poweredOff() noexcept {
// ensure all hci states are reset.
hci.clearAllStates();
- iocap_defaultval = SMPIOCapability::UNSET;
- conn_blocking_device_ptr = nullptr;
+ unlockConnectAny();
DBG_PRINT("DBTAdapter::poweredOff: XXX");
}
@@ -355,72 +357,115 @@ bool DBTAdapter::setPowered(bool value) noexcept {
return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, value ? 1 : 0, current_settings);
}
-bool DBTAdapter::lockConnect(const DBTDevice & device, const bool wait) noexcept {
- const DBTDevice * exp_device = nullptr; // C++11, exp as value since C++20
+bool DBTAdapter::lockConnect(const DBTDevice & device, const bool wait, const SMPIOCapability io_cap) noexcept {
+ std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
const uint32_t timeout_ms = 10000; // FIXME: Configurable?
- const uint32_t poll_period_ms = 125;
- uint32_t td = 0;
- while( !conn_blocking_device_ptr.compare_exchange_strong(exp_device, &device) ) {
- if( device == *exp_device ) {
+
+ if( nullptr != single_conn_device_ptr ) {
+ if( device == *single_conn_device_ptr ) {
+ COND_PRINT(debug_lock, "DBTAdapter::lockConnect: Success: Already locked, same device: %s", device.toString(false).c_str());
return true; // already set, same device: OK, locked
}
if( wait ) {
- if( timeout_ms <= td ) {
- return false; // already set, timeout, blocked
+ while( nullptr != single_conn_device_ptr ) {
+ std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
+ std::cv_status s = cv_single_conn_device.wait_until(lock, t0 + std::chrono::milliseconds(timeout_ms));
+ if( std::cv_status::timeout == s && nullptr != single_conn_device_ptr ) {
+ if( debug_lock ) {
+ jau::PLAIN_PRINT(true, "DBTAdapter::lockConnect: Failed: Locked (waited)");
+ jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString(false).c_str());
+ jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString(false).c_str());
+ }
+ return false;
+ }
}
- std::this_thread::sleep_for(std::chrono::milliseconds(poll_period_ms));
- td += poll_period_ms;
- exp_device = nullptr; // continue wait for nullptr
+ // lock was released
} else {
+ if( debug_lock ) {
+ jau::PLAIN_PRINT(true, "DBTAdapter::lockConnect: Failed: Locked (no-wait)");
+ jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString(false).c_str());
+ jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString(false).c_str());
+ }
return false; // already set, not waiting, blocked
}
}
- // newly locked: iocap_blocking_device_ptr := &device
- return true;
-}
+ single_conn_device_ptr = &device;
-bool DBTAdapter::setConnIOCapability(const DBTDevice & device, const SMPIOCapability io_cap, bool& blocking, SMPIOCapability& pre_io_cap) noexcept {
- if( SMPIOCapability::UNSET == io_cap ) {
- blocking = false;
- return false;
- }
- if( !lockConnect(device, blocking) ) {
- blocking = true;
- return false;
+ if( SMPIOCapability::UNSET != io_cap ) {
+ SMPIOCapability pre_io_cap { SMPIOCapability::UNSET };
+ const bool res_iocap = mgmt.setIOCapability(dev_id, io_cap, pre_io_cap);
+ if( res_iocap ) {
+ iocap_defaultval = pre_io_cap;
+ COND_PRINT(debug_lock, "DBTAdapter::lockConnect: Success: setIOCapability[%s -> %s], %s",
+ getSMPIOCapabilityString(pre_io_cap).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
+ device.toString(false).c_str());
+ return true;
+ } else {
+ // failed, unlock and exit
+ COND_PRINT(debug_lock, "DBTAdapter::lockConnect: Failed: setIOCapability[%s], %s",
+ getSMPIOCapabilityString(io_cap).c_str(), device.toString(false).c_str());
+ single_conn_device_ptr = nullptr;
+ cv_single_conn_device.notify_all(); // notify waiting getter
+ return false;
+ }
+ } else {
+ COND_PRINT(debug_lock, "DBTAdapter::lockConnect: Success: New lock, no io-cap: %s", device.toString(false).c_str());
+ return true;
}
- blocking = false;
- const bool res = mgmt.setIOCapability(dev_id, io_cap, pre_io_cap);
- if( res ) {
- iocap_defaultval = pre_io_cap;
+}
+
+bool DBTAdapter::unlockConnect(const DBTDevice & device) noexcept {
+ std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
+
+ if( nullptr != single_conn_device_ptr && device == *single_conn_device_ptr ) {
+ const SMPIOCapability v = iocap_defaultval;
+ iocap_defaultval = SMPIOCapability::UNSET;
+ if( SMPIOCapability::UNSET != v ) {
+ SMPIOCapability o;
+ const bool res = mgmt.setIOCapability(dev_id, v, o);
+ COND_PRINT(debug_lock, "DBTAdapter::unlockConnect: Success: setIOCapability[res %d: %s -> %s], %s",
+ res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(v).c_str(),
+ single_conn_device_ptr->toString(false).c_str());
+ } else {
+ COND_PRINT(debug_lock, "DBTAdapter::unlockConnect: Success: %s",
+ single_conn_device_ptr->toString(false).c_str());
+ }
+ single_conn_device_ptr = nullptr;
+ cv_single_conn_device.notify_all(); // notify waiting getter
return true;
} else {
- conn_blocking_device_ptr = nullptr;
+ if( debug_lock ) {
+ const std::string other_device_str = nullptr != single_conn_device_ptr ? single_conn_device_ptr->toString(false) : "null";
+ jau::PLAIN_PRINT(true, "DBTAdapter::unlockConnect: Not locked:");
+ jau::PLAIN_PRINT(true, " - locked-by-other-device %s", other_device_str.c_str());
+ jau::PLAIN_PRINT(true, " - unlock-failed-for %s", device.toString(false).c_str());
+ }
return false;
}
}
-bool DBTAdapter::unlockConnect(const DBTDevice & device) noexcept {
- SMPIOCapability pre_io_cap { SMPIOCapability::UNSET };
- return unlockConnect(device, pre_io_cap);
-}
+bool DBTAdapter::unlockConnectAny() noexcept {
+ std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
-bool DBTAdapter::unlockConnect(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept {
- if( nullptr != conn_blocking_device_ptr && device == *conn_blocking_device_ptr ) {
+ if( nullptr != single_conn_device_ptr ) {
const SMPIOCapability v = iocap_defaultval;
iocap_defaultval = SMPIOCapability::UNSET;
if( SMPIOCapability::UNSET != v ) {
SMPIOCapability o;
const bool res = mgmt.setIOCapability(dev_id, v, o);
- DBG_PRINT("DBTAdapter::resetConnIOCapability: result %d: %s -> %s, %s",
+ COND_PRINT(debug_lock, "DBTAdapter::unlockConnectAny: Success: setIOCapability[res %d: %s -> %s]; %s",
res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(v).c_str(),
- device.toString(false).c_str());
- if( res ) {
- pre_io_cap = o;
- }
+ single_conn_device_ptr->toString(false).c_str());
+ } else {
+ COND_PRINT(debug_lock, "DBTAdapter::unlockConnectAny: Success: %s",
+ single_conn_device_ptr->toString(false).c_str());
}
- conn_blocking_device_ptr = nullptr;
+ single_conn_device_ptr = nullptr;
+ cv_single_conn_device.notify_all(); // notify waiting getter
return true;
} else {
+ iocap_defaultval = SMPIOCapability::UNSET;
+ COND_PRINT(debug_lock, "DBTAdapter::unlockConnectAny: Not locked");
return false;
}
}
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index bfab960e..01aef32b 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -323,7 +323,7 @@ HCIStatusCode DBTDevice::connectLE(uint16_t le_scan_interval, uint16_t le_scan_w
ERR_PRINT("DBTDevice::connectLE: HCI closed: %s", toString(false).c_str());
return HCIStatusCode::INTERNAL_FAILURE;
}
- if( !adapter.lockConnect(*this, true /* wait */) ) {
+ if( !adapter.lockConnect(*this, true /* wait */, pairing_data.ioCap_user) ) {
ERR_PRINT("DBTDevice::connectLE: adapter::lockConnect() failed: %s", toString(false).c_str());
return HCIStatusCode::INTERNAL_FAILURE;
}
@@ -372,7 +372,7 @@ HCIStatusCode DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t cl
ERR_PRINT("DBTDevice::connectBREDR: HCI closed: %s", toString(false).c_str());
return HCIStatusCode::INTERNAL_FAILURE;
}
- if( !adapter.lockConnect(*this, true /* wait */) ) {
+ if( !adapter.lockConnect(*this, true /* wait */, pairing_data.ioCap_user) ) {
ERR_PRINT("DBTDevice::connectBREDR: adapter::lockConnect() failed: %s", toString(false).c_str());
return HCIStatusCode::INTERNAL_FAILURE;
}
@@ -834,18 +834,16 @@ bool DBTDevice::setConnSecurityLevel(const BTSecurityLevel sec_level) noexcept {
getBTSecurityLevelString(sec_level).c_str(), toString(false).c_str());
return false;
}
- const BTSecurityLevel old_sec_level = pairing_data.sec_level_user;
- pairing_data.sec_level_user = sec_level;
const bool res = true;
+ pairing_data.sec_level_user = sec_level;
- DBG_PRINT("DBTDevice::setConnSecurityLevel: result %d: lvl %s -> %s, %s", res,
- getBTSecurityLevelString(old_sec_level).c_str(),
+ DBG_PRINT("DBTDevice::setConnSecurityLevel: result %d: lvl %s, %s", res,
getBTSecurityLevelString(sec_level).c_str(),
toString(false).c_str());
return res;
}
-bool DBTDevice::setConnIOCapability(const SMPIOCapability io_cap, bool& blocking, SMPIOCapability& pre_io_cap) noexcept {
+bool DBTDevice::setConnIOCapability(const SMPIOCapability io_cap) noexcept {
if( SMPIOCapability::UNSET == io_cap ) {
DBG_PRINT("DBTDevice::setConnIOCapability: io %s, invalid value.", getSMPIOCapabilityString(io_cap).c_str());
return false;
@@ -856,19 +854,17 @@ bool DBTDevice::setConnIOCapability(const SMPIOCapability io_cap, bool& blocking
getSMPIOCapabilityString(io_cap).c_str(), toString(false).c_str());
return false;
}
+ const bool res = true;
+ pairing_data.ioCap_user = io_cap;
- const bool blocking_call = blocking;
- const bool res = adapter.setConnIOCapability(*this, io_cap, blocking, pre_io_cap);
-
- DBG_PRINT("DBTDevice::setConnIOCapability: result %d (blocked %d): io %s -> %s (blocking %d), %s",
- res, blocking,
- getSMPIOCapabilityString(pre_io_cap).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
- blocking_call, toString(false).c_str());
+ DBG_PRINT("DBTDevice::setConnIOCapability: result %d: io %s, %s", res,
+ getSMPIOCapabilityString(io_cap).c_str(),
+ toString(false).c_str());
return res;
}
-bool DBTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap, bool& blocking, SMPIOCapability& pre_io_cap) noexcept {
+bool DBTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept {
if( BTSecurityLevel::UNSET == sec_level ) {
DBG_PRINT("DBTAdapter::setConnSecurity: lvl %s, invalid value.", getBTSecurityLevelString(sec_level).c_str());
return false;
@@ -884,25 +880,34 @@ bool DBTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapa
getSMPIOCapabilityString(io_cap).c_str(), toString(false).c_str());
return false;
}
+ const bool res = true;
+ pairing_data.ioCap_user = io_cap;
+ pairing_data.sec_level_user = sec_level;
- const BTSecurityLevel pre_sec_level = pairing_data.sec_level_user;
-
- const bool blocking_call = blocking;
- const bool res = adapter.setConnIOCapability(*this, io_cap, blocking, pre_io_cap);
- if( res ) {
- pairing_data.sec_level_user = sec_level;
- }
-
- DBG_PRINT("DBTDevice::setConnSecurity: result %d (blocked %d): lvl %s -> %s, io %s -> %s (blocking %d), %s",
- res, blocking,
- getBTSecurityLevelString(pre_sec_level).c_str(),
- getBTSecurityLevelString(pairing_data.sec_level_user).c_str(),
- getSMPIOCapabilityString(pre_io_cap).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
- blocking_call, toString(false).c_str());
+ DBG_PRINT("DBTDevice::setConnSecurity: result %d: lvl %s, io %s, %s", res,
+ getBTSecurityLevelString(sec_level).c_str(),
+ getSMPIOCapabilityString(io_cap).c_str(),
+ toString(false).c_str());
return res;
}
+bool DBTDevice::setConnSecurityBest(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept {
+ if( BTSecurityLevel::UNSET < sec_level && SMPIOCapability::UNSET != io_cap ) {
+ return setConnSecurity(sec_level, io_cap);
+ } else if( BTSecurityLevel::UNSET < sec_level ) {
+ if( BTSecurityLevel::ENC_ONLY >= sec_level ) {
+ return setConnSecurity(sec_level, SMPIOCapability::NO_INPUT_NO_OUTPUT);
+ } else {
+ return setConnSecurityLevel(sec_level);
+ }
+ } else if( SMPIOCapability::UNSET != io_cap ) {
+ return setConnIOCapability(io_cap);
+ } else {
+ return false;
+ }
+}
+
HCIStatusCode DBTDevice::setPairingPasskey(const uint32_t passkey) noexcept {
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
@@ -956,6 +961,7 @@ void DBTDevice::clearSMPStates(const bool connected) noexcept {
if( !connected ) {
// needs to survive connected, or will be set right @ connected
+ pairing_data.ioCap_user = SMPIOCapability::UNSET;
pairing_data.ioCap_conn = SMPIOCapability::UNSET;
pairing_data.sec_level_user = BTSecurityLevel::UNSET;
}