summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-12-03 09:13:33 +0100
committerSven Gothel <[email protected]>2020-12-03 09:13:33 +0100
commit43d6ce7aa9729031f54df3b9062d83abd452ad58 (patch)
tree6546fd7baf34311ae595725959b0b912273be4fc
parentac8ee0059de28dde908c0a2c94fed60bea25d5cb (diff)
DBTAdapter: One Connect Command at a time due to SMPIOCapability (1/2 WIP)
We need to lock all Connect Commands starting with [1] - connectLE or connectBREDR, or [2] - setConn*Security* for SMPIOCapability (FIXME) The release ASAP on - success after connect from within DBTDevice::processL2CAPSetup(..) - disconnect or connect-failure or power-off TODO: Above [2] locking at setConn* shall be remove by cashing the attribute and locking at [1] connect* command. This shall give a more natural API and expected behavior.
-rw-r--r--api/direct_bt/DBTAdapter.hpp25
-rw-r--r--src/direct_bt/DBTAdapter.cpp85
-rw-r--r--src/direct_bt/DBTDevice.cpp13
3 files changed, 66 insertions, 57 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp
index 4e668529..56cbb871 100644
--- a/api/direct_bt/DBTAdapter.hpp
+++ b/api/direct_bt/DBTAdapter.hpp
@@ -214,8 +214,8 @@ namespace direct_bt {
std::atomic<ScanType> currentMetaScanType; // = ScanType::NONE
std::atomic<bool> keep_le_scan_alive; // = false;
- jau::ordered_atomic<SMPIOCapability, std::memory_order_relaxed> io_capability_defaultval = SMPIOCapability::UNSET;
- std::atomic<const DBTDevice *> io_capability_device_ptr = nullptr;
+ jau::ordered_atomic<SMPIOCapability, std::memory_order_relaxed> iocap_defaultval = SMPIOCapability::UNSET;
+ std::atomic<const DBTDevice *> conn_blocking_device_ptr = nullptr;
std::vector<std::shared_ptr<DBTDevice>> connectedDevices;
std::vector<std::shared_ptr<DBTDevice>> discoveredDevices; // all discovered devices
@@ -258,25 +258,10 @@ namespace direct_bt {
friend void DBTDevice::processDeviceReady(std::shared_ptr<DBTDevice> sthis, const uint64_t timestamp);
friend std::vector<std::shared_ptr<GATTService>> DBTDevice::getGATTServices() noexcept;
- /**
- * Sets the given SMPIOCapability for the next DBTDevice::connectLE() or DBTDevice::connectBREDR().
- * <p>
- * The ::SMPIOCapability value will be reset to its previous value when connection is completed or failed.
- * </p>
- * <p>
- * A value of ::SMPIOCapability::UNSET will be ignored and method returns immediately.
- * </p>
- * @param[in] io_cap ::SMPIOCapability to be applied
- * @param[in,out] blocking if true, blocks until previous setting is completed,
- * i.e. until connection has been completed or failed.
- * Otherwise returns immediately with false if previous connection result is still pending.
- * On return, this parameter will be set to true if failure was caused by blocking or timeout, otherwise set to false.
- * @param[out] pre_io_cap return the previous set ::SMPIOCapability value if successful
- */
+ bool lockConnect(const DBTDevice & device, const bool wait) noexcept;
bool setConnIOCapability(const DBTDevice & device, const SMPIOCapability io_cap, bool& blocking, SMPIOCapability& pre_io_cap) noexcept;
- bool resetConnIOCapability(const DBTDevice & device) noexcept;
- bool resetConnIOCapability(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept;
- bool isConnIOCapabilitySet() const noexcept { return nullptr != io_capability_device_ptr; }
+ bool unlockConnect(const DBTDevice & device) noexcept;
+ bool unlockConnect(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept;
bool addConnectedDevice(const std::shared_ptr<DBTDevice> & device) noexcept;
bool removeConnectedDevice(const DBTDevice & device) noexcept;
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index b78facf4..d3f19347 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -315,8 +315,8 @@ void DBTAdapter::poweredOff() noexcept {
// ensure all hci states are reset.
hci.clearAllStates();
- io_capability_defaultval = SMPIOCapability::UNSET;
- io_capability_device_ptr = nullptr;
+ iocap_defaultval = SMPIOCapability::UNSET;
+ conn_blocking_device_ptr = nullptr;
DBG_PRINT("DBTAdapter::poweredOff: XXX");
}
@@ -355,58 +355,71 @@ bool DBTAdapter::setPowered(bool value) noexcept {
return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, value ? 1 : 0, current_settings);
}
-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;
- }
- const DBTDevice * exp_null_device = nullptr; // C++11, exp as value since C++20
+bool DBTAdapter::lockConnect(const DBTDevice & device, const bool wait) noexcept {
+ const DBTDevice * exp_device = nullptr; // C++11, exp as value since C++20
const uint32_t timeout_ms = 10000; // FIXME: Configurable?
const uint32_t poll_period_ms = 125;
uint32_t td = 0;
- while( !io_capability_device_ptr.compare_exchange_strong(exp_null_device, &device) ) {
- if( blocking ) {
+ while( !conn_blocking_device_ptr.compare_exchange_strong(exp_device, &device) ) {
+ if( device == *exp_device ) {
+ return true; // already set, same device: OK, locked
+ }
+ if( wait ) {
if( timeout_ms <= td ) {
- blocking = true;
- return false;
+ return false; // already set, timeout, blocked
}
std::this_thread::sleep_for(std::chrono::milliseconds(poll_period_ms));
td += poll_period_ms;
+ exp_device = nullptr; // continue wait for nullptr
} else {
- blocking = true;
- return false;
+ return false; // already set, not waiting, blocked
}
}
+ // newly locked: iocap_blocking_device_ptr := &device
+ return true;
+}
+
+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;
+ }
blocking = false;
const bool res = mgmt.setIOCapability(dev_id, io_cap, pre_io_cap);
if( res ) {
- io_capability_defaultval = pre_io_cap;
+ iocap_defaultval = pre_io_cap;
return true;
} else {
- io_capability_device_ptr = nullptr;
+ conn_blocking_device_ptr = nullptr;
return false;
}
}
-bool DBTAdapter::resetConnIOCapability(const DBTDevice & device) noexcept {
+bool DBTAdapter::unlockConnect(const DBTDevice & device) noexcept {
SMPIOCapability pre_io_cap { SMPIOCapability::UNSET };
- return resetConnIOCapability(device, pre_io_cap);
-}
-
-bool DBTAdapter::resetConnIOCapability(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept {
- if( nullptr != io_capability_device_ptr && device == *io_capability_device_ptr ) {
- const SMPIOCapability v = io_capability_defaultval;
- io_capability_defaultval = SMPIOCapability::UNSET;
- io_capability_device_ptr = nullptr;
- SMPIOCapability o;
- const bool res = mgmt.setIOCapability(dev_id, v, o);
- DBG_PRINT("DBTAdapter::resetConnIOCapability: result %d: %s -> %s, %s",
- res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(v).c_str(),
- device.toString(false).c_str());
- if( res ) {
- pre_io_cap = o;
+ return unlockConnect(device, pre_io_cap);
+}
+
+bool DBTAdapter::unlockConnect(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept {
+ if( nullptr != conn_blocking_device_ptr && device == *conn_blocking_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",
+ res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(v).c_str(),
+ device.toString(false).c_str());
+ if( res ) {
+ pre_io_cap = o;
+ }
}
- return res;
+ conn_blocking_device_ptr = nullptr;
+ return true;
} else {
return false;
}
@@ -770,7 +783,7 @@ void DBTAdapter::removeDevice(DBTDevice & device) noexcept {
WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString(false).c_str());
const HCIStatusCode status = device.disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
WORDY_PRINT("DBTAdapter::removeDevice: disconnect %s, %s", getHCIStatusCodeString(status).c_str(), toString(false).c_str());
- resetConnIOCapability(device);
+ unlockConnect(device);
removeConnectedDevice(device); // usually done in DBTAdapter::mgmtEvDeviceDisconnectedHCI
removeDiscoveredDevice(device); // usually done in DBTAdapter::mgmtEvDeviceDisconnectedHCI
WORDY_PRINT("DBTAdapter::removeDevice: End %s", toString(false).c_str());
@@ -1073,7 +1086,7 @@ bool DBTAdapter::mgmtEvConnectFailedHCI(std::shared_ptr<MgmtEvent> e) noexcept {
dev_id, event.toString().c_str(), jau::uint16HexString(handle).c_str(),
device->toString().c_str());
- resetConnIOCapability(*device);
+ unlockConnect(*device);
device->notifyDisconnected();
removeConnectedDevice(*device);
@@ -1163,7 +1176,7 @@ bool DBTAdapter::mgmtEvDeviceDisconnectedHCI(std::shared_ptr<MgmtEvent> e) noexc
dev_id, event.toString().c_str(), jau::uint16HexString(event.getHCIHandle()).c_str(),
device->toString().c_str());
- resetConnIOCapability(*device);
+ unlockConnect(*device);
device->notifyDisconnected();
removeConnectedDevice(*device);
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index ca65fb29..bfab960e 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -323,6 +323,10 @@ 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 */) ) {
+ ERR_PRINT("DBTDevice::connectLE: adapter::lockConnect() failed: %s", toString(false).c_str());
+ return HCIStatusCode::INTERNAL_FAILURE;
+ }
HCIStatusCode status = hci.le_create_conn(address,
hci_peer_mac_type, hci_own_mac_type,
le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
@@ -334,12 +338,14 @@ HCIStatusCode DBTDevice::connectLE(uint16_t le_scan_interval, uint16_t le_scan_w
getHCILEPeerAddressTypeString(hci_peer_mac_type).c_str(),
getHCILEOwnAddressTypeString(hci_own_mac_type).c_str(),
toString(false).c_str());
+ adapter.unlockConnect(*this);
} else if ( HCIStatusCode::SUCCESS != status ) {
ERR_PRINT("DBTDevice::connectLE: Could not create connection: status 0x%2.2X (%s), errno %d %s, hci-atype[peer %s, own %s] on %s",
static_cast<uint8_t>(status), getHCIStatusCodeString(status).c_str(), errno, strerror(errno),
getHCILEPeerAddressTypeString(hci_peer_mac_type).c_str(),
getHCILEOwnAddressTypeString(hci_own_mac_type).c_str(),
toString(false).c_str());
+ adapter.unlockConnect(*this);
}
return status;
}
@@ -366,11 +372,16 @@ 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 */) ) {
+ ERR_PRINT("DBTDevice::connectBREDR: adapter::lockConnect() failed: %s", toString(false).c_str());
+ return HCIStatusCode::INTERNAL_FAILURE;
+ }
HCIStatusCode status = hci.create_conn(address, pkt_type, clock_offset, role_switch);
allowDisconnect = true;
if ( HCIStatusCode::SUCCESS != status ) {
ERR_PRINT("DBTDevice::connectBREDR: Could not create connection: status 0x%2.2X (%s), errno %d %s on %s",
static_cast<uint8_t>(status), getHCIStatusCodeString(status).c_str(), errno, strerror(errno), toString(false).c_str());
+ adapter.unlockConnect(*this);
}
return status;
}
@@ -457,7 +468,7 @@ void DBTDevice::processL2CAPSetup(std::shared_ptr<DBTDevice> sthis) {
DBG_PRINT("DBTDevice::processL2CAPSetup: lvl %s, connect[smp_enc %d, l2cap[open %d, enc %d]]",
getBTSecurityLevelString(sec_level).c_str(), smp_enc, l2cap_open, l2cap_enc);
- adapter.resetConnIOCapability(*this);
+ adapter.unlockConnect(*this);
if( !l2cap_open ) {
pairing_data.sec_level_conn = BTSecurityLevel::NONE;