diff options
author | Sven Gothel <[email protected]> | 2020-12-03 09:13:33 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-12-03 09:13:33 +0100 |
commit | 43d6ce7aa9729031f54df3b9062d83abd452ad58 (patch) | |
tree | 6546fd7baf34311ae595725959b0b912273be4fc | |
parent | ac8ee0059de28dde908c0a2c94fed60bea25d5cb (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.hpp | 25 | ||||
-rw-r--r-- | src/direct_bt/DBTAdapter.cpp | 85 | ||||
-rw-r--r-- | src/direct_bt/DBTDevice.cpp | 13 |
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; |