diff options
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | api/direct_bt/BTAdapter.hpp | 18 | ||||
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 15 | ||||
-rw-r--r-- | api/direct_bt/BTTypes0.hpp | 274 | ||||
-rw-r--r-- | api/direct_bt/HCIHandler.hpp | 139 | ||||
-rw-r--r-- | api/direct_bt/HCITypes.hpp | 20 | ||||
-rw-r--r-- | api/direct_bt/MgmtTypes.hpp | 4 | ||||
-rw-r--r-- | api/direct_bt/SMPHandler.hpp | 20 | ||||
-rw-r--r-- | doc/log/dbt_scanner10-bt5_intel-01.btmon | bin | 0 -> 19316 bytes | |||
-rw-r--r-- | doc/log/dbt_scanner10-bt5_intel-01.log.txt | 292 | ||||
-rw-r--r-- | doc/log/dbt_scanner10-bt5_intel.txt | 14 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 25 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 19 | ||||
-rwxr-xr-x | scripts/run-dbt_scanner10.sh | 2 | ||||
-rw-r--r-- | src/direct_bt/BTAdapter.cpp | 45 | ||||
-rw-r--r-- | src/direct_bt/BTDevice.cpp | 46 | ||||
-rw-r--r-- | src/direct_bt/BTTypes0.cpp | 324 | ||||
-rw-r--r-- | src/direct_bt/HCIHandler.cpp | 350 | ||||
-rw-r--r-- | src/direct_bt/HCITypes.cpp | 17 |
19 files changed, 1434 insertions, 207 deletions
@@ -233,9 +233,13 @@ The following **platforms** are tested and hence supported The following **Bluetooth Adapter** were tested -* Intel Bluemoon Bluetooth Adapter -* CSR Bluetooth Adapter (CSR8510,..) -* Raspberry Pi Bluetooth Adapter (BCM43455 on 3+, 4) +* Bluetooth 4.0 + - Intel Bluemoon Bluetooth Adapter + - CSR Bluetooth Adapter (CSR8510,..) + - Raspberry Pi Bluetooth Adapter (BCM43455 on 3+, 4) + +* Bluetooth 5.0 + - Intel AX200 Bluetooth 5.0 (Wi-Fi 6 802.11ax (2.4Gbps) + BT 5.0) ## Build Status @@ -373,6 +377,13 @@ make doc * TODO +**2.2.14** + +* Bluetooth 5.0 Support + - Using HCI extended scanning and connecting if supported (old API may not work on new adapter) + - Parsing and translating extended and enhanced event types, etc + - TODO: User selection of `LE_2M` and `L2_CODED`, ... ??? + **2.2.13** * Revised API: BTGattChar::addCharListener(..) in C++ and Java for a more intuitive use. diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index 3e5db55e..7acd9c41 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -254,6 +254,9 @@ namespace direct_bt { const bool debug_event, debug_lock; BTManager& mgmt; AdapterInfo adapterInfo; + LE_Features le_features; + bool hci_uses_ext_scan; + bool hci_uses_ext_conn; /** * Either the adapter's initially reported public address or a random address setup via HCI before scanning / discovery. @@ -298,6 +301,7 @@ namespace direct_bt { mutable std::mutex mtx_sharedDevices; // final mutex of all BTDevice lifecycle mutable jau::sc_atomic_bool sync_data; + bool updateDataFromHCI() noexcept; bool validateDevInfo() noexcept; static std::shared_ptr<BTDevice> findDevice(device_list_t & devices, const EUI48 & address, const BDAddressType addressType) noexcept; @@ -453,6 +457,20 @@ namespace direct_bt { } /** + * Return LE_Features for this controller. + * <pre> + * BT Core Spec v5.2: Vol 6, Part B, 4.6 (LE LL) Feature Support + * </pre> + */ + constexpr LE_Features getLEFeatures() const noexcept { return le_features; } + + /** Returns true if HCI_LE_Set_Extended_Scan_Parameters and HCI_LE_Set_Extended_Scan_Enable is supported (Bluetooth 5.0). */ + constexpr bool hasHCIExtScan() const noexcept { return hci_uses_ext_scan; } + + /** Returns true if HCI_LE_Extended_Create_Connection is supported (Bluetooth 5.0). */ + constexpr bool hasHCIExtConn() const noexcept { return hci_uses_ext_conn; } + + /** * Returns whether the adapter is valid, i.e. reference is valid, plugged in and generally operational, * but not necessarily BTAdapter::isPowered() powered. * @return true if this adapter references are valid and hasn't been BTAdapter::close() 'ed diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index 0b44afb1..3584beeb 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -68,7 +68,7 @@ namespace direct_bt { int8_t tx_power = 127; // The core spec defines 127 as the "not available" value AppearanceCat appearance = AppearanceCat::UNKNOWN; jau::relaxed_atomic_uint16 hciConnHandle; - jau::ordered_atomic<LEFeatures, std::memory_order_relaxed> le_features; + jau::ordered_atomic<LE_Features, std::memory_order_relaxed> le_features; std::shared_ptr<ManufactureSpecificData> advMSD = nullptr; jau::darray<std::shared_ptr<uuid_t>> advServices; #if SMP_SUPPORTED_BY_OS @@ -140,7 +140,7 @@ namespace direct_bt { void notifyDisconnected() noexcept; void notifyConnected(std::shared_ptr<BTDevice> sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept; - void notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LEFeatures features) noexcept; + void notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LE_Features features) noexcept; /** * Setup L2CAP channel connection to device incl. optional security encryption level off-thread. @@ -359,6 +359,17 @@ namespace direct_bt { bool getConnected() noexcept { return isConnected.load(); } /** + * Request and return LE_PHYs bit for the given connection. + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 7.8.47 LE Read PHY command (we transfer the sequential value to this bitmask for unification) + * </pre> + * @param resRx reference for the resulting receiver LE_PHYs bit + * @param resTx reference for the resulting transmitter LE_PHYs bit + * @return HCIStatusCode + */ + HCIStatusCode getConnectedLE_PHY(LE_PHYs& resRx, LE_PHYs& resTx) noexcept; + + /** * Establish a HCI BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM connection to this device. * <p> * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command diff --git a/api/direct_bt/BTTypes0.hpp b/api/direct_bt/BTTypes0.hpp index f5f3de4d..ce6b0326 100644 --- a/api/direct_bt/BTTypes0.hpp +++ b/api/direct_bt/BTTypes0.hpp @@ -76,37 +76,130 @@ namespace direct_bt { BTMode to_BTMode(const std::string & value) noexcept; /** + * HCI Supported Commands + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 6.27 (HCI) Supported Commands + * BT Core Spec v5.2: Vol 4, Part E, 7.4.2 Read Local Supported Commands command + * </pre> + * + * Unable to encode via enum and build-in integer: 64 octets = 512 bit wide! + * + enum class HCISupportedCommands : jau::uint512_t { + NONE = 0, + HCI_Create_Connection = ... + }; + */ + + /** * LE Link Layer Feature Set * <pre> - * BT Core Spec v5.2: Vol 6, Part B, 4.6 (LE) Features Support + * BT Core Spec v5.2: Vol 6, Part B, 4.6 (LE LL) Feature Support + * + * BT Core Spec v5.2: Vol 4, Part E, 7.8.3 LE Read Local Supported Features command + * + * BT Core Spec v5.2: Vol 4, Part E, 7.8.21 LE Read Remote Features command + * BT Core Spec v5.2: Vol 4, Part E, 7.7.65.4 LE Read Remote Features Complete event + * + * BT Core Spec v5.2: Vol 6, Part B, 7.8.115 LE Set Host Feature Command * </pre> */ - enum class LEFeatures : uint64_t { + enum class LE_Features : uint64_t { NONE = 0,/**< NONE */ LE_Encryption = 0b0000000000000000000000000000000000000000000000000000000000000001,/**< LE_Encryption */ - Conn_Param_Request_Proc = 0b0000000000000000000000000000000000000000000000000000000000000010 /**< Conn_Param_Request_Proc */ + Conn_Param_Req_Proc = 0b0000000000000000000000000000000000000000000000000000000000000010,/**< Conn_Param_Request_Proc */ + Ext_Rej_Ind = 0b0000000000000000000000000000000000000000000000000000000000000100, + SlaveInit_Feat_Exchg = 0b0000000000000000000000000000000000000000000000000000000000001000, + LE_Ping = 0b0000000000000000000000000000000000000000000000000000000000010000, + LE_Data_Pkt_Len_Ext = 0b0000000000000000000000000000000000000000000000000000000000100000, + LL_Privacy = 0b0000000000000000000000000000000000000000000000000000000001000000, + Ext_Scan_Filter_Pol = 0b0000000000000000000000000000000000000000000000000000000010000000, + LE_2M_PHY = 0b0000000000000000000000000000000000000000000000000000000100000000, + Stable_Mod_Idx_Tx = 0b0000000000000000000000000000000000000000000000000000001000000000, + Stable_Mod_Idx_Rx = 0b0000000000000000000000000000000000000000000000000000010000000000, + LE_Coded_PHY = 0b0000000000000000000000000000000000000000000000000000100000000000, + LE_Ext_Adv = 0b0000000000000000000000000000000000000000000000000001000000000000, + LE_Per_Adv = 0b0000000000000000000000000000000000000000000000000010000000000000, + Chan_Sel_Algo_2 = 0b0000000000000000000000000000000000000000000000000100000000000000, + LE_Pwr_Cls_1 = 0b0000000000000000000000000000000000000000000000001000000000000000, + Min_Num_Used_Chan_Proc = 0b0000000000000000000000000000000000000000000000010000000000000000, + Conn_CTE_Req = 0b0000000000000000000000000000000000000000000000100000000000000000, + Conn_CTE_Res = 0b0000000000000000000000000000000000000000000001000000000000000000, + ConnLess_CTE_Tx = 0b0000000000000000000000000000000000000000000010000000000000000000, + ConnLess_CTE_Rx = 0b0000000000000000000000000000000000000000000100000000000000000000, + AoD = 0b0000000000000000000000000000000000000000001000000000000000000000, + AoA = 0b0000000000000000000000000000000000000000010000000000000000000000, + Rx_Const_Tone_Ext = 0b0000000000000000000000000000000000000000100000000000000000000000, // bit #23 + Per_Adv_Sync_Tx_Sender = 0b0000000000000000000000000000000000000001000000000000000000000000, + Per_Adv_Sync_Tx_Rec = 0b0000000000000000000000000000000000000010000000000000000000000000, + Zzz_Clk_Acc_Upd = 0b0000000000000000000000000000000000000100000000000000000000000000, + Rem_Pub_Key_Val = 0b0000000000000000000000000000000000001000000000000000000000000000, + Conn_Iso_Stream_Master = 0b0000000000000000000000000000000000010000000000000000000000000000, + Conn_Iso_Stream_Slave = 0b0000000000000000000000000000000000100000000000000000000000000000, + Iso_Brdcst = 0b0000000000000000000000000000000001000000000000000000000000000000, + Sync_Rx = 0b0000000000000000000000000000000010000000000000000000000000000000, + Iso_Chan = 0b0000000000000000000000000000000100000000000000000000000000000000, + LE_Pwr_Ctrl_Req = 0b0000000000000000000000000000001000000000000000000000000000000000, + LE_Pwr_Chg_Ind = 0b0000000000000000000000000000010000000000000000000000000000000000, + LE_Path_Loss_Mon = 0b0000000000000000000000000000100000000000000000000000000000000000 // bit #35 }; - constexpr uint64_t number(const LEFeatures rhs) noexcept { + constexpr uint64_t number(const LE_Features rhs) noexcept { return static_cast<uint64_t>(rhs); } - constexpr LEFeatures operator ^(const LEFeatures lhs, const LEFeatures rhs) noexcept { - return static_cast<LEFeatures> ( number(lhs) ^ number(rhs) ); + constexpr LE_Features operator ^(const LE_Features lhs, const LE_Features rhs) noexcept { + return static_cast<LE_Features> ( number(lhs) ^ number(rhs) ); + } + constexpr LE_Features operator |(const LE_Features lhs, const LE_Features rhs) noexcept { + return static_cast<LE_Features> ( number(lhs) | number(rhs) ); + } + constexpr LE_Features operator &(const LE_Features lhs, const LE_Features rhs) noexcept { + return static_cast<LE_Features> ( number(lhs) & number(rhs) ); + } + constexpr bool operator ==(const LE_Features lhs, const LE_Features rhs) noexcept { + return number(lhs) == number(rhs); + } + constexpr bool operator !=(const LE_Features lhs, const LE_Features rhs) noexcept { + return !( lhs == rhs ); + } + constexpr bool isLEFeaturesBitSet(const LE_Features mask, const LE_Features bit) noexcept { + return LE_Features::NONE != ( mask & bit ); } - constexpr LEFeatures operator |(const LEFeatures lhs, const LEFeatures rhs) noexcept { - return static_cast<LEFeatures> ( number(lhs) | number(rhs) ); + std::string to_string(const LE_Features mask) noexcept; + + /** + * LE Transport PHY bit values + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 7.8.47 LE Read PHY command (we transfer the sequential value to this bitmask for unification) + * BT Core Spec v5.2: Vol 4, Part E, 7.8.48 LE Set Default PHY command + * </pre> + */ + enum class LE_PHYs : uint8_t { + NONE = 0, + LE_1M = 0b00000001, + LE_2M = 0b00000010, + LE_CODED = 0b00000100 + }; + constexpr uint8_t number(const LE_PHYs rhs) noexcept { + return static_cast<uint8_t>(rhs); + } + constexpr LE_PHYs operator ^(const LE_PHYs lhs, const LE_PHYs rhs) noexcept { + return static_cast<LE_PHYs> ( number(lhs) ^ number(rhs) ); + } + constexpr LE_PHYs operator |(const LE_PHYs lhs, const LE_PHYs rhs) noexcept { + return static_cast<LE_PHYs> ( number(lhs) | number(rhs) ); } - constexpr LEFeatures operator &(const LEFeatures lhs, const LEFeatures rhs) noexcept { - return static_cast<LEFeatures> ( number(lhs) & number(rhs) ); + constexpr LE_PHYs operator &(const LE_PHYs lhs, const LE_PHYs rhs) noexcept { + return static_cast<LE_PHYs> ( number(lhs) & number(rhs) ); } - constexpr bool operator ==(const LEFeatures lhs, const LEFeatures rhs) noexcept { + constexpr bool operator ==(const LE_PHYs lhs, const LE_PHYs rhs) noexcept { return number(lhs) == number(rhs); } - constexpr bool operator !=(const LEFeatures lhs, const LEFeatures rhs) noexcept { + constexpr bool operator !=(const LE_PHYs lhs, const LE_PHYs rhs) noexcept { return !( lhs == rhs ); } - constexpr bool isLEFeaturesBitSet(const LEFeatures mask, const LEFeatures bit) noexcept { - return LEFeatures::NONE != ( mask & bit ); + constexpr bool isLEPHYBitSet(const LE_PHYs mask, const LE_PHYs bit) noexcept { + return LE_PHYs::NONE != ( mask & bit ); } + std::string to_string(const LE_PHYs mask) noexcept; /** * Bluetooth Security Level. @@ -240,6 +333,7 @@ namespace direct_bt { * LE Advertising (AD) Protocol Data Unit (PDU) Types * <p> * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.7.65.2 LE Advertising Report event + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.7.65.13 LE Extended Advertising Report event * BT Core Spec v5.2: Vol 6 LE Controller, Part B Link Layer: 2.3 Advertising physical channel PDU * BT Core Spec v5.2: Vol 6 LE Controller, Part B Link Layer: 2.3.1 Advertising PDUs * </p> @@ -249,15 +343,30 @@ namespace direct_bt { * Advertising Indications (ADV_IND), * where a peripheral device requests connection to any central device * (i.e., not directed at a particular central device). */ - ADV_IND = 0x00, + ADV_IND = 0x00, /** Similar to ADV_IND, yet the connection request is directed at a specific central device. */ - ADV_DIRECT_IND = 0x01, - /** Similar to ADV_NONCONN_IND, with the option additional information via scan responses. */ - ADV_SCAN_IND = 0x02, + ADV_DIRECT_IND = 0x01, + /** Similar to ADV_IND, w/o connection requests and with the option additional information via scan responses. */ + ADV_SCAN_IND = 0x02, /** Non connectable devices, advertising information to any listening device. */ - ADV_NONCONN_IND = 0x03, - SCAN_RSP = 0x04, - ADV_UNDEFINED = 0xff + ADV_NONCONN_IND = 0x03, + /** Scan response PDU type. */ + SCAN_RSP = 0x04, + + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_IND variant */ + ADV_IND2 = 0b0010011, + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_DIRECT_IND variant */ + DIRECT_IND2 = 0b0010101, + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_SCAN_IND variant */ + SCAN_IND2 = 0b0010010, + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_NONCONN_IND variant */ + NONCONN_IND2 = 0b0010000, + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: SCAN_RSP variant to an ADV_IND */ + SCAN_RSP_to_ADV_IND = 0b0011011, + /** EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: SCAN_RSP variant to an ADV_SCAN_IND */ + SCAN_RSP_to_ADV_SCAN_IND = 0b0011010, + + UNDEFINED = 0xff }; constexpr uint8_t number(const AD_PDU_Type rhs) noexcept { return static_cast<uint8_t>(rhs); @@ -266,6 +375,41 @@ namespace direct_bt { /** + * LE Extended Advertising (EAD) Event Types + * <p> + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.7.65.13 LE Extended Advertising Report event + * </p> + */ + enum class EAD_Event_Type : uint16_t { + NONE = 0, + CONN_ADV = 0b00000001, + SCAN_ADV = 0b00000010, + DIR_ADV = 0b00000100, + SCAN_RSP = 0b00001000, + LEGACY_PDU = 0b00010000, + DATA_B0 = 0b00100000, + DATA_B1 = 0b01000000, + }; + constexpr uint16_t number(const EAD_Event_Type rhs) noexcept { + return static_cast<uint16_t>(rhs); + } + constexpr EAD_Event_Type operator |(const EAD_Event_Type lhs, const EAD_Event_Type rhs) noexcept { + return static_cast<EAD_Event_Type> ( number(lhs) | number(rhs) ); + } + constexpr EAD_Event_Type operator &(const EAD_Event_Type lhs, const EAD_Event_Type rhs) noexcept { + return static_cast<EAD_Event_Type> ( number(lhs) & number(rhs) ); + } + constexpr bool operator ==(const EAD_Event_Type lhs, const EAD_Event_Type rhs) noexcept { + return number(lhs) == number(rhs); + } + constexpr bool operator !=(const EAD_Event_Type lhs, const EAD_Event_Type rhs) noexcept { + return !( lhs == rhs ); + } + constexpr bool isEAD_Event_TypeSet(const EAD_Event_Type mask, const EAD_Event_Type bit) noexcept { return EAD_Event_Type::NONE != ( mask & bit ); } + constexpr void setEAD_Event_TypeSet(EAD_Event_Type &mask, const EAD_Event_Type bit) noexcept { mask = mask | bit; } + std::string to_string(const EAD_Event_Type v) noexcept; + + /** * HCI Whitelist connection type. */ enum class HCIWhitelistConnectType : uint8_t { @@ -608,30 +752,31 @@ namespace direct_bt { enum class EIRDataType : uint32_t { NONE = 0, EVT_TYPE = (1 << 0), - BDADDR_TYPE = (1 << 1), - BDADDR = (1 << 2), - FLAGS = (1 << 3), - NAME = (1 << 4), - NAME_SHORT = (1 << 5), - RSSI = (1 << 6), - TX_POWER = (1 << 7), - MANUF_DATA = (1 << 8), - DEVICE_CLASS = (1 << 9), - APPEARANCE = (1 << 10), - HASH = (1 << 11), - RANDOMIZER = (1 << 12), - DEVICE_ID = (1 << 13), + EXT_EVT_TYPE = (1 << 1), + BDADDR_TYPE = (1 << 2), + BDADDR = (1 << 3), + FLAGS = (1 << 4), + NAME = (1 << 5), + NAME_SHORT = (1 << 6), + RSSI = (1 << 7), + TX_POWER = (1 << 8), + MANUF_DATA = (1 << 9), + DEVICE_CLASS = (1 << 10), + APPEARANCE = (1 << 11), + HASH = (1 << 12), + RANDOMIZER = (1 << 13), + DEVICE_ID = (1 << 14), SERVICE_UUID = (1 << 30) }; constexpr uint32_t number(const EIRDataType rhs) noexcept { return static_cast<uint32_t>(rhs); } constexpr EIRDataType operator |(const EIRDataType lhs, const EIRDataType rhs) noexcept { - return static_cast<EIRDataType> ( static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs) ); + return static_cast<EIRDataType> ( number(lhs) | number(rhs) ); } constexpr EIRDataType operator &(const EIRDataType lhs, const EIRDataType rhs) noexcept { - return static_cast<EIRDataType> ( static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs) ); + return static_cast<EIRDataType> ( number(lhs) & number(rhs) ); } constexpr bool operator ==(const EIRDataType lhs, const EIRDataType rhs) noexcept { - return static_cast<uint32_t>(lhs) == static_cast<uint32_t>(rhs); + return number(lhs) == number(rhs); } constexpr bool operator !=(const EIRDataType lhs, const EIRDataType rhs) noexcept { return !( lhs == rhs ); @@ -641,8 +786,18 @@ namespace direct_bt { std::string to_string(const EIRDataType mask) noexcept; /** - * Collection of 'Advertising Data' (AD) + * Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) * or 'Extended Inquiry Response' (EIR) information. + * + * References: + * + * - BT Core Spec v5.2: Vol 4, Part E, 7.7.65.2 LE Advertising Report event + * - BT Core Spec v5.2: Vol 4, Part E, 7.7.65.13 LE Extended Advertising Report event + * - BT Core Spec v5.2: Vol 3, Part C, 11 ADVERTISING AND SCAN RESPONSE DATA FORMAT + * - BT Core Spec v5.2: Vol 3, Part C, 8 EXTENDED INQUIRY RESPONSE DATA FORMAT + * - BT Core Spec Supplement v9, Part A: Section 1 + 2 Examples, p25.. + * - [Assigned Numbers - Generic Access Profile](https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/) + * */ class EInfoReport { @@ -652,6 +807,8 @@ namespace direct_bt { NA, /* Advertising Data (AD) */ AD, + /* Extended Advertising Data (EAD) */ + EAD, /** Extended Inquiry Response (EIR) */ EIR, /** Extended Inquiry Response (EIR) from Kernel Mgmt */ @@ -663,7 +820,8 @@ namespace direct_bt { uint64_t timestamp = 0; EIRDataType eir_data_mask = static_cast<EIRDataType>(0); - AD_PDU_Type evt_type = AD_PDU_Type::ADV_UNDEFINED; + AD_PDU_Type evt_type = AD_PDU_Type::UNDEFINED; + EAD_Event_Type ead_type = EAD_Event_Type::NONE; uint8_t ad_address_type = 0; BDAddressType addressType = BDAddressType::BDADDR_UNDEFINED; EUI48 address; @@ -706,6 +864,7 @@ namespace direct_bt { void setSource(Source s) noexcept { source = s; } void setTimestamp(uint64_t ts) noexcept { timestamp = ts; } void setEvtType(AD_PDU_Type et) noexcept { evt_type = et; set(EIRDataType::EVT_TYPE); } + void setExtEvtType(EAD_Event_Type eadt) noexcept { ead_type = eadt; set(EIRDataType::EXT_EVT_TYPE); } void setADAddressType(uint8_t adAddressType) noexcept; void setAddressType(BDAddressType at) noexcept; void setAddress(EUI48 const &a) noexcept { address = a; set(EIRDataType::BDADDR); } @@ -714,16 +873,32 @@ namespace direct_bt { /** * Reads a complete Advertising Data (AD) Report * and returns the number of AD reports in form of a sharable list of EInfoReport; - * <p> - * See Bluetooth Core Specification V5.2 [Vol. 4, Part E, 7.7.65.2, p 2382] - * <p> + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 7.7.65.2 LE Advertising Report event + * BT Core Spec v5.2: Vol 3, Part C, 11 ADVERTISING AND SCAN RESPONSE DATA FORMAT + * BT Core Spec v5.2: Vol 3, Part C, 8 EXTENDED INQUIRY RESPONSE DATA FORMAT + * <pre> * https://www.bluetooth.com/specifications/archived-specifications/ * </p> */ static jau::darray<std::unique_ptr<EInfoReport>> read_ad_reports(uint8_t const * data, jau::nsize_t const data_length) noexcept; /** - * Reads the Extended Inquiry Response (EIR) or Advertising Data (AD) segments + * Reads a complete Extended Advertising Data (AD) Report + * and returns the number of AD reports in form of a sharable list of EInfoReport; + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 7.7.65.13 LE Extended Advertising Report event + * BT Core Spec v5.2: Vol 3, Part C, 11 ADVERTISING AND SCAN RESPONSE DATA FORMAT + * BT Core Spec v5.2: Vol 3, Part C, 8 EXTENDED INQUIRY RESPONSE DATA FORMAT + * </pre> + * <p> + * https://www.bluetooth.com/specifications/archived-specifications/ + * </p> + */ + static jau::darray<std::unique_ptr<EInfoReport>> read_ext_ad_reports(uint8_t const * data, jau::nsize_t const data_length) noexcept; + + /** + * Reads the Extended Inquiry Response (EIR) or (Extended) Advertising Data (EAD or AD) segments * and returns the number of parsed data segments; * <p> * AD as well as EIR information is passed in little endian order @@ -738,10 +913,14 @@ namespace direct_bt { * </pre> * </p> * <p> - * * See Bluetooth Core Specification V5.2 [Vol. 3, Part C, Section 8] - * * and Bluetooth Core Specification V5.2 [Vol. 3, Part C, Section 11] - * * and Bluetooth Core Specification Supplement V9, Part A: Section 1 + 2 Examples, p25.. - * * and [Assigned Numbers - Generic Access Profile](https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/) + * + * References: + * + * - BT Core Spec v5.2: Vol 3, Part C, 11 ADVERTISING AND SCAN RESPONSE DATA FORMAT + * - BT Core Spec v5.2: Vol 3, Part C, 8 EXTENDED INQUIRY RESPONSE DATA FORMAT + * - BT Core Spec Supplement v9, Part A: Section 1 + 2 Examples, p25.. + * - [Assigned Numbers - Generic Access Profile](https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/) + * * </p> * <p> * https://www.bluetooth.com/specifications/archived-specifications/ @@ -755,6 +934,7 @@ namespace direct_bt { EIRDataType getEIRDataMask() const noexcept { return eir_data_mask; } AD_PDU_Type getEvtType() const noexcept { return evt_type; } + EAD_Event_Type getExtEvtType() const noexcept { return ead_type; } GAPFlags getFlags() const noexcept { return flags; } uint8_t getADAddressType() const noexcept { return ad_address_type; } BDAddressType getAddressType() const noexcept { return addressType; } diff --git a/api/direct_bt/HCIHandler.hpp b/api/direct_bt/HCIHandler.hpp index 780e1e63..9fb5716b 100644 --- a/api/direct_bt/HCIHandler.hpp +++ b/api/direct_bt/HCIHandler.hpp @@ -255,6 +255,16 @@ namespace direct_bt { std::recursive_mutex mtx_sendReply; // for sendWith*Reply, process*Command, ..; Recurses from many.. + /** + * Cached bitfield of Local Supported Commands, 64 octets + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 6.27 (HCI) Supported Commands + * BT Core Spec v5.2: Vol 4, Part E, 7.4.2 Read Local Supported Commands command + * </pre> + */ + uint8_t sup_commands[64]; + jau::relaxed_atomic_bool sup_commands_set; + jau::sc_atomic_bool allowClose; std::atomic<BTMode> btMode; @@ -267,6 +277,9 @@ namespace direct_bt { /** Exclusive [le] connection command (status + pending completed) one at a time */ std::mutex mtx_connect_cmd; + void zeroSupCommands() noexcept; + bool initSupCommands() noexcept; + /** * Returns a newly added HCIConnectionRef tracker connection with given parameters, if not existing yet. * <p> @@ -362,6 +375,17 @@ namespace direct_bt { return true == allowClose.load() && comm.isOpen(); } + /** Use extended scanning if HCI_LE_Set_Extended_Scan_Parameters and HCI_LE_Set_Extended_Scan_Enable is supported (Bluetooth 5.0). */ + bool use_ext_scan() const noexcept{ + return 0 != ( sup_commands[37] & ( 1 << 5 ) ) && + 0 != ( sup_commands[37] & ( 1 << 6 ) ); + } + + /** Use extended connection if HCI_LE_Extended_Create_Connection is supported (Bluetooth 5.0). */ + bool use_ext_conn() const noexcept{ + return 0 != ( sup_commands[37] & ( 1 << 7 ) ); + } + ScanType getCurrentScanType() const noexcept { return currentScanType.load(); } void setCurrentScanType(const ScanType v) noexcept { currentScanType = v; } @@ -398,18 +422,39 @@ namespace direct_bt { HCIStatusCode resetAdapter(); /** + * HCI Reset Command + * <pre> * BT Core Spec v5.2: Vol 4, Part E HCI: 7.3.2 Reset command + * </pre> */ HCIStatusCode reset() noexcept; HCIStatusCode getLocalVersion(HCILocalVersion &version) noexcept; /** + * Request and return LE_Features for the controller. + * <pre> + * BT Core Spec v5.2: Vol 6, Part B, 4.6 (LE LL) Feature Support + * + * BT Core Spec v5.2: Vol 4, Part E, 7.8.3 LE Read Local Supported Features command + * </pre> + * @param res reference for the resulting LE_Features + * @return HCIStatusCode + */ + HCIStatusCode le_read_local_features(LE_Features& res) noexcept; + + /** * Sets LE scanning parameters. - * <p> + * <pre> + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.64 LE Set Extended Scan Parameters command (Bluetooth 5.0) + * + * if available, otherwise using + * * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.10 LE Set Scan Parameters command + * + * * BT Core Spec v5.2: Vol 6 LE, Part B Link Layer: 4.4.3 Scanning State - * </p> + * </pre> * <p> * Scan parameters control advertising (AD) Protocol Data Unit (PDU) delivery behavior. * </p> @@ -430,9 +475,13 @@ namespace direct_bt { /** * Starts or stops LE scanning. - * <p> + * <pre> + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.65 LE Set Extended Scan Enable command (Bluetooth 5.0) + * + * if available, otherwise using + * * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.11 LE Set Scan Enable command - * </p> + * </pre> * @param enable true to enable discovery, otherwise false * @param filter_dup true to filter out duplicate AD PDUs (default), otherwise all will be reported. */ @@ -440,10 +489,12 @@ namespace direct_bt { /** * Start LE scanning, i.e. performs le_set_scan_param() and le_enable_scan() in one atomic operation. - * <p> + * <pre> + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.64 LE Set Extended Scan Parameters command (Bluetooth 5.0) * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.10 LE Set Scan Parameters command + * BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.65 LE Set Extended Scan Enable command (Bluetooth 5.0) * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.11 LE Set Scan Enable command - * </p> + * </pre> * <p> * Scan parameters control advertising (AD) Protocol Data Unit (PDU) delivery behavior. * </p> @@ -460,6 +511,7 @@ namespace direct_bt { * @param le_scan_interval in units of 0.625ms, default value 24 for 15ms; Value range [4 .. 0x4000] for [2.5ms .. 10.24s] * @param le_scan_window in units of 0.625ms, default value 24 for 15ms; Value range [4 .. 0x4000] for [2.5ms .. 10.24s]. Shall be <= le_scan_interval * @param filter_policy 0x00 accepts all PDUs (default), 0x01 only of whitelisted, ... + * @see le_read_local_features() */ HCIStatusCode le_start_scan(const bool filter_dup=true, const bool le_scan_active=false, @@ -469,9 +521,13 @@ namespace direct_bt { /** * Establish a connection to the given LE peer. - * <p> + * <pre> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.66 LE Extended Create Connection command (Bluetooth 5.0) + * + * if available, otherwise using + * * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command - * </p> + * </pre> * <p> * Set window to the same value as the interval, enables continuous scanning. * </p> @@ -516,9 +572,9 @@ namespace direct_bt { /** * Establish a connection to the given BREDR (non LE). - * <p> + * <pre> * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command - * </p> + * </pre> * <p> * Implementation tries to mitigate HCIStatusCode::COMMAND_DISALLOWED failure due to any pending connection commands, * waiting actively up to HCIEnv::HCI_COMMAND_COMPLETE_REPLY_TIMEOUT, testing every HCIEnv::HCI_COMMAND_POLL_PERIOD if resolved.<br> @@ -540,17 +596,32 @@ namespace direct_bt { /** * Disconnect an established connection. - * <p> + * <pre> * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command - * </p> + * </pre> */ HCIStatusCode disconnect(const uint16_t conn_handle, const BDAddressAndType& addressAndType, const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept; /** - * Clear all internal states, i.e. connection and disconnect lists. + * Request and return LE_PHYs bit for the given connection. + * <pre> + * BT Core Spec v5.2: Vol 4, Part E, 7.8.47 LE Read PHY command (we transfer the sequential value to this bitmask for unification) + * </pre> + * @param conn_handle + * @param addressAndType + * @param resRx reference for the resulting receiver LE_PHYs bit + * @param resTx reference for the resulting transmitter LE_PHYs bit + * @return HCIStatusCode */ - void clearAllStates() noexcept; + HCIStatusCode le_read_phy(const uint16_t conn_handle, const BDAddressAndType& addressAndType, + LE_PHYs& resRx, LE_PHYs& resTx) noexcept; + + /** + * Reset all internal states, i.e. connection and disconnect lists. + * @param powered_on indicates whether the adapter is powered on or not + */ + void resetAllStates(const bool powered_on) noexcept; /** MgmtEventCallback handling */ @@ -576,46 +647,6 @@ namespace direct_bt { /** Manually send a MgmtEvent to all of its listeners. */ void sendMgmtEvent(const MgmtEvent& event) noexcept; - - /** - * FIXME / TODO: Privacy Mode / Pairing / Bonding - * - * LE Secure Connections: - * <pre> - * BT Core Spec v5.2: Vol 1, Part A Architecture: 5.4 LE Security - * BT Core Spec v5.2: Vol 3, Part C GAP: 10.2 LE SECURITY MODES - * BT Core Spec v5.2: Vol 3, Part H SM: 2 Security Manager - * BT Core Spec v5.2: Vol 3, Part H SM: 2.3.5 Pairing: 2.3.5.6 LE Secure Connections pairing phase 2 - * BT Core Spec v5.2: Vol 3, Part H SM: 2.3.5 Pairing: 2.3.5.6.3 LE Authentication stage 1 – Passkey Entry - * BT Core Spec v5.2: Vol 3, Part H SM: 3 Security Manager Protocol (SMP) fixed channel over L2CAP - * - * - * LE Legacy Pairing** similar to BREDR like: Secure Simple Pairing (SSP) - * LE Secure Connections functional equivalent to SSP, using 128 bit Long Term Key (LTK) - * </pre> - * - * <p> - * BT Core Spec v5.2: Vol 1, Part A Architecture: 5 Security architecture - * BT Core Spec v5.2: Vol 1, Part A Architecture: 5.4 LE Security - * BT Core Spec v5.2: Vol 1, Part A Architecture: 5.4.5 LE Privacy feature - * - device privacy mode (mixed mode, also accept other peer address) - * - network privacy mode (only private address - default!) - * -> add device to resolving list, implying being added to device white list! - * - * BT Core Spec v5.2: Vol 3, Part C GAP: 10.2 LE SECURITY MODES - * - * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.77 LE Set Privacy Mode command - * BT Core Spec v5.2: Vol 6 LE Adapter, Part B Link Layer Spec: 4.7 Resolving List - * </p> - * FIXME: TODO - * - * @return - * HCIStatusCode le_set_privacy_mode(); - * - * Fills buffer with random bytes, usually 16 bytes for 128 bit key. - * FIXME: TODO - * static bool generateIRK(uint8_t *buffer, int size); - */ }; } // namespace direct_bt diff --git a/api/direct_bt/HCITypes.hpp b/api/direct_bt/HCITypes.hpp index b0cd8a8e..fd6b7890 100644 --- a/api/direct_bt/HCITypes.hpp +++ b/api/direct_bt/HCITypes.hpp @@ -322,7 +322,7 @@ namespace direct_bt { LE_DATA_LENGTH_CHANGE = 0x07,/**< LE_DATA_LENGTH_CHANGE */ LE_READ_LOCAL_P256_PUBKEY_COMPLETE = 0x08,/**< LE_READ_LOCAL_P256_PUBKEY_COMPLETE */ LE_GENERATE_DHKEY_COMPLETE = 0x09,/**< LE_GENERATE_DHKEY_COMPLETE */ - LE_ENHANCED_CONN_COMPLETE = 0x0A,/**< LE_ENHANCED_CONN_COMPLETE */ + LE_EXT_CONN_COMPLETE = 0x0A,/**< LE_ENHANCED_CONN_COMPLETE */ LE_DIRECT_ADV_REPORT = 0x0B,/**< LE_DIRECT_ADV_REPORT */ LE_PHY_UPDATE_COMPLETE = 0x0C,/**< LE_PHY_UPDATE_COMPLETE */ LE_EXT_ADV_REPORT = 0x0D,/**< LE_EXT_ADV_REPORT */ @@ -374,6 +374,7 @@ namespace direct_bt { SET_EVENT_MASK = 0x0C01,/**< SET_EVENT_MASK */ RESET = 0x0C03, READ_LOCAL_VERSION = 0x1001, + READ_LOCAL_COMMANDS = 0x1002, LE_SET_EVENT_MASK = 0x2001,/**< LE_SET_EVENT_MASK */ LE_READ_BUFFER_SIZE = 0x2002, LE_READ_LOCAL_FEATURES = 0x2003, @@ -393,7 +394,12 @@ namespace direct_bt { LE_DEL_FROM_WHITE_LIST = 0x2012, LE_CONN_UPDATE = 0x2013, LE_READ_REMOTE_FEATURES = 0x2016, - LE_ENABLE_ENC = 0x2019 + LE_ENABLE_ENC = 0x2019, + LE_READ_PHY = 0x2030, + LE_SET_DEFAULT_PHY = 0x2031, + LE_SET_EXT_SCAN_PARAMS = 0x2041, + LE_SET_EXT_SCAN_ENABLE = 0x2042, + LE_EXT_CREATE_CONN = 0x2043, // etc etc - incomplete }; constexpr uint16_t number(const HCIOpcode rhs) noexcept { @@ -410,6 +416,7 @@ namespace direct_bt { SET_EVENT_MASK = 7, RESET = 8, READ_LOCAL_VERSION = 10, + READ_LOCAL_COMMANDS = 11, LE_SET_EVENT_MASK = 20, LE_READ_BUFFER_SIZE = 21, LE_READ_LOCAL_FEATURES = 22, @@ -429,7 +436,12 @@ namespace direct_bt { LE_DEL_FROM_WHITE_LIST = 36, LE_CONN_UPDATE = 37, LE_READ_REMOTE_FEATURES = 38, - LE_ENABLE_ENC = 39 + LE_ENABLE_ENC = 39, + LE_READ_PHY = 40, + LE_SET_DEFAULT_PHY = 41, + LE_SET_EXT_SCAN_PARAMS = 42, + LE_SET_EXT_SCAN_ENABLE = 43, + LE_EXT_CREATE_CONN = 44 // etc etc - incomplete }; constexpr uint8_t number(const HCIOpcodeBit rhs) noexcept { @@ -559,7 +571,7 @@ namespace direct_bt { HCICommand(const HCIOpcode opc, const jau::nsize_t param_size) : HCIPacket(HCIPacketType::COMMAND, number(HCIConstSizeT::COMMAND_HDR_SIZE)+param_size) { - checkOpcode(opc, HCIOpcode::SPECIAL, HCIOpcode::LE_ENABLE_ENC); + checkOpcode(opc, HCIOpcode::SPECIAL, HCIOpcode::LE_EXT_CREATE_CONN); pdu.put_uint16_nc(1, static_cast<uint16_t>(opc)); pdu.put_uint8_nc(3, param_size); diff --git a/api/direct_bt/MgmtTypes.hpp b/api/direct_bt/MgmtTypes.hpp index 7f31bfb2..21fd5518 100644 --- a/api/direct_bt/MgmtTypes.hpp +++ b/api/direct_bt/MgmtTypes.hpp @@ -2177,7 +2177,7 @@ namespace direct_bt { } public: - MgmtEvtHCILERemoteUserFeatures(const uint16_t dev_id, const BDAddressAndType& addressAndType, const LEFeatures features_) + MgmtEvtHCILERemoteUserFeatures(const uint16_t dev_id, const BDAddressAndType& addressAndType, const LE_Features features_) : MgmtEvent(Opcode::HCI_LE_REMOTE_USR_FEATURES, dev_id, 6+1+8) { pdu.put_eui48_nc(MGMT_HEADER_SIZE, addressAndType.address); @@ -2188,7 +2188,7 @@ namespace direct_bt { const EUI48& getAddress() const noexcept { return *reinterpret_cast<const EUI48 *>( pdu.get_ptr_nc(MGMT_HEADER_SIZE + 0) ); } // mgmt_addr_info BDAddressType getAddressType() const noexcept { return static_cast<BDAddressType>(pdu.get_uint8_nc(MGMT_HEADER_SIZE+6)); } // mgmt_addr_info - LEFeatures getFeatures() const noexcept { return static_cast<LEFeatures>(pdu.get_uint64_nc(MGMT_HEADER_SIZE+6+1)); } + LE_Features getFeatures() const noexcept { return static_cast<LE_Features>(pdu.get_uint64_nc(MGMT_HEADER_SIZE+6+1)); } jau::nsize_t getDataOffset() const noexcept override { return MGMT_HEADER_SIZE+6+1+8; } jau::nsize_t getDataSize() const noexcept override { return 0; } diff --git a/api/direct_bt/SMPHandler.hpp b/api/direct_bt/SMPHandler.hpp index bbc269fc..06f14420 100644 --- a/api/direct_bt/SMPHandler.hpp +++ b/api/direct_bt/SMPHandler.hpp @@ -64,6 +64,26 @@ * - BT Core Spec v5.2: Vol 3, Part H Security Manager Specification (SM): 2 Security Manager (SM) * - BT Core Spec v5.2: Vol 3, Part H Security Manager Specification (SM): 3 Security Manager Protocol (SMP) * + * Overall bookmark regarding BT Security + * + * - BT Core Spec v5.2: Vol 1, Part A Architecture: 5 Security architecture + * - BT Core Spec v5.2: Vol 1, Part A Architecture: 5.4 LE Security + * - BT Core Spec v5.2: Vol 1, Part A Architecture: 5.4.5 LE Privacy feature + * - device privacy mode (mixed mode, also accept other peer address) + * - network privacy mode (only private address - default!) + * - add device to resolving list, implying being added to device white list! + * + * - BT Core Spec v5.2: Vol 3, Part C GAP: 10.2 LE SECURITY MODES + * + * - BT Core Spec v5.2: Vol 3, Part H Security Manager Specification (SM): 2 Security Manager (SM) + * - 2.3.5 Pairing: 2.3.5.6 LE Secure Connections pairing phase 2 + * - 2.3.5 Pairing: 2.3.5.6.3 LE Authentication stage 1 – Passkey Entry + * - BT Core Spec v5.2: Vol 3, Part H Security Manager Specification (SM): 3 Security Manager Protocol (SMP) + * - fixed channel over L2CAP + * + * - BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.77 LE Set Privacy Mode command + * + * - BT Core Spec v5.2: Vol 6 LE Adapter, Part B Link Layer Spec: 4.7 Resolving List */ namespace direct_bt { diff --git a/doc/log/dbt_scanner10-bt5_intel-01.btmon b/doc/log/dbt_scanner10-bt5_intel-01.btmon Binary files differnew file mode 100644 index 00000000..a0491e28 --- /dev/null +++ b/doc/log/dbt_scanner10-bt5_intel-01.btmon diff --git a/doc/log/dbt_scanner10-bt5_intel-01.log.txt b/doc/log/dbt_scanner10-bt5_intel-01.log.txt new file mode 100644 index 00000000..7ecfafc8 --- /dev/null +++ b/doc/log/dbt_scanner10-bt5_intel-01.log.txt @@ -0,0 +1,292 @@ +BT Adapter: Intel AX200 Bluetooth 5.0 (Wi-Fi 6 802.11ax (2.4Gbps) + BT 5.0) + +Full Procedure, start with adapter powered-off, then power-on: +Start powered-off: +- rfkill block 5 +- start test + +Power on: +- rfkill unblock 5 +- hciconfig hci0 up + ++++ + +script invocation: ../scripts/run-dbt_scanner10.sh -dev TD1107 -seclevel TD1107 1 -dbt_debug false +username sven +run_setcap 0 +run_root 0 +dbt_scanner10 commandline -dev TD1107 -seclevel TD1107 1 -dbt_debug false +EXE_WRAPPER +logbasename /home/sven/run-dbt_scanner10 +logfile /home/sven/run-dbt_scanner10.log +valgrindlogfile /home/sven/run-dbt_scanner10-valgrind.log +callgrindoutfile /home/sven/run-dbt_scanner10-callgrind.out +direct_bt_debug +direct_bt_verbose +LD_LIBRARY_PATH=/usr/local/projects/zafena/direct_bt/dist-amd64/lib bin/dbt_scanner10 -dev TD1107 -seclevel TD1107 1 -dbt_debug false +sudo capsh ... +Set sec_level in BTSecurityDetail['TD1107', lvl NONE, io UNSET, auto-io UNSET, passkey -1] +pid 2336286 +Run with '[-btmode LE|BREDR|DUAL] [-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] [-scanPassive][-resetEachCon connectionCount] (-dev <device_[address|name]_sub>)* (-wl <device_address>)* (-seclevel <device_[address|name]_sub> <int_sec_level>)* (-iocap <device_[address|name]_sub> <int_iocap>)* (-secauto <device_[address|name]_sub> <int_iocap>)* (-passkey <device_[address|name]_sub> <digits>)* [-unpairPre] [-unpairPost] [-charid <uuid>] [-charval <byte-val>] [-dbt_verbose true|false] [-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] [-dbt_mgmt cmd.timeout=3000,ringsize=64,...] [-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] [-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] [-dbt_l2cap reader.timeout=10000,restart.count=0,...] +MULTI_MEASUREMENTS 8 +KEEP_CONNECTED 1 +RESET_ADAPTER_EACH_CONN 0 +GATT_PING_ENABLED 0 +REMOVE_DEVICE 1 +USE_WHITELIST 0 +SHOW_UPDATE_EVENTS 0 +QUIET 0 +btmode DUAL +scanActive 1 +characteristic-id: +characteristic-value: 0 +security-details: BTSecurityDetail['TD1107', lvl NONE, io UNSET, auto-io UNSET, passkey -1] +waitForDevice: [n: 'TD1107'] +****** TEST start +[ 1] Error @ /src/direct_bt/HCIHandler.cpp:520 sendCommand: HCIHandler::sendCommand: HCIComm write error, req HCICommand[opcode=0x1002 READ_LOCAL_COMMANDS, param[size 0, data ], tsz 4] - HCIHandler[dev_id 0, BTMode NONE, open 1, scan NONE, ext[init 0, scan 0, conn 0], ring[entries 0]]; last errno 100 Network is down +[ 1] Warning @ /src/direct_bt/HCIHandler.cpp:1459 processCommandComplete: HCIHandler::processCommandComplete Send failed: Status 0xFE (INTERNAL_FAILURE), errno 11 Resource temporarily unavailable: res nullptr, req HCICommand[opcode=0x1002 READ_LOCAL_COMMANDS, param[size 0, data ], tsz 4] - HCIHandler[dev_id 0, BTMode NONE, open 1, scan NONE, ext[init 0, scan 0, conn 0], ring[entries 0]] +[ 2] Error @ /src/direct_bt/HCIHandler.cpp:520 sendCommand: HCIHandler::sendCommand: HCIComm write error, req HCICommand[opcode=0x1002 READ_LOCAL_COMMANDS, param[size 0, data ], tsz 4] - HCIHandler[dev_id 1, BTMode NONE, open 1, scan NONE, ext[init 0, scan 0, conn 0], ring[entries 0]]; last errno 100 Network is down +[ 2] Warning @ /src/direct_bt/HCIHandler.cpp:1459 processCommandComplete: HCIHandler::processCommandComplete Send failed: Status 0xFE (INTERNAL_FAILURE), errno 100 Network is down: res nullptr, req HCICommand[opcode=0x1002 READ_LOCAL_COMMANDS, param[size 0, data ], tsz 4] - HCIHandler[dev_id 1, BTMode NONE, open 1, scan NONE, ext[init 0, scan 0, conn 0], ring[entries 0]] +[ 2] ****** SETTINGS_INITIAL: [] -> [BONDABLE, SSP, BREDR, LE, SECURE_CONN], changed [] +[ 2] Status BTAdapter: +[ 2] Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] Adapter not powered (2): Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] ****** Adapter ADDED__: Ignored Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] ****** Adapter Features: [] +[ 2] ****** SETTINGS_INITIAL: [] -> [BONDABLE, SSP, BREDR, LE, SECURE_CONN], changed [] +[ 2] Status BTAdapter: +[ 2] Adapter[BTMode DUAL, [00:1A:7D:DA:71:08, BDADDR_LE_PUBLIC], 'CSR8510 A10', id 1, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] Adapter not powered (2): Adapter[BTMode DUAL, [00:1A:7D:DA:71:08, BDADDR_LE_PUBLIC], 'CSR8510 A10', id 1, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] ****** Adapter ADDED__: Ignored Adapter[BTMode DUAL, [00:1A:7D:DA:71:08, BDADDR_LE_PUBLIC], 'CSR8510 A10', id 1, curSettings[BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 0, conn 0], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 2] ****** Adapter Features: [] +[ 12,330] ****** SETTINGS_CHANGED: [BONDABLE, SSP, BREDR, LE, SECURE_CONN] -> [POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], changed [POWERED] +[ 12,330] Status BTAdapter: +[ 12,330] Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 12,336] ****** DISCOVERING: meta LE, changed[LE, enabled 1, keepAlive 1]: Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native LE, meta LE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 12,336] ****** Start discovery (powered-on) result: SUCCESS +[ 12,537] ****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' Device[[74:43:E0:00:EB:12, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name[''], age[total 1, ldisc 1, lup 1]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -93, tx-power 127, appearance 0xfd6f (Unknown AppearanceCat 0xfd6f), MSD[null], JavaAnon[null]] + 0000fd6f-0000-1000-8000-00805f9b34fb, 2 bytes +[ 12,759] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -71, tx-power 0, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes +[ 12,760] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name['BLESmart_0000035728FFB2C14619'], age[total 2, ldisc 2, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -71, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes +[ 33,281] ****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' Device[[7B:7B:95:C5:2F:36, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -100, tx-power 127, appearance 0xfd6f (Unknown AppearanceCat 0xfd6f), MSD[null], JavaAnon[null]] + 0000fd6f-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,377] ****** FOUND__-1: NOP Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[null], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,378] ****** FOUND__-0: Connecting Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 1, ldisc 1, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,378] PERF: adapter-init -> FOUND__-0 52377 ms +[ 52,378] ****** Connecting Device: Start Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 1, ldisc 1, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] +[ 52,378] ****** Connecting Device: Unpair-Pre result: SUCCESS +[ 52,456] ****** DISCOVERING: meta NONE, changed[LE, enabled 0, keepAlive 0]: Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 52,456] ****** Connecting Device: stopDiscovery result SUCCESS +[ 52,456] ****** Connecting Device: No SecurityDetail for Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 79, ldisc 79, lup 78]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] +[ 52,456] Read SMPKeyBin: OK keys/bd_C0_26_DA_01_DA_B1_1-smpkey.bin: SMPKeyBin[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], sec ENC_ONLY, io NO_INPUT_NO_OUTPUT, Init[LTK[props [], enc_size 16, ediv 0x8f79, rand 0xa7a72569b5f81a5c, ltk 43b6c37b3b94625200ef3377964beb52, valid 1], CSRK[props [], csrk 1cbe232e1eebb3a195eac33137bdf732]], Resp[LTK[props [RESPONDER], enc_size 16, ediv 0x3c23, rand 0x1a4de360c7d4f09d, ltk abaaf21850fbfa7709f241bfd49574a7, valid 1], CSRK[props [RESPONDER], csrk 7dfa0dd991c98762474c4fe1124c1eca]], ver[0x5557, ok 1], size[113, calc 113, valid 1], 2021-08-30 05:27:30, valid 1], remaining 0 +[ 52,457] ****** Connecting Device: SMPKeyBin::readAndApply(..) result SUCCESS +[ 52,460] ****** Connecting Device: End result SUCCESS of Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 83, ldisc 83, lup 82]ms, connected[1/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] +[ 52,638] ****** CONNECTED: Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 261, ldisc 261, lup 0]ms, connected[1/1, handle 0x0e01, sec[lvl UNSET, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,638] ****** CONNECTED(2): Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 261, ldisc 261, lup 0]ms, connected[1/1, handle 0x0e01, sec[lvl UNSET, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,832] ****** PAIRING STATE: state COMPLETED, mode PRE_PAIRED, Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 455, ldisc 455, lup 194]ms, connected[1/1, handle 0x0e01, sec[lvl ENC_ONLY, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing PRE_PAIRED, state COMPLETED]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] +[ 52,952] ****** READY-0: Processing[1] Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 575, ldisc 575, lup 314]ms, connected[1/1, handle 0x0e01, sec[lvl ENC_ONLY, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing PRE_PAIRED, state COMPLETED]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 52,952] ****** Processing Ready Device: Start Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 575, ldisc 575, lup 314]ms, connected[1/1, handle 0x0e01, sec[lvl ENC_ONLY, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing PRE_PAIRED, state COMPLETED]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] +[ 52,952] Write SMPKeyBin: Not overwriting existing file keys/bd_C0_26_DA_01_DA_B1_1-smpkey.bin, SMPKeyBin[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], sec ENC_ONLY, io NO_INPUT_NO_OUTPUT, Init[], Resp[], ver[0x5557, ok 1], size[23, calc 23, valid 1], 2021-08-30 05:29:17, valid 1] +[ 52,954] ****** Connected LE PHY: status SUCCESS: RX [LE_1M], TX [LE_1M] +[ 52,954] ****** Processing Ready Device: GATT start: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC] +[ 52,954] - BTAdapter::SharedDevices : 1 elements +[ 52,954] - 1 / 1: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name 'TAIDOC TD1107' +[ 52,954] - BTAdapter::ConnectedDevices : 1 elements +[ 52,954] - 1 / 1: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name 'TAIDOC TD1107' +[ 52,954] - BTAdapter::DiscoveredDevices : 4 elements +[ 52,954] - 1 / 4: [74:43:E0:00:EB:12, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name '' +[ 52,954] - 2 / 4: [28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name 'BLESmart_0000035728FFB2C14619' +[ 52,954] - 3 / 4: [7B:7B:95:C5:2F:36, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name '' +[ 52,954] - 4 / 4: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name 'TAIDOC TD1107' +[ 52,954] - BTAdapter::StatusListener : 2 elements +[ 52,954] - 1 / 2: MyAdapterStatusListener[this 0x0000555fb4f35e50] +[ 52,954] - 2 / 2: TempAdapterStatusListener[this 0x00007fac64000c30] +[ 54,602] + + +[ 54,602] PERF: GATT primary-services completed +[ 54,602] PERF: adapter-init to processing-start 52951 ms, +PERF: get-gatt-services 1650 ms, +PERF: discovered to gatt-complete 2225 ms (connect 575 ms), +PERF: adapter-init to gatt-complete 54601 ms + +[ 54,602] GenericAccess: 'TAIDOC TD1107'[appearance 0x0000 (UNKNOWN), PrefConnectionParam[interval[80..160], slaveLatency 0, csTimeoutMul 1000]] + +[ 54,887] DeviceInformation: DeviceInfo[manufacturer 'Manufacturer Name', model 'Model Number', serial 'Serial Number', systemID 'size 8, capacity 8, 0000000000000000', revisions[firmware 'V1.2.29', hardware 'Hardware Revision', software 'V1.0.0'], pnpID[vendor_id[source 0x01, id 0x000d], product_id 0x0000, product_version 0x0110], regCertData 'size 14, capacity 14, fe006578706572696d656e74616c'] + +[ 54,887] [00] Service UUID 00001800-0000-1000-8000-00805f9b34fb +[ 54,887] [00] [type 0x1800, handle [0x0001..0x0007] - GENERIC_ACCESS, 3 characteristics] +[ 54,887] [00.00] Characteristic: UUID 00002a00-0000-1000-8000-00805f9b34fb +[ 54,887] [00.00] [handle 0x0002, props 0x02 [read], value[type 0x2a00, handle 0x0003, DEVICE_NAME, descr[ [type 0x2803, handle 0x0004, value[size 5, capacity 5, 020500012a] ], [type 0x2a01, handle 0x0005, value[size 2, capacity 2, 0000] ], ]], service[type 0x1800, handle[ 0x0002..0x0007 ], GENERIC_ACCESS, enabled[notify 0, indicate 0] ] ] +[ 54,917] [00.00] value: size 13, capacity 512, 544149444f4320544431313037 ('TAIDOC TD1107') +[ 54,917] [00.00.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 54,917] [00.00.00] [type 0x2803, handle 0x0004, value[size 5, capacity 5, 020500012a] ] +[ 54,917] [00.00.01] Descriptor: UUID 00002a01-0000-1000-8000-00805f9b34fb +[ 54,917] [00.00.01] [type 0x2a01, handle 0x0005, value[size 2, capacity 2, 0000] ] +[ 54,917] [00.00] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 54,917] +[ 54,917] [00.01] Characteristic: UUID 00002a01-0000-1000-8000-00805f9b34fb +[ 54,917] [00.01] [handle 0x0004, props 0x02 [read], value[type 0x2a01, handle 0x0005, APPEARANCE, descr[ [type 0x2803, handle 0x0006, value[size 5, capacity 5, 020700042a] ], [type 0x2a04, handle 0x0007, value[size 8, capacity 8, 5000a0000000e803] ], ]], service[type 0x1800, handle[ 0x0004..0x0007 ], GENERIC_ACCESS, enabled[notify 0, indicate 0] ] ] +[ 54,947] [00.01] value: size 2, capacity 512, 0000 ('') +[ 54,947] [00.01.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 54,947] [00.01.00] [type 0x2803, handle 0x0006, value[size 5, capacity 5, 020700042a] ] +[ 54,947] [00.01.01] Descriptor: UUID 00002a04-0000-1000-8000-00805f9b34fb +[ 54,947] [00.01.01] [type 0x2a04, handle 0x0007, value[size 8, capacity 8, 5000a0000000e803] ] +[ 54,947] [00.01] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 54,947] +[ 54,947] [00.02] Characteristic: UUID 00002a04-0000-1000-8000-00805f9b34fb +[ 54,947] [00.02] [handle 0x0006, props 0x02 [read], value[type 0x2a04, handle 0x0007, PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, descr[ ]], service[type 0x1800, handle[ 0x0006..0x0007 ], GENERIC_ACCESS, enabled[notify 0, indicate 0] ] ] +[ 54,977] [00.02] value: size 8, capacity 512, 5000a0000000e803 ('P') +[ 54,977] [00.02] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 54,977] +[ 54,977] +[ 54,977] [01] Service UUID 00001801-0000-1000-8000-00805f9b34fb +[ 54,977] [01] [type 0x1801, handle [0x0008..0x0008] - Unknown, 0 characteristics] +[ 54,977] +[ 54,977] [02] Service UUID 0000180a-0000-1000-8000-00805f9b34fb +[ 54,977] [02] [type 0x180a, handle [0x0009..0x001b] - DEVICE_INFORMATION, 9 characteristics] +[ 54,977] [02.00] Characteristic: UUID 00002a23-0000-1000-8000-00805f9b34fb +[ 54,977] [02.00] [handle 0x000a, props 0x02 [read], value[type 0x2a23, handle 0x000b, SYSTEM_ID, descr[ [type 0x2803, handle 0x000c, value[size 5, capacity 5, 020d00242a] ], [type 0x2a24, handle 0x000d, value[size 12, capacity 12, 4d6f64656c204e756d626572] ], ]], service[type 0x180a, handle[ 0x000a..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,022] [02.00] value: size 8, capacity 512, 0000000000000000 ('') +[ 55,022] [02.00.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,022] [02.00.00] [type 0x2803, handle 0x000c, value[size 5, capacity 5, 020d00242a] ] +[ 55,022] [02.00.01] Descriptor: UUID 00002a24-0000-1000-8000-00805f9b34fb +[ 55,022] [02.00.01] [type 0x2a24, handle 0x000d, value[size 12, capacity 12, 4d6f64656c204e756d626572] ] +[ 55,022] [02.00] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,022] +[ 55,022] [02.01] Characteristic: UUID 00002a24-0000-1000-8000-00805f9b34fb +[ 55,022] [02.01] [handle 0x000c, props 0x02 [read], value[type 0x2a24, handle 0x000d, MODEL_NUMBER_STRING, descr[ [type 0x2803, handle 0x000e, value[size 5, capacity 5, 020f00252a] ], [type 0x2a25, handle 0x000f, value[size 13, capacity 13, 53657269616c204e756d626572] ], ]], service[type 0x180a, handle[ 0x000c..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,052] [02.01] value: size 12, capacity 512, 4d6f64656c204e756d626572 ('Model Number') +[ 55,052] [02.01.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,052] [02.01.00] [type 0x2803, handle 0x000e, value[size 5, capacity 5, 020f00252a] ] +[ 55,052] [02.01.01] Descriptor: UUID 00002a25-0000-1000-8000-00805f9b34fb +[ 55,052] [02.01.01] [type 0x2a25, handle 0x000f, value[size 13, capacity 13, 53657269616c204e756d626572] ] +[ 55,052] [02.01] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,052] +[ 55,052] [02.02] Characteristic: UUID 00002a25-0000-1000-8000-00805f9b34fb +[ 55,052] [02.02] [handle 0x000e, props 0x02 [read], value[type 0x2a25, handle 0x000f, SERIAL_NUMBER_STRING, descr[ [type 0x2803, handle 0x0010, value[size 5, capacity 5, 021100262a] ], [type 0x2a26, handle 0x0011, value[size 7, capacity 7, 56312e322e3239] ], ]], service[type 0x180a, handle[ 0x000e..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,082] [02.02] value: size 13, capacity 512, 53657269616c204e756d626572 ('Serial Number') +[ 55,082] [02.02.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,082] [02.02.00] [type 0x2803, handle 0x0010, value[size 5, capacity 5, 021100262a] ] +[ 55,082] [02.02.01] Descriptor: UUID 00002a26-0000-1000-8000-00805f9b34fb +[ 55,082] [02.02.01] [type 0x2a26, handle 0x0011, value[size 7, capacity 7, 56312e322e3239] ] +[ 55,082] [02.02] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,082] +[ 55,082] [02.03] Characteristic: UUID 00002a26-0000-1000-8000-00805f9b34fb +[ 55,082] [02.03] [handle 0x0010, props 0x02 [read], value[type 0x2a26, handle 0x0011, FIRMWARE_REVISION_STRING, descr[ [type 0x2803, handle 0x0012, value[size 5, capacity 5, 021300272a] ], [type 0x2a27, handle 0x0013, value[size 17, capacity 17, 4861726477617265205265766973696f6e] ], ]], service[type 0x180a, handle[ 0x0010..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,127] [02.03] value: size 7, capacity 512, 56312e322e3239 ('V1.2.29') +[ 55,127] [02.03.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,127] [02.03.00] [type 0x2803, handle 0x0012, value[size 5, capacity 5, 021300272a] ] +[ 55,127] [02.03.01] Descriptor: UUID 00002a27-0000-1000-8000-00805f9b34fb +[ 55,127] [02.03.01] [type 0x2a27, handle 0x0013, value[size 17, capacity 17, 4861726477617265205265766973696f6e] ] +[ 55,127] [02.03] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,127] +[ 55,127] [02.04] Characteristic: UUID 00002a27-0000-1000-8000-00805f9b34fb +[ 55,127] [02.04] [handle 0x0012, props 0x02 [read], value[type 0x2a27, handle 0x0013, HARDWARE_REVISION_STRING, descr[ [type 0x2803, handle 0x0014, value[size 5, capacity 5, 021500282a] ], [type 0x2a28, handle 0x0015, value[size 6, capacity 6, 56312e302e30] ], ]], service[type 0x180a, handle[ 0x0012..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,157] [02.04] value: size 17, capacity 512, 4861726477617265205265766973696f6e ('Hardware Revision') +[ 55,157] [02.04.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,157] [02.04.00] [type 0x2803, handle 0x0014, value[size 5, capacity 5, 021500282a] ] +[ 55,157] [02.04.01] Descriptor: UUID 00002a28-0000-1000-8000-00805f9b34fb +[ 55,157] [02.04.01] [type 0x2a28, handle 0x0015, value[size 6, capacity 6, 56312e302e30] ] +[ 55,157] [02.04] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,157] +[ 55,157] [02.05] Characteristic: UUID 00002a28-0000-1000-8000-00805f9b34fb +[ 55,157] [02.05] [handle 0x0014, props 0x02 [read], value[type 0x2a28, handle 0x0015, SOFTWARE_REVISION_STRING, descr[ [type 0x2803, handle 0x0016, value[size 5, capacity 5, 021700292a] ], [type 0x2a29, handle 0x0017, value[size 17, capacity 17, 4d616e756661637475726572204e616d65] ], ]], service[type 0x180a, handle[ 0x0014..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,187] [02.05] value: size 6, capacity 512, 56312e302e30 ('V1.0.0') +[ 55,187] [02.05.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,187] [02.05.00] [type 0x2803, handle 0x0016, value[size 5, capacity 5, 021700292a] ] +[ 55,187] [02.05.01] Descriptor: UUID 00002a29-0000-1000-8000-00805f9b34fb +[ 55,187] [02.05.01] [type 0x2a29, handle 0x0017, value[size 17, capacity 17, 4d616e756661637475726572204e616d65] ] +[ 55,187] [02.05] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,187] +[ 55,187] [02.06] Characteristic: UUID 00002a29-0000-1000-8000-00805f9b34fb +[ 55,187] [02.06] [handle 0x0016, props 0x02 [read], value[type 0x2a29, handle 0x0017, MANUFACTURER_NAME_STRING, descr[ [type 0x2803, handle 0x0018, value[size 5, capacity 5, 0219002a2a] ], [type 0x2a2a, handle 0x0019, value[size 14, capacity 14, fe006578706572696d656e74616c] ], ]], service[type 0x180a, handle[ 0x0016..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,217] [02.06] value: size 17, capacity 512, 4d616e756661637475726572204e616d65 ('Manufacturer Name') +[ 55,217] [02.06.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,217] [02.06.00] [type 0x2803, handle 0x0018, value[size 5, capacity 5, 0219002a2a] ] +[ 55,217] [02.06.01] Descriptor: UUID 00002a2a-0000-1000-8000-00805f9b34fb +[ 55,217] [02.06.01] [type 0x2a2a, handle 0x0019, value[size 14, capacity 14, fe006578706572696d656e74616c] ] +[ 55,217] [02.06] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,217] +[ 55,217] [02.07] Characteristic: UUID 00002a2a-0000-1000-8000-00805f9b34fb +[ 55,217] [02.07] [handle 0x0018, props 0x02 [read], value[type 0x2a2a, handle 0x0019, REGULATORY_CERT_DATA_LIST, descr[ [type 0x2803, handle 0x001a, value[size 5, capacity 5, 021b00502a] ], [type 0x2a50, handle 0x001b, value[size 7, capacity 7, 010d0000001001] ], ]], service[type 0x180a, handle[ 0x0018..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,262] [02.07] value: size 14, capacity 512, fe006578706572696d656e74616c ('') +[ 55,262] [02.07.00] Descriptor: UUID 00002803-0000-1000-8000-00805f9b34fb +[ 55,262] [02.07.00] [type 0x2803, handle 0x001a, value[size 5, capacity 5, 021b00502a] ] +[ 55,262] [02.07.01] Descriptor: UUID 00002a50-0000-1000-8000-00805f9b34fb +[ 55,262] [02.07.01] [type 0x2a50, handle 0x001b, value[size 7, capacity 7, 010d0000001001] ] +[ 55,262] [02.07] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,262] +[ 55,262] [02.08] Characteristic: UUID 00002a50-0000-1000-8000-00805f9b34fb +[ 55,262] [02.08] [handle 0x001a, props 0x02 [read], value[type 0x2a50, handle 0x001b, PNP_ID, descr[ ]], service[type 0x180a, handle[ 0x001a..0x001b ], DEVICE_INFORMATION, enabled[notify 0, indicate 0] ] ] +[ 55,292] [02.08] value: size 7, capacity 512, 010d0000001001 ('
') +[ 55,292] [02.08] Characteristic-Listener: Notification(0), Indication(0): Added 0 +[ 55,292] +[ 55,292] +[ 55,292] [03] Service UUID 00001809-0000-1000-8000-00805f9b34fb +[ 55,292] [03] [type 0x1809, handle [0x001c..0x001f] - HEALTH_THERMOMETER, 1 characteristics] +[ 55,292] [03.00] Characteristic: UUID 00002a1c-0000-1000-8000-00805f9b34fb +[ 55,292] [03.00] [handle 0x001d, props 0x20 [indicate], value[type 0x2a1c, handle 0x001e, TEMPERATURE_MEASUREMENT, descr[ [type 0x2902, handle 0x001f, value[size 2, capacity 2, 0200] ], ]], service[type 0x1809, handle[ 0x001d..0x001f ], HEALTH_THERMOMETER, enabled[notify 0, indicate 0] ] ] +[ 55,292] [03.00.00] Descriptor: UUID 00002902-0000-1000-8000-00805f9b34fb +[ 55,292] [03.00.00] [type 0x2902, handle 0x001f, value[size 2, capacity 2, 0200] ] +[ 55,322] [03.00] Characteristic-Listener: Notification(0), Indication(1): Added 1 +[ 55,322] +[ 55,322] +[ 55,322] [04] Service UUID 00001523-1212-efde-1523-785feabcd123 +[ 55,322] [04] [type 0x00001523-1212-efde-1523-785feabcd123, handle [0x0020..0xffff], 1 characteristics] +[ 55,322] [04.00] Characteristic: UUID 00001524-1212-efde-1523-785feabcd123 +[ 55,322] [04.00] [handle 0x0021, props 0x18 [write, notify], value[type 0x00001524-1212-efde-1523-785feabcd123, handle 0x0022, descr[ [type 0x2902, handle 0x0023, value[size 2, capacity 2, 0100] ], ]], service[type 0x00001523-1212-efde-1523-785feabcd123, handle[ 0x0021..0xffff ], enabled[notify 0, indicate 0] ] ] +[ 55,322] [04.00.00] Descriptor: UUID 00002902-0000-1000-8000-00805f9b34fb +[ 55,322] [04.00.00] [type 0x2902, handle 0x0023, value[size 2, capacity 2, 0100] ] +[ 55,352] [04.00] Characteristic-Listener: Notification(1), Indication(0): Added 1 +[ 55,352] +[ 55,352] +[ 55,412] **[03.00] Characteristic-Indication: UUID 00002a1c-0000-1000-8000-00805f9b34fb, td 0, confirmed 1 ****** +[ 55,412] **[03.00] Characteristic: [handle 0x001d, props 0x20 [indicate], value[type 0x2a1c, handle 0x001e, TEMPERATURE_MEASUREMENT, descr[ [type 0x2902, handle 0x001f, value[size 2, capacity 2, 0200] ], ]], service[type 0x1809, handle[ 0x001d..0x001f ], HEALTH_THERMOMETER, enabled[notify 0, indicate 1] ] ] ****** +[ 55,412] **[03.00] Value T: 35.600002 C, 2021-08-30 07:28:00, type 0 ****** +[ 55,412] **[03.00] Value R: size 13, ro: 06640100ffe507081e071c0000 ****** +[ 56,352] ****** Processing Ready Device: End-1: Success 1 on Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 3975, ldisc 3975, lup 1750]ms, connected[1/1, handle 0x0e01, sec[lvl ENC_ONLY, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing PRE_PAIRED, state COMPLETED]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]]; devInProc 1 +[ 56,357] ****** DISCOVERING: meta LE, changed[LE, enabled 1, keepAlive 1]: Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native LE, meta LE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 56,357] ****** Start discovery (post-processing-1) result: SUCCESS +[ 56,357] - BTAdapter::SharedDevices : 1 elements +[ 56,357] - 1 / 1: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name 'TAIDOC TD1107' +[ 56,357] - BTAdapter::ConnectedDevices : 1 elements +[ 56,357] - 1 / 1: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name 'TAIDOC TD1107' +[ 56,357] - BTAdapter::DiscoveredDevices : 0 elements +[ 56,357] - BTAdapter::StatusListener : 2 elements +[ 56,357] - 1 / 2: MyAdapterStatusListener[this 0x0000555fb4f35e50] +[ 56,357] - 2 / 2: TempAdapterStatusListener[this 0x00007fac64000c30] +[ 56,357] ****** Processing Ready Device: End-2: Success 1 on Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 3980, ldisc 3980, lup 1755]ms, connected[1/1, handle 0x0e01, sec[lvl ENC_ONLY, io NO_INPUT_NO_OUTPUT, auto UNSET, pairing PRE_PAIRED, state COMPLETED]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]]; devInProc 0 +[ 56,357] ****** Processing Ready Device: MULTI_MEASUREMENTS left 7: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC] +[ 56,502] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -72, tx-power 0, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes +[ 56,504] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name['BLESmart_0000035728FFB2C14619'], age[total 2, ldisc 2, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -72, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes +[ 56,631] ****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' Device[[7B:7B:95:C5:2F:36, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -94, tx-power 127, appearance 0xfd6f (Unknown AppearanceCat 0xfd6f), MSD[null], JavaAnon[null]] + 0000fd6f-0000-1000-8000-00805f9b34fb, 2 bytes +[ 58,772] Interrupted @ /src/direct_bt/L2CAPComm.cpp:432 read: L2CAPComm::read: Error res -20; State[open 1, ioError 1], dd 8, [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], psm UNDEFINED, cid ATT; last errno 107 Transport endpoint is not connected +[ 58,772] ****** DISCONNECTED: Reason 0x8 (CONNECTION_TIMEOUT), old handle 0x0e01: Device[[C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC], name['TAIDOC TD1107'], age[total 6395, ldisc 6395, lup 4170]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -85, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[0 0], data[size 6, capacity 6, c026da01dab1]], JavaAnon[null]] + 00001809-0000-1000-8000-00805f9b34fb, 2 bytes + 0000180a-0000-1000-8000-00805f9b34fb, 2 bytes +[ 58,772] ****** Remove Device: removing: [C0:26:DA:01:DA:B1, BDADDR_LE_PUBLIC] +[ 58,842] ****** DISCOVERING: meta NONE, changed[LE, enabled 0, keepAlive 0]: Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native NONE, meta NONE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 58,849] ****** DISCOVERING: meta LE, changed[LE, enabled 1, keepAlive 1]: Adapter[BTMode DUAL, [DC:FB:48:00:90:19, BDADDR_LE_PUBLIC], '', id 0, curSettings[POWERED, BONDABLE, SSP, BREDR, LE, SECURE_CONN], scanType[native LE, meta LE], valid 1, hci_ext[scan 1, conn 1], open[mgmt, 1, hci 1], JavaAnon[null]] +[ 58,849] ****** Start discovery (post-remove-device) result: SUCCESS +[ 58,938] ****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' Device[[7B:7B:95:C5:2F:36, BDADDR_LE_RANDOM, random RESOLVABLE_PRIVAT], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -96, tx-power 127, appearance 0xfd6f (Unknown AppearanceCat 0xfd6f), MSD[null], JavaAnon[null]] + 0000fd6f-0000-1000-8000-00805f9b34fb, 2 bytes +[ 59,147] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name[''], age[total 0, ldisc 0, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -73, tx-power 0, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes +[ 59,149] ****** FOUND__-1: NOP Device[[28:FF:B2:C1:46:19, BDADDR_LE_PUBLIC], name['BLESmart_0000035728FFB2C14619'], age[total 2, ldisc 2, lup 0]ms, connected[0/0, handle 0x0000, sec[lvl UNSET, io UNSET, auto UNSET, pairing NONE, state NONE]], rssi -73, tx-power 127, appearance 0x0000 (UNKNOWN), MSD[company[526 526], data[size 5, capacity 5, 0100170017]], JavaAnon[null]] + 00001810-0000-1000-8000-00805f9b34fb, 2 bytes diff --git a/doc/log/dbt_scanner10-bt5_intel.txt b/doc/log/dbt_scanner10-bt5_intel.txt new file mode 100644 index 00000000..e32fad8d --- /dev/null +++ b/doc/log/dbt_scanner10-bt5_intel.txt @@ -0,0 +1,14 @@ +Machine: +- GNU/Linux x86_64 (amd64) +- Debian 11 +- Linux risa 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 GNU/Linux + +Bluetooth Adapter: +- Intel AX200 Bluetooth 5.0 (Wi-Fi 6 802.11ax (2.4Gbps) + BT 5.0) + +[ 16.767652] Bluetooth: Core ver 2.22 +.. +[ 21.127370] Bluetooth: hci0: Found Intel DDC parameters: intel/ibt-20-1-3.ddc +[ 21.136102] Bluetooth: hci0: Applying Intel DDC parameters completed +[ 21.147097] Bluetooth: hci0: Firmware revision 0.0 build 121 week 7 2021 + diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 4fff044f..4ba38de1 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -387,6 +387,11 @@ static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device) { } const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName()); + if( nullptr != sec ) { + fprintf_td(stderr, "****** Connecting Device: Found SecurityDetail %s for %s\n", sec->toString().c_str(), device->toString().c_str()); + } else { + fprintf_td(stderr, "****** Connecting Device: No SecurityDetail for %s\n", device->toString().c_str()); + } const BTSecurityLevel req_sec_level = nullptr != sec ? sec->getSecLevel() : BTSecurityLevel::UNSET; HCIStatusCode res = SMPKeyBin::readAndApply(KEY_PATH, *device, req_sec_level, true /* verbose */); fprintf_td(stderr, "****** Connecting Device: SMPKeyBin::readAndApply(..) result %s\n", to_string(res).c_str()); @@ -403,7 +408,6 @@ static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY (%s) -> set OK %d\n", sec->toString().c_str(), r); } } else { - fprintf_td(stderr, "****** Connecting Device: No SecurityDetail for %s\n", device->getAddressAndType().toString().c_str()); bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY ); fprintf_td(stderr, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK %d\n", r); } @@ -431,6 +435,13 @@ static void processReadyDevice(std::shared_ptr<BTDevice> device) { bool success = false; + { + LE_PHYs resRx, resTx; + HCIStatusCode res = device->getConnectedLE_PHY(resRx, resTx); + fprintf_td(stderr, "****** Connected LE PHY: status %s: RX %s, TX %s\n", + to_string(res).c_str(), to_string(resRx).c_str(), to_string(resTx).c_str()); + } + // // GATT Service Processing // @@ -633,16 +644,17 @@ static bool startDiscovery(BTAdapter *a, std::string msg) { } static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { - if( !adapter->isPowered() ) { // should have been covered above - fprintf_td(stderr, "Adapter not powered (2): %s\n", adapter->toString().c_str()); - return false; - } + // Even if adapter is not yet powered, listen to it and act when it gets powered-on adapter->addStatusListener(std::shared_ptr<AdapterStatusListener>(new MyAdapterStatusListener())); - // Flush discovered devices after registering our status listener. // This avoids discovered devices before we have registered! adapter->removeDiscoveredDevices(); + if( !adapter->isPowered() ) { // should have been covered above + fprintf_td(stderr, "Adapter not powered (2): %s\n", adapter->toString().c_str()); + return false; + } + if( USE_WHITELIST ) { for (auto it = WHITELIST.begin(); it != WHITELIST.end(); ++it) { bool res = adapter->addDeviceToWhitelist(*it, HCIWhitelistConnectType::HCI_AUTO_CONN_ALWAYS); @@ -663,6 +675,7 @@ static bool myChangedAdapterSetFunc(const bool added, std::shared_ptr<BTAdapter> } else { fprintf_td(stderr, "****** Adapter ADDED__: Ignored %s\n", adapter->toString().c_str()); } + fprintf_td(stderr, "****** Adapter Features: %s\n", direct_bt::to_string(adapter->getLEFeatures()).c_str()); } else { fprintf_td(stderr, "****** Adapter REMOVED: %s\n", adapter->toString().c_str()); } diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 14ffee1e..8d022ca2 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -351,7 +351,7 @@ public class DBTScanner10 { @Override public String toString() { - return "AdapterStatusListener[user, device "+device.getAddressAndType().toString()+"]"; + return "TempAdapterStatusListener[user, device "+device.getAddressAndType().toString()+"]"; } }); { @@ -365,6 +365,11 @@ public class DBTScanner10 { } final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName()); + if( null != sec ) { + BTUtils.println(System.err, "****** Connecting Device: Found SecurityDetail "+sec.toString()+" for "+device.toString()); + } else { + BTUtils.println(System.err, "****** Connecting Device: No SecurityDetail for "+device.toString()); + } final BTSecurityLevel req_sec_level = null != sec ? sec.getSecLevel() : BTSecurityLevel.UNSET; HCIStatusCode res = SMPKeyBin.readAndApply(KEY_PATH, device, req_sec_level, true /* verbose */); BTUtils.fprintf_td(System.err, "****** Connecting Device: SMPKeyBin::readAndApply(..) result %s\n", res.toString()); @@ -381,7 +386,6 @@ public class DBTScanner10 { BTUtils.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+") -> set OK "+r); } } else { - BTUtils.println(System.err, "****** Connecting Device: No SecurityDetail for "+device.getAddressAndType()); final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY ); BTUtils.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r); } @@ -641,12 +645,17 @@ public class DBTScanner10 { } private boolean initAdapter(final BTAdapter adapter) { + // Even if adapter is not yet powered, listen to it and act when it gets powered-on + adapter.addStatusListener(statusListener); + // Flush discovered devices after registering our status listener. + // This avoids discovered devices before we have registered! + adapter.removeDiscoveredDevices(); + if( !adapter.isPowered() ) { // should have been covered above BTUtils.println(System.err, "Adapter not powered (2): "+adapter.toString()); return false; } - adapter.addStatusListener(statusListener); adapter.enableDiscoverableNotifications(new BooleanNotification("Discoverable", timestamp_t0)); adapter.enableDiscoveringNotifications(new BooleanNotification("Discovering", timestamp_t0)); @@ -655,10 +664,6 @@ public class DBTScanner10 { adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0)); - // Flush discovered devices after registering our status listener. - // This avoids discovered devices before we have registered! - adapter.removeDiscoveredDevices(); - if( USE_WHITELIST ) { for(final Iterator<BDAddressAndType> wliter = whitelist.iterator(); wliter.hasNext(); ) { final BDAddressAndType addr = wliter.next(); diff --git a/scripts/run-dbt_scanner10.sh b/scripts/run-dbt_scanner10.sh index 421ae886..8598677d 100755 --- a/scripts/run-dbt_scanner10.sh +++ b/scripts/run-dbt_scanner10.sh @@ -40,6 +40,8 @@ # To do a BT adapter removal/add via software, assuming the device is '1-4' (Bus 1.Port 4): # echo '1-4' > /sys/bus/usb/drivers/usb/unbind # echo '1-4' > /sys/bus/usb/drivers/usb/bind +# To retrieve Bus and Port: +# lsusb -t # # Non root (we use the capsh solution per default): # Debian 11: Package libcap2-bin, version 1:2.44-1, binaries: /sbin/setcap /sbin/getcap diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index e3e5b729..fd460a2f 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -123,6 +123,31 @@ std::shared_ptr<BTDevice> BTAdapter::findConnectedDevice (const EUI48 & address, // ************************************************* // ************************************************* +bool BTAdapter::updateDataFromHCI() noexcept { + HCILocalVersion version; + HCIStatusCode status = hci.getLocalVersion(version); + if( HCIStatusCode::SUCCESS != status ) { + ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: POWERED, LocalVersion failed %s - %s", + dev_id, to_string(status).c_str(), adapterInfo.toString().c_str()); + return false; + } + LE_Features le_ll_feats; + if( HCIStatusCode::SUCCESS != hci.le_read_local_features(le_ll_feats) ) { + ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: le_read_local_features failed %s - %s", + dev_id, to_string(status).c_str(), adapterInfo.toString().c_str()); + return false; + } + le_features = le_ll_feats; + hci_uses_ext_scan = hci.use_ext_scan(); + hci_uses_ext_conn = hci.use_ext_conn(); + + WORDY_PRINT("BTAdapter::updateDataFromHCI: Adapter[%d]: POWERED, %s - %s, hci_ext[scan %d, conn %d], features: %s", + dev_id, version.toString().c_str(), adapterInfo.toString().c_str(), + hci_uses_ext_scan, hci_uses_ext_conn, + direct_bt::to_string(le_ll_feats).c_str()); + return true; +} + bool BTAdapter::validateDevInfo() noexcept { bool ok = false; currentMetaScanType = ScanType::NONE; @@ -147,19 +172,13 @@ bool BTAdapter::validateDevInfo() noexcept { hci.setBTMode(btMode); if( adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED) ) { - HCILocalVersion version; - HCIStatusCode status = hci.getLocalVersion(version); - if( HCIStatusCode::SUCCESS != status ) { - ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: POWERED, LocalVersion failed %s - %s", - dev_id, to_string(status).c_str(), adapterInfo.toString().c_str()); + if( !updateDataFromHCI() ) { return false; - } else { - WORDY_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: POWERED, %s - %s", - dev_id, version.toString().c_str(), adapterInfo.toString().c_str()); } } else { WORDY_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: Not POWERED: %s", dev_id, adapterInfo.toString().c_str()); } + ok = true; ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDiscoveringMgmt)) && ok; ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_SETTINGS, jau::bindMemberFunc(this, &BTAdapter::mgmtEvNewSettingsMgmt)) && ok; @@ -207,6 +226,7 @@ BTAdapter::BTAdapter(const BTAdapter::ctor_cookie& cc, BTManager& mgmt_, const A debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)), mgmt( mgmt_ ), adapterInfo( adapterInfo_ ), + le_features( LE_Features::NONE ), visibleAddressAndType( adapterInfo_.addressAndType ), dev_id( adapterInfo.dev_id ), hci( dev_id ) @@ -285,7 +305,7 @@ void BTAdapter::poweredOff() noexcept { currentMetaScanType = ScanType::NONE; // ensure all hci states are reset. - hci.clearAllStates(); + hci.resetAllStates(false); unlockConnectAny(); @@ -939,7 +959,9 @@ std::string BTAdapter::toString(bool includeDiscoveredDevices) const noexcept { ", '"+getName()+"', id "+std::to_string(dev_id)+ ", curSettings"+to_string(adapterInfo.getCurrentSettingMask())+ ", scanType[native "+to_string(hci.getCurrentScanType())+", meta "+to_string(currentMetaScanType)+"]" - ", valid "+std::to_string(isValid())+", open[mgmt, "+std::to_string(mgmt.isOpen())+", hci "+std::to_string(hci.isOpen())+ + ", valid "+std::to_string(isValid())+ + ", hci_ext[scan "+std::to_string(hci_uses_ext_scan)+", conn "+std::to_string(hci_uses_ext_conn)+ + "], open[mgmt, "+std::to_string(mgmt.isOpen())+", hci "+std::to_string(hci.isOpen())+ "], "+javaObjectToString()+"]"); if( includeDiscoveredDevices ) { device_list_t devices = getDiscoveredDevices(); @@ -1108,7 +1130,8 @@ bool BTAdapter::mgmtEvNewSettingsMgmt(const MgmtEvent& e) noexcept { if( justPoweredOn ) { // Adapter has been powered on, ensure all hci states are reset. - hci.clearAllStates(); + hci.resetAllStates(true); + updateDataFromHCI(); } sendAdapterSettingsChanged(old_settings_, new_settings, changes, event.getTimestamp()); diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index 5f98d855..a8f7d005 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -53,7 +53,7 @@ BTDevice::BTDevice(const ctor_cookie& cc, BTAdapter & a, EInfoReport const & r) (void)cc; ts_last_discovery = ts_creation; hciConnHandle = 0; - le_features = LEFeatures::NONE; + le_features = LE_Features::NONE; isConnected = false; allowDisconnect = false; clearSMPStates(false /* connected */); @@ -561,9 +561,9 @@ void BTDevice::notifyConnected(std::shared_ptr<BTDevice> sthis, const uint16_t h (void)sthis; // not used yet } -void BTDevice::notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LEFeatures features) noexcept { - DBG_PRINT("BTDevice::notifyLEFeatures: LE_Encryption %d, %s", - isLEFeaturesBitSet(features, LEFeatures::LE_Encryption), toString().c_str()); +void BTDevice::notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LE_Features features) noexcept { + DBG_PRINT("BTDevice::notifyLEFeatures: %s, %s", + direct_bt::to_string(features).c_str(), toString().c_str()); le_features = features; if( addressAndType.isLEAddress() && !l2cap_att.isOpen() ) { @@ -585,7 +585,7 @@ void BTDevice::processL2CAPSetup(std::shared_ptr<BTDevice> sthis) { const SMPIOCapability io_cap_conn = pairing_data.ioCap_conn; BTSecurityLevel sec_level { BTSecurityLevel::UNSET }; - const bool responderLikesEncryption = pairing_data.res_requested_sec || isLEFeaturesBitSet(le_features, LEFeatures::LE_Encryption); + const bool responderLikesEncryption = pairing_data.res_requested_sec || isLEFeaturesBitSet(le_features, LE_Features::LE_Encryption); if( BTSecurityLevel::UNSET != sec_level_user ) { sec_level = sec_level_user; } else if( responderLikesEncryption && SMPIOCapability::UNSET != io_cap_conn ) { @@ -1645,6 +1645,42 @@ int BTDevice::removeAllCharListener() noexcept { return gatt->removeAllCharListener(); } +HCIStatusCode BTDevice::getConnectedLE_PHY(LE_PHYs& resRx, LE_PHYs& resTx) noexcept { + const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor + + HCIStatusCode res = HCIStatusCode::SUCCESS; + HCIHandler &hci = adapter.getHCI(); + + if( !isConnected ) { // should not happen + res = HCIStatusCode::DISCONNECTED; + goto errout; + } + + if( 0 == hciConnHandle ) { + res = HCIStatusCode::UNSPECIFIED_ERROR; + goto errout; + } + + if( !adapter.isPowered() ) { + res = HCIStatusCode::NOT_POWERED; // powered-off + goto errout; + } + + res = hci.le_read_phy(hciConnHandle.load(), addressAndType, resRx, resTx); + if( HCIStatusCode::SUCCESS == res ) { + DBG_PRINT("BTDevice::getConnectedLE_PHY: status %s: RX %s, TX %s - %s", + to_string(res).c_str(), + to_string(resRx).c_str(), to_string(resTx).c_str(), toString().c_str()); + return res; + } + +errout: + ERR_PRINT("BTDevice::getConnectedLE_PHY: status %s: RX %s, TX %s - %s", + to_string(res).c_str(), + to_string(resRx).c_str(), to_string(resTx).c_str(), toString().c_str()); + return res; +} + void BTDevice::notifyDisconnected() noexcept { // coming from disconnect callback, ensure cleaning up! DBG_PRINT("BTDevice::notifyDisconnected: handle %s -> zero, %s", diff --git a/src/direct_bt/BTTypes0.cpp b/src/direct_bt/BTTypes0.cpp index 303b0ec3..dec45c53 100644 --- a/src/direct_bt/BTTypes0.cpp +++ b/src/direct_bt/BTTypes0.cpp @@ -370,6 +370,111 @@ BTMode direct_bt::to_BTMode(const std::string & value) noexcept { return BTMode::NONE; } +// ************************************************* +// ************************************************* +// ************************************************* + +#define LEFEATURES_ENUM(X) \ + X(LE_Features,NONE) \ + X(LE_Features,LE_Encryption) \ + X(LE_Features,Conn_Param_Req_Proc) \ + X(LE_Features,Ext_Rej_Ind) \ + X(LE_Features,SlaveInit_Feat_Exchg) \ + X(LE_Features,LE_Ping) \ + X(LE_Features,LE_Data_Pkt_Len_Ext) \ + X(LE_Features,LL_Privacy) \ + X(LE_Features,Ext_Scan_Filter_Pol) \ + X(LE_Features,LE_2M_PHY) \ + X(LE_Features,Stable_Mod_Idx_Tx) \ + X(LE_Features,Stable_Mod_Idx_Rx) \ + X(LE_Features,LE_Coded_PHY) \ + X(LE_Features,LE_Ext_Adv) \ + X(LE_Features,LE_Per_Adv) \ + X(LE_Features,Chan_Sel_Algo_2) \ + X(LE_Features,LE_Pwr_Cls_1) \ + X(LE_Features,Min_Num_Used_Chan_Proc) \ + X(LE_Features,Conn_CTE_Req) \ + X(LE_Features,Conn_CTE_Res) \ + X(LE_Features,ConnLess_CTE_Tx) \ + X(LE_Features,ConnLess_CTE_Rx) \ + X(LE_Features,AoD) \ + X(LE_Features,AoA) \ + X(LE_Features,Rx_Const_Tone_Ext) \ + X(LE_Features,Per_Adv_Sync_Tx_Sender) \ + X(LE_Features,Per_Adv_Sync_Tx_Rec) \ + X(LE_Features,Zzz_Clk_Acc_Upd) \ + X(LE_Features,Rem_Pub_Key_Val) \ + X(LE_Features,Conn_Iso_Stream_Master) \ + X(LE_Features,Conn_Iso_Stream_Slave) \ + X(LE_Features,Iso_Brdcst) \ + X(LE_Features,Sync_Rx) \ + X(LE_Features,Iso_Chan) \ + X(LE_Features,LE_Pwr_Ctrl_Req) \ + X(LE_Features,LE_Pwr_Chg_Ind) \ + X(LE_Features,LE_Path_Loss_Mon) + +static std::string _getLEFeaturesBitStr(const LE_Features bit) noexcept { + switch(bit) { + LEFEATURES_ENUM(CASE2_TO_STRING) + default: ; // fall through intended + } + return "??? "+jau::to_hexstring(number(bit)); +} + +std::string direct_bt::to_string(const LE_Features mask) noexcept { + const uint64_t one = 1; + bool has_pre = false; + std::string out("["); + for(int i=0; i<36; i++) { + const LE_Features settingBit = static_cast<LE_Features>( one << i ); + if( LE_Features::NONE != ( mask & settingBit ) ) { + if( has_pre ) { out.append(", "); } + out.append(_getLEFeaturesBitStr(settingBit)); + has_pre = true; + } + } + out.append("]"); + return out; +} + +// ************************************************* +// ************************************************* +// ************************************************* + +#define LE_PHYs_ENUM(X) \ + X(LE_PHYs,NONE) \ + X(LE_PHYs,LE_1M) \ + X(LE_PHYs,LE_2M) \ + X(LE_PHYs,LE_CODED) + +static std::string _getLE_PHYsBitStr(const LE_PHYs bit) noexcept { + switch(bit) { + LE_PHYs_ENUM(CASE2_TO_STRING) + default: ; // fall through intended + } + return "??? "+jau::to_hexstring(number(bit)); +} + +std::string direct_bt::to_string(const LE_PHYs mask) noexcept { + const uint8_t one = 1; + bool has_pre = false; + std::string out("["); + for(int i=0; i<4; i++) { + const LE_PHYs settingBit = static_cast<LE_PHYs>( one << i ); + if( LE_PHYs::NONE != ( mask & settingBit ) ) { + if( has_pre ) { out.append(", "); } + out.append(_getLE_PHYsBitStr(settingBit)); + has_pre = true; + } + } + out.append("]"); + return out; +} + +// ************************************************* +// ************************************************* +// ************************************************* + std::string direct_bt::to_string(const BTSecurityLevel v) noexcept { switch(v) { case BTSecurityLevel::UNSET: return "UNSET"; @@ -431,7 +536,13 @@ std::string direct_bt::to_string(const ScanType v) noexcept { X(ADV_SCAN_IND) \ X(ADV_NONCONN_IND) \ X(SCAN_RSP) \ - X(ADV_UNDEFINED) + X(ADV_IND2) \ + X(DIRECT_IND2) \ + X(SCAN_IND2) \ + X(NONCONN_IND2) \ + X(SCAN_RSP_to_ADV_IND) \ + X(SCAN_RSP_to_ADV_SCAN_IND) \ + X(UNDEFINED) #define AD_PDU_Type_CASE_TO_STRING(V) case AD_PDU_Type::V: return #V; @@ -443,6 +554,48 @@ std::string direct_bt::to_string(const AD_PDU_Type v) noexcept { return "Unknown AD_PDU_Type "+jau::to_hexstring(number(v)); } +// ************************************************* +// ************************************************* +// ************************************************* + +#define EAD_Event_Type_ENUM(X) \ + X(EAD_Event_Type,NONE) \ + X(EAD_Event_Type,CONN_ADV) \ + X(EAD_Event_Type,SCAN_ADV) \ + X(EAD_Event_Type,DIR_ADV) \ + X(EAD_Event_Type,SCAN_RSP) \ + X(EAD_Event_Type,LEGACY_PDU) \ + X(EAD_Event_Type,DATA_B0) \ + X(EAD_Event_Type,DATA_B1) + +static std::string _getEAD_Event_TypeBitStr(const EAD_Event_Type bit) noexcept { + switch(bit) { + EAD_Event_Type_ENUM(CASE2_TO_STRING) + default: ; // fall through intended + } + return "??? "+jau::to_hexstring(number(bit)); +} + +std::string direct_bt::to_string(const EAD_Event_Type mask) noexcept { + const uint16_t one = 1; + bool has_pre = false; + std::string out("["); + for(int i=0; i<8; i++) { + const EAD_Event_Type settingBit = static_cast<EAD_Event_Type>( one << i ); + if( EAD_Event_Type::NONE != ( mask & settingBit ) ) { + if( has_pre ) { out.append(", "); } + out.append(_getEAD_Event_TypeBitStr(settingBit)); + has_pre = true; + } + } + out.append("]"); + return out; +} + +// ************************************************* +// ************************************************* +// ************************************************* + #define L2CAP_CID_ENUM(X) \ X(UNDEFINED) \ X(SIGNALING) \ @@ -610,7 +763,7 @@ static std::string _getGAPFlagBitStr(const GAPFlags bit) noexcept { GAPFLAGS_ENUM(CASE2_TO_STRING) default: ; // fall through intended } - return "Unknown GAP_Flags Bit "+jau::to_hexstring(number(bit)); + return "??? "+jau::to_hexstring(number(bit)); } std::string direct_bt::to_string(const GAPFlags v) noexcept { @@ -636,6 +789,7 @@ std::string direct_bt::to_string(const GAPFlags v) noexcept { #define EIRDATATYPE_ENUM(X) \ X(EIRDataType,NONE) \ X(EIRDataType,EVT_TYPE) \ + X(EIRDataType,EXT_EVT_TYPE) \ X(EIRDataType,BDADDR_TYPE) \ X(EIRDataType,BDADDR) \ X(EIRDataType,FLAGS) \ @@ -656,7 +810,7 @@ static std::string _getEIRDataBitStr(const EIRDataType bit) noexcept { EIRDATATYPE_ENUM(CASE2_TO_STRING) default: ; // fall through intended } - return "Unknown EIRDataType Bit "+jau::to_hexstring(number(bit)); + return "??? "+jau::to_hexstring(number(bit)); } std::string direct_bt::to_string(const EIRDataType mask) noexcept { @@ -683,6 +837,7 @@ std::string direct_bt::to_string(EInfoReport::Source source) noexcept { switch (source) { case EInfoReport::Source::NA: return "N/A"; case EInfoReport::Source::AD: return "AD"; + case EInfoReport::Source::EAD: return "EAD"; case EInfoReport::Source::EIR: return "EIR"; case EInfoReport::Source::EIR_MGMT: return "EIR_MGMT"; } @@ -758,8 +913,8 @@ std::string EInfoReport::toString(const bool includeServices) const noexcept { std::string out("EInfoReport::"+to_string(source)+ "[address["+address.toString()+", "+to_string(getAddressType())+"/"+std::to_string(ad_address_type)+ "], name['"+name+"'/'"+name_short+"'], "+eirDataMaskToString()+ - ", evt-type "+to_string(evt_type)+ - ", flags"+to_string(flags)+ + ", type[evt "+to_string(evt_type)+", ead "+to_string(ead_type)+ + "], flags"+to_string(flags)+ ", rssi "+std::to_string(rssi)+ ", tx-power "+std::to_string(tx_power)+ ", dev-class "+jau::to_hexstring(device_class)+ @@ -944,6 +1099,8 @@ int EInfoReport::read_data(uint8_t const * data, uint8_t const data_length) noex return count; } +// #define AD_DEBUG 1 + jau::darray<std::unique_ptr<EInfoReport>> EInfoReport::read_ad_reports(uint8_t const * data, jau::nsize_t const data_length) noexcept { jau::nsize_t const num_reports = (jau::nsize_t) data[0]; jau::darray<std::unique_ptr<EInfoReport>> ad_reports; @@ -955,7 +1112,7 @@ jau::darray<std::unique_ptr<EInfoReport>> EInfoReport::read_ad_reports(uint8_t c uint8_t const *limes = data + data_length; uint8_t const *i_octets = data + 1; uint8_t ad_data_len[0x19]; - const jau::nsize_t segment_count = 6; + jau::nsize_t segment_count = 5 * num_reports; // excluding data segment jau::nsize_t read_segments = 0; jau::nsize_t i; const uint64_t timestamp = jau::getCurrentMilliseconds(); @@ -980,9 +1137,104 @@ jau::darray<std::unique_ptr<EInfoReport>> EInfoReport::read_ad_reports(uint8_t c ad_data_len[i] = *i_octets++; read_segments++; } - for(i = 0; i < num_reports && i_octets + ad_data_len[i] < limes; i++) { - ad_reports[i]->read_data(i_octets, ad_data_len[i]); - i_octets += ad_data_len[i]; + for(i = 0; i < num_reports; i++) { // adjust segement_count + if( 0 < ad_data_len[i] ) { + segment_count++; + } + } + for(i = 0; i < num_reports && i_octets + ad_data_len[i] <= limes; i++) { + if( 0 < ad_data_len[i] ) { + ad_reports[i]->read_data(i_octets, ad_data_len[i]); + i_octets += ad_data_len[i]; + read_segments++; + } + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + ad_reports[i]->setRSSI(*const_uint8_to_const_int8_ptr(i_octets)); + i_octets++; + read_segments++; + } + const jau::snsize_t bytes_left = static_cast<jau::snsize_t>(limes - i_octets); + + if( segment_count != read_segments || 0 > bytes_left ) { + if( 0 > bytes_left ) { + ERR_PRINT("EAD-Reports: Buffer overflow\n"); + } else { + WARN_PRINT("EAD-Reports: Incomplete data segments\n"); + } + WARN_PRINT("AD-Reports: %zu reports within %zu bytes: Segment read %zu < %zu, data-ptr %zd bytes to limes\n", + num_reports, data_length, read_segments, segment_count, bytes_left); + for(i=0; i<num_reports; i++) { + WARN_PRINT("EAD[%d]: ad_data_length %d, %s\n", (int)i, (int)ad_data_len[i], ad_reports[i]->toString(false).c_str()); + } + } +#if AD_DEBUG + else { + DBG_PRINT("AD-Reports: %zu reports within %zu bytes: Segment read %zu (%zu), data-ptr %zd bytes to limes\n", + num_reports, data_length, read_segments, segment_count, bytes_left); + for(i=0; i<num_reports; i++) { + DBG_PRINT("EAD[%d]: ad_data_length %d, %s\n", (int)i, (int)ad_data_len[i], ad_reports[i]->toString(false).c_str()); + } + } +#endif /* AD_DEBUG */ + return ad_reports; +} + +jau::darray<std::unique_ptr<EInfoReport>> EInfoReport::read_ext_ad_reports(uint8_t const * data, jau::nsize_t const data_length) noexcept { + jau::nsize_t const num_reports = (jau::nsize_t) data[0]; + jau::darray<std::unique_ptr<EInfoReport>> ad_reports; + + if( 0 == num_reports || num_reports > 0x19 ) { + DBG_PRINT("EAD-Reports: Invalid reports count: %d", num_reports); + return ad_reports; + } + uint8_t const *limes = data + data_length; + uint8_t const *i_octets = data + 1; + uint8_t ad_data_len[0x19]; + jau::nsize_t segment_count = 12 * num_reports; // excluding data segment + jau::nsize_t read_segments = 0; + jau::nsize_t i; + const uint64_t timestamp = jau::getCurrentMilliseconds(); + + for(i = 0; i < num_reports && i_octets < limes; i++) { + ad_reports.push_back( std::make_unique<EInfoReport>() ); + ad_reports[i]->setSource(Source::EAD); + ad_reports[i]->setTimestamp(timestamp); + const EAD_Event_Type ead_type_ = static_cast<EAD_Event_Type>(jau::get_uint16(i_octets, 0, true /* littleEndian */)); + ad_reports[i]->setExtEvtType(ead_type_); + i_octets+=2; + if( isEAD_Event_TypeSet(ead_type_, EAD_Event_Type::LEGACY_PDU) ) { + ad_reports[i]->setEvtType(static_cast<AD_PDU_Type>(number(ead_type_))); + } + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + ad_reports[i]->setADAddressType(*i_octets++); + read_segments++; + } + for(i = 0; i < num_reports && i_octets + 5 < limes; i++) { + ad_reports[i]->setAddress( *((EUI48 const *)i_octets) ); + i_octets += 6; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Primary_PHY: 0x01 = LE_1M, 0x03 = LE_CODED + i_octets++; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Sexondary_PHY: 0x00 None, 0x01 = LE_1M, 0x02 = LE_2M, 0x03 = LE_CODED + i_octets++; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Advertising_SID + i_octets++; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + ad_reports[i]->setTxPower(*const_uint8_to_const_int8_ptr(i_octets)); + i_octets++; read_segments++; } for(i = 0; i < num_reports && i_octets < limes; i++) { @@ -990,12 +1242,60 @@ jau::darray<std::unique_ptr<EInfoReport>> EInfoReport::read_ad_reports(uint8_t c i_octets++; read_segments++; } - const jau::nsize_t bytes_left = static_cast<jau::nsize_t>(limes - i_octets); + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Periodic_Advertising_Interval + i_octets+=2; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Direct_Address_Type + i_octets++; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + // Direct_Address + i_octets+=6; + read_segments++; + } + for(i = 0; i < num_reports && i_octets < limes; i++) { + ad_data_len[i] = *i_octets++; + read_segments++; + } + for(i = 0; i < num_reports; i++) { // adjust segement_count + if( 0 < ad_data_len[i] ) { + segment_count++; + } + } + for(i = 0; i < num_reports && i_octets + ad_data_len[i] <= limes; i++) { + if( 0 < ad_data_len[i] ) { + ad_reports[i]->read_data(i_octets, ad_data_len[i]); + i_octets += ad_data_len[i]; + read_segments++; + } + } + const jau::snsize_t bytes_left = static_cast<jau::snsize_t>(limes - i_octets); - if( segment_count != read_segments ) { - WARN_PRINT("AD-Reports: Incomplete %zu reports within %zu bytes: Segment read %zu < %zu, data-ptr %zu bytes to limes\n", + if( segment_count != read_segments || 0 > bytes_left ) { + if( 0 > bytes_left ) { + ERR_PRINT("EAD-Reports: Buffer overflow\n"); + } else { + WARN_PRINT("EAD-Reports: Incomplete data segments\n"); + } + WARN_PRINT("EAD-Reports: %zu reports within %zu bytes: Segment read %zu < %zu, data-ptr %zd bytes to limes\n", num_reports, data_length, read_segments, segment_count, bytes_left); + for(i=0; i<num_reports; i++) { + WARN_PRINT("EAD[%d]: ad_data_length %d, %s\n", (int)i, (int)ad_data_len[i], ad_reports[i]->toString(false).c_str()); + } + } +#if AD_DEBUG + else { + DBG_PRINT("EAD-Reports: %zu reports within %zu bytes: Segment read %zu (%zu), data-ptr %zd bytes to limes\n", + num_reports, data_length, read_segments, segment_count, bytes_left); + for(i=0; i<num_reports; i++) { + DBG_PRINT("EAD[%d]: ad_data_length %d, %s\n", (int)i, (int)ad_data_len[i], ad_reports[i]->toString(false).c_str()); + } } +#endif /* AD_DEBUG */ return ad_reports; } diff --git a/src/direct_bt/HCIHandler.cpp b/src/direct_bt/HCIHandler.cpp index 0ab57d21..1f09146c 100644 --- a/src/direct_bt/HCIHandler.cpp +++ b/src/direct_bt/HCIHandler.cpp @@ -165,18 +165,27 @@ HCIHandler::HCIConnectionRef HCIHandler::removeHCIConnection(jau::darray<HCIConn return nullptr; } -void HCIHandler::clearAllStates() noexcept { +void HCIHandler::resetAllStates(const bool powered_on) noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor connectionList.clear(); disconnectCmdList.clear(); currentScanType = ScanType::NONE; + zeroSupCommands(); + if( powered_on ) { + initSupCommands(); + } } MgmtEvent::Opcode HCIHandler::translate(HCIEventType evt, HCIMetaEventType met) noexcept { if( HCIEventType::LE_META == evt ) { switch( met ) { - case HCIMetaEventType::LE_CONN_COMPLETE: return MgmtEvent::Opcode::DEVICE_CONNECTED; - default: return MgmtEvent::Opcode::INVALID; + case HCIMetaEventType::LE_CONN_COMPLETE: + [[fallthrough]]; + case HCIMetaEventType::LE_EXT_CONN_COMPLETE: + return MgmtEvent::Opcode::DEVICE_CONNECTED; + + default: + return MgmtEvent::Opcode::INVALID; } } switch( evt ) { @@ -213,6 +222,25 @@ std::unique_ptr<MgmtEvent> HCIHandler::translate(HCIEvent& ev) noexcept { return std::make_unique<MgmtEvtDeviceConnectFailed>(dev_id, addressAndType, status); } } + case HCIMetaEventType::LE_EXT_CONN_COMPLETE: { + HCIStatusCode status; + const hci_ev_le_enh_conn_complete * ev_cc = getMetaReplyStruct<hci_ev_le_enh_conn_complete>(ev, mevt, &status); + if( nullptr == ev_cc ) { + ERR_PRINT("HCIHandler::translate(reader): LE_EXT_CONN_COMPLETE: Null reply-struct: %s - %s", + ev.toString().c_str(), toString().c_str()); + return nullptr; + } + const HCILEPeerAddressType hciAddrType = static_cast<HCILEPeerAddressType>(ev_cc->bdaddr_type); + const BDAddressAndType addressAndType(ev_cc->bdaddr, to_BDAddressType(hciAddrType)); + const uint16_t handle = jau::le_to_cpu(ev_cc->handle); + const HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, handle); + if( HCIStatusCode::SUCCESS == status ) { + return std::make_unique<MgmtEvtDeviceConnected>(dev_id, addressAndType, handle); + } else { + removeTrackerConnection(conn); + return std::make_unique<MgmtEvtDeviceConnectFailed>(dev_id, addressAndType, status); + } + } case HCIMetaEventType::LE_REMOTE_FEAT_COMPLETE: { HCIStatusCode status; const hci_ev_le_remote_feat_complete * ev_cc = getMetaReplyStruct<hci_ev_le_remote_feat_complete>(ev, mevt, &status); @@ -222,7 +250,7 @@ std::unique_ptr<MgmtEvent> HCIHandler::translate(HCIEvent& ev) noexcept { return nullptr; } const uint16_t handle = jau::le_to_cpu(ev_cc->handle); - const LEFeatures features = static_cast<LEFeatures>(jau::get_uint64(ev_cc->features, 0, true /* littleEndian */)); + const LE_Features features = static_cast<LE_Features>(jau::get_uint64(ev_cc->features, 0, true /* littleEndian */)); const HCIConnectionRef conn = findTrackerConnection(handle); if( nullptr == conn ) { WARN_PRINT("HCIHandler::translate(reader): LE_REMOTE_FEAT_COMPLETE: Not tracked conn_handle %s", @@ -364,8 +392,10 @@ void HCIHandler::hciReaderThreadImpl() noexcept { std::unique_ptr<HCIACLData> acldata = HCIACLData::getSpecialized(rbuffer.get_ptr(), len2); if( nullptr == acldata ) { // not valid acl-data ... - WARN_PRINT("HCIHandler-IO RECV Drop (non-acl-data) %s - %s", - jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str()); + if( jau::environment::get().verbose ) { + WARN_PRINT("HCIHandler-IO RECV Drop (non-acl-data) %s - %s", + jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str()); + } continue; } const uint8_t* l2cap_data = nullptr; // owned by acldata @@ -425,9 +455,19 @@ void HCIHandler::hciReaderThreadImpl() noexcept { } else if( event->isMetaEvent(HCIMetaEventType::LE_ADVERTISING_REPORT) ) { // issue callbacks for the translated AD events jau::darray<std::unique_ptr<EInfoReport>> eirlist = EInfoReport::read_ad_reports(event->getParam(), event->getParamSize()); - for(jau::nsize_t eircount = eirlist.size(); eircount>0; --eircount) { - const MgmtEvtDeviceFound e(dev_id, std::move( eirlist[0] ) ); - COND_PRINT(env.DEBUG_SCAN_AD_EIR, "HCIHandler-IO RECV (AD EIR) %s", e.getEIR()->toString().c_str()); + for(jau::nsize_t eircount = 0; eircount < eirlist.size(); ++eircount) { + const MgmtEvtDeviceFound e(dev_id, std::move( eirlist[eircount] ) ); + COND_PRINT(env.DEBUG_SCAN_AD_EIR, "HCIHandler-IO RECV (AD EIR) [%d] %s", + eircount, e.getEIR()->toString().c_str()); + sendMgmtEvent( e ); + } + } else if( event->isMetaEvent(HCIMetaEventType::LE_EXT_ADV_REPORT) ) { + // issue callbacks for the translated EAD events + jau::darray<std::unique_ptr<EInfoReport>> eirlist = EInfoReport::read_ext_ad_reports(event->getParam(), event->getParamSize()); + for(jau::nsize_t eircount = 0; eircount < eirlist.size(); ++eircount) { + const MgmtEvtDeviceFound e(dev_id, std::move( eirlist[eircount] ) ); + COND_PRINT(env.DEBUG_SCAN_AD_EIR, "HCIHandler-IO RECV (EAD EIR (ext)) [%d] %s", + eircount, e.getEIR()->toString().c_str()); sendMgmtEvent( e ); } } else { @@ -651,6 +691,9 @@ HCIHandler::HCIHandler(const uint16_t dev_id_, const BTMode btMode_) noexcept filter_set_metaev(HCIMetaEventType::LE_CONN_COMPLETE, mask); filter_set_metaev(HCIMetaEventType::LE_ADVERTISING_REPORT, mask); filter_set_metaev(HCIMetaEventType::LE_REMOTE_FEAT_COMPLETE, mask); + filter_set_metaev(HCIMetaEventType::LE_EXT_CONN_COMPLETE, mask); + filter_set_metaev(HCIMetaEventType::LE_EXT_ADV_REPORT, mask); + filter_set_metaev(HCIMetaEventType::LE_CHANNEL_SEL_ALGO, mask); #endif filter_put_metaevs(mask); } @@ -666,14 +709,23 @@ HCIHandler::HCIHandler(const uint16_t dev_id_, const BTMode btMode_) noexcept // filter_set_opcbit(HCIOpcodeBit::IO_CAPABILITY_REQ_NEG_REPLY, mask); filter_set_opcbit(HCIOpcodeBit::RESET, mask); filter_set_opcbit(HCIOpcodeBit::READ_LOCAL_VERSION, mask); + filter_set_opcbit(HCIOpcodeBit::READ_LOCAL_COMMANDS, mask); filter_set_opcbit(HCIOpcodeBit::LE_SET_SCAN_PARAM, mask); filter_set_opcbit(HCIOpcodeBit::LE_SET_SCAN_ENABLE, mask); filter_set_opcbit(HCIOpcodeBit::LE_CREATE_CONN, mask); filter_set_opcbit(HCIOpcodeBit::LE_ENABLE_ENC, mask); + filter_set_opcbit(HCIOpcodeBit::LE_READ_PHY, mask); + // filter_set_opcbit(HCIOpcodeBit::LE_SET_DEFAULT_PHY, mask); + filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_PARAMS, mask); + filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_ENABLE, mask); + filter_set_opcbit(HCIOpcodeBit::LE_EXT_CREATE_CONN, mask); #endif filter_put_opcbit(mask); } + sup_commands_set = false; + initSupCommands(); // OK to fail + PERF_TS_TD("HCIHandler::ctor.ok"); WORDY_PRINT("HCIHandler.ctor: End OK - %s", toString().c_str()); return; @@ -685,6 +737,32 @@ fail: return; } +void HCIHandler::zeroSupCommands() noexcept { + bzero(sup_commands, sizeof(sup_commands)); + sup_commands_set = false; +} +bool HCIHandler::initSupCommands() noexcept { + // We avoid using a lock or an atomic-switch as we rely on sensible calls. + if( !isOpen() ) { + zeroSupCommands(); + return false; + } + HCICommand req0(HCIOpcode::READ_LOCAL_COMMANDS, 0); + const hci_rp_read_local_commands * ev_cmds; + HCIStatusCode status; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_cmds, &status); + if( nullptr == ev || nullptr == ev_cmds || HCIStatusCode::SUCCESS != status ) { + DBG_PRINT("HCIHandler::ctor: READ_LOCAL_COMMANDS: 0x%x (%s) - %s", + number(status), to_string(status).c_str(), toString().c_str()); + zeroSupCommands(); + return false; + } else { + memcpy(sup_commands, ev_cmds->commands, sizeof(sup_commands)); + sup_commands_set = true; + return true; + } +} + void HCIHandler::close() noexcept { // Avoid disconnect re-entry -> potential deadlock bool expConn = true; // C++11, exp as value since C++20 @@ -692,7 +770,7 @@ void HCIHandler::close() noexcept { // not open DBG_PRINT("HCIHandler::close: Not connected %s", toString().c_str()); clearAllCallbacks(); - clearAllStates(); + resetAllStates(false); comm.close(); return; } @@ -700,7 +778,7 @@ void HCIHandler::close() noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor DBG_PRINT("HCIHandler::close: Start %s", toString().c_str()); clearAllCallbacks(); - clearAllStates(); + resetAllStates(false); // Interrupt HCIHandler's HCIComm::read(..), avoiding prolonged hang // and pull all underlying hci read operations! @@ -735,7 +813,9 @@ void HCIHandler::close() noexcept { std::string HCIHandler::toString() const noexcept { return "HCIHandler[dev_id "+std::to_string(dev_id)+", BTMode "+to_string(btMode)+", open "+std::to_string(isOpen())+ - ", scan "+to_string(currentScanType)+", ring[entries "+std::to_string(hciEventRing.getSize())+"]]"; + ", scan "+to_string(currentScanType)+ + ", ext[init "+std::to_string(sup_commands_set)+", scan "+std::to_string(use_ext_scan())+", conn "+std::to_string(use_ext_conn())+ + "], ring[entries "+std::to_string(hciEventRing.getSize())+"]]"; } HCIStatusCode HCIHandler::startAdapter() { @@ -752,6 +832,7 @@ HCIStatusCode HCIHandler::startAdapter() { return HCIStatusCode::INTERNAL_FAILURE; } } + resetAllStates(true); return HCIStatusCode::SUCCESS; #else #warning add implementation @@ -779,7 +860,7 @@ HCIStatusCode HCIHandler::stopAdapter() { status = HCIStatusCode::INTERNAL_FAILURE; #endif if( HCIStatusCode::SUCCESS == status ) { - clearAllStates(); + resetAllStates(false); } return status; } @@ -815,7 +896,7 @@ HCIStatusCode HCIHandler::reset() noexcept { return HCIStatusCode::INTERNAL_TIMEOUT; // timeout } if( HCIStatusCode::SUCCESS == status ) { - clearAllStates(); + resetAllStates(true); } return status; } @@ -843,6 +924,25 @@ HCIStatusCode HCIHandler::getLocalVersion(HCILocalVersion &version) noexcept { return status; } +HCIStatusCode HCIHandler::le_read_local_features(LE_Features& res) noexcept { + if( !isOpen() ) { + ERR_PRINT("HCIHandler::le_read_local_features: Not connected %s", toString().c_str()); + return HCIStatusCode::INTERNAL_FAILURE; + } + res = LE_Features::NONE; + HCICommand req0(HCIOpcode::LE_READ_LOCAL_FEATURES, 0); + const hci_rp_le_read_local_features * ev_lf; + HCIStatusCode status; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_lf, &status); + if( nullptr == ev || nullptr == ev_lf || HCIStatusCode::SUCCESS != status ) { + ERR_PRINT("HCIHandler::le_read_local_features: LE_READ_LOCAL_FEATURES: 0x%x (%s) - %s", + number(status), to_string(status).c_str(), toString().c_str()); + } else { + res = static_cast<LE_Features>( jau::get_uint64(ev_lf->features, 0, true /* littleEndian */) ); + } + return status; +} + HCIStatusCode HCIHandler::le_set_scan_param(const bool le_scan_active, const HCILEOwnAddressType own_mac_type, const uint16_t le_scan_interval, const uint16_t le_scan_window, @@ -856,20 +956,45 @@ HCIStatusCode HCIHandler::le_set_scan_param(const bool le_scan_active, toString().c_str(), 0.625f * (float)le_scan_interval, 0.625f * (float)le_scan_window); return HCIStatusCode::COMMAND_DISALLOWED; } - DBG_PRINT("HCI Scan Param: scan [interval %.3f ms, window %.3f ms] - %s", - 0.625f * (float)le_scan_interval, 0.625f * (float)le_scan_window, toString().c_str()); - - HCIStructCommand<hci_cp_le_set_scan_param> req0(HCIOpcode::LE_SET_SCAN_PARAM); - hci_cp_le_set_scan_param * cp = req0.getWStruct(); - cp->type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE; - cp->interval = jau::cpu_to_le(le_scan_interval); - cp->window = jau::cpu_to_le(le_scan_window); - cp->own_address_type = static_cast<uint8_t>(own_mac_type); - cp->filter_policy = filter_policy; + DBG_PRINT("HCI Scan Param: scan [active %d, interval %.3f ms, window %.3f ms, filter %d] - %s", + le_scan_active, + 0.625f * (float)le_scan_interval, 0.625f * (float)le_scan_window, + filter_policy, toString().c_str()); - const hci_rp_status * ev_status; HCIStatusCode status; - std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status); + if( use_ext_scan() ) { + struct le_set_ext_scan_params { + __u8 own_address_type; + __u8 filter_policy; + __u8 scanning_phys; + hci_cp_le_scan_phy_params p1; + // hci_cp_le_scan_phy_params p2; + } __packed; + HCIStructCommand<le_set_ext_scan_params> req0(HCIOpcode::LE_SET_EXT_SCAN_PARAMS); + le_set_ext_scan_params * cp = req0.getWStruct(); + cp->own_address_type = static_cast<uint8_t>(own_mac_type); + cp->filter_policy = filter_policy; + cp->scanning_phys = direct_bt::number(LE_PHYs::LE_1M); // TODO: Support LM_CODED? + + cp->p1.type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE; + cp->p1.interval = jau::cpu_to_le(le_scan_interval); + cp->p1.window = jau::cpu_to_le(le_scan_window); + // TODO: Support LE_1M + LE_CODED combo? + + const hci_rp_status * ev_status; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status); + } else { + HCIStructCommand<hci_cp_le_set_scan_param> req0(HCIOpcode::LE_SET_SCAN_PARAM); + hci_cp_le_set_scan_param * cp = req0.getWStruct(); + cp->type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE; + cp->interval = jau::cpu_to_le(le_scan_interval); + cp->window = jau::cpu_to_le(le_scan_window); + cp->own_address_type = static_cast<uint8_t>(own_mac_type); + cp->filter_policy = filter_policy; + + const hci_rp_status * ev_status; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status); + } return status; } @@ -886,13 +1011,23 @@ HCIStatusCode HCIHandler::le_enable_scan(const bool enable, const bool filter_du HCIStatusCode status; if( currentScanType != nextScanType ) { - HCIStructCommand<hci_cp_le_set_scan_enable> req0(HCIOpcode::LE_SET_SCAN_ENABLE); - hci_cp_le_set_scan_enable * cp = req0.getWStruct(); - cp->enable = enable ? LE_SCAN_ENABLE : LE_SCAN_DISABLE; - cp->filter_dup = filter_dup ? LE_SCAN_FILTER_DUP_ENABLE : LE_SCAN_FILTER_DUP_DISABLE; - - const hci_rp_status * ev_status; - std::unique_ptr<HCIEvent> evComplete = processCommandComplete(req0, &ev_status, &status); + if( use_ext_scan() ) { + HCIStructCommand<hci_cp_le_set_ext_scan_enable> req0(HCIOpcode::LE_SET_EXT_SCAN_ENABLE); + hci_cp_le_set_ext_scan_enable * cp = req0.getWStruct(); + cp->enable = enable ? LE_SCAN_ENABLE : LE_SCAN_DISABLE; + cp->filter_dup = LE_SCAN_FILTER_DUP_DISABLE; // filter_dup ? LE_SCAN_FILTER_DUP_ENABLE : LE_SCAN_FILTER_DUP_DISABLE; + // cp->duration = 0; // until disabled + // cp->period = 0; // until disabled + const hci_rp_status * ev_status; + std::unique_ptr<HCIEvent> evComplete = processCommandComplete(req0, &ev_status, &status); + } else { + HCIStructCommand<hci_cp_le_set_scan_enable> req0(HCIOpcode::LE_SET_SCAN_ENABLE); + hci_cp_le_set_scan_enable * cp = req0.getWStruct(); + cp->enable = enable ? LE_SCAN_ENABLE : LE_SCAN_DISABLE; + cp->filter_dup = filter_dup ? LE_SCAN_FILTER_DUP_ENABLE : LE_SCAN_FILTER_DUP_DISABLE; + const hci_rp_status * ev_status; + std::unique_ptr<HCIEvent> evComplete = processCommandComplete(req0, &ev_status, &status); + } } else { status = HCIStatusCode::SUCCESS; WARN_PRINT("HCI Enable Scan: current %s == next %s, OK, skip command - %s", @@ -964,22 +1099,6 @@ HCIStatusCode HCIHandler::le_create_conn(const EUI48 &peer_bdaddr, 1.25f * (float)conn_interval_min, 1.25f * (float)conn_interval_max, conn_latency, supervision_timeout*10, toString().c_str()); - HCIStructCommand<hci_cp_le_create_conn> req0(HCIOpcode::LE_CREATE_CONN); - hci_cp_le_create_conn * cp = req0.getWStruct(); - cp->scan_interval = jau::cpu_to_le(le_scan_interval); - cp->scan_window = jau::cpu_to_le(le_scan_window); - cp->filter_policy = initiator_filter; - cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type); - cp->peer_addr = peer_bdaddr; - cp->own_address_type = static_cast<uint8_t>(own_mac_type); - cp->conn_interval_min = jau::cpu_to_le(conn_interval_min); - cp->conn_interval_max = jau::cpu_to_le(conn_interval_max); - cp->conn_latency = jau::cpu_to_le(conn_latency); - cp->supervision_timeout = jau::cpu_to_le(supervision_timeout); - cp->min_ce_len = jau::cpu_to_le(min_ce_length); - cp->max_ce_len = jau::cpu_to_le(max_ce_length); - const BDAddressAndType addressAndType(peer_bdaddr, to_BDAddressType(peer_mac_type)); - int pendingConnections = countPendingTrackerConnections(); if( 0 < pendingConnections ) { DBG_PRINT("HCIHandler::le_create_conn: %d connections pending - %s", pendingConnections, toString().c_str()); @@ -995,6 +1114,7 @@ HCIStatusCode HCIHandler::le_create_conn(const EUI48 &peer_bdaddr, DBG_PRINT("HCIHandler::le_create_conn: pending connections resolved after %d ms - %s", td, toString().c_str()); } } + const BDAddressAndType addressAndType(peer_bdaddr, to_BDAddressType(peer_mac_type)); HCIConnectionRef disconn = findDisconnectCmd(addressAndType); if( nullptr != disconn ) { DBG_PRINT("HCIHandler::le_create_conn: disconnect pending %s - %s", @@ -1014,7 +1134,60 @@ HCIStatusCode HCIHandler::le_create_conn(const EUI48 &peer_bdaddr, } HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, 0); HCIStatusCode status; - std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status); + + if( use_ext_conn() ) { + struct le_ext_create_conn { + __u8 filter_policy; + __u8 own_address_type; + __u8 peer_addr_type; + bdaddr_t peer_addr; + __u8 phys; + hci_cp_le_ext_conn_param p1; + // hci_cp_le_ext_conn_param p2; + // hci_cp_le_ext_conn_param p3; + } __packed; + HCIStructCommand<le_ext_create_conn> req0(HCIOpcode::LE_EXT_CREATE_CONN); + le_ext_create_conn * cp = req0.getWStruct(); + cp->filter_policy = initiator_filter; + cp->own_address_type = static_cast<uint8_t>(own_mac_type); + cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type); + cp->peer_addr = peer_bdaddr; + cp->phys = direct_bt::number(LE_PHYs::LE_1M); // TODO: Support other PHYs? + + cp->p1.scan_interval = jau::cpu_to_le(le_scan_interval); + cp->p1.scan_window = jau::cpu_to_le(le_scan_window); + cp->p1.conn_interval_min = jau::cpu_to_le(conn_interval_min); + cp->p1.conn_interval_max = jau::cpu_to_le(conn_interval_max); + cp->p1.conn_latency = jau::cpu_to_le(conn_latency); + cp->p1.supervision_timeout = jau::cpu_to_le(supervision_timeout); + cp->p1.min_ce_len = jau::cpu_to_le(min_ce_length); + cp->p1.max_ce_len = jau::cpu_to_le(max_ce_length); + // TODO: Support some PHYs combo settings? + + std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status); + // Events on successful connection: + // - HCI_LE_Enhanced_Connection_Complete + // - HCI_LE_Channel_Selection_Algorithm + } else { + HCIStructCommand<hci_cp_le_create_conn> req0(HCIOpcode::LE_CREATE_CONN); + hci_cp_le_create_conn * cp = req0.getWStruct(); + cp->scan_interval = jau::cpu_to_le(le_scan_interval); + cp->scan_window = jau::cpu_to_le(le_scan_window); + cp->filter_policy = initiator_filter; + cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type); + cp->peer_addr = peer_bdaddr; + cp->own_address_type = static_cast<uint8_t>(own_mac_type); + cp->conn_interval_min = jau::cpu_to_le(conn_interval_min); + cp->conn_interval_max = jau::cpu_to_le(conn_interval_max); + cp->conn_latency = jau::cpu_to_le(conn_latency); + cp->supervision_timeout = jau::cpu_to_le(supervision_timeout); + cp->min_ce_len = jau::cpu_to_le(min_ce_length); + cp->max_ce_len = jau::cpu_to_le(max_ce_length); + + std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status); + // Events on successful connection: + // - HCI_LE_Connection_Complete + } if( HCIStatusCode::SUCCESS != status ) { removeTrackerConnection(conn); @@ -1151,6 +1324,83 @@ HCIStatusCode HCIHandler::disconnect(const uint16_t conn_handle, const BDAddress return status; } +HCIStatusCode HCIHandler::le_read_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType, + LE_PHYs& resRx, LE_PHYs& resTx) noexcept { + resRx = LE_PHYs::NONE; + resTx = LE_PHYs::NONE; + + if( !isOpen() ) { + ERR_PRINT("HCIHandler::le_read_phy: Not connected %s", toString().c_str()); + return HCIStatusCode::INTERNAL_FAILURE; + } + if( 0 == conn_handle ) { + ERR_PRINT("HCIHandler::le_read_phy: Null conn_handle given address%s (drop) - %s", + peerAddressAndType.toString().c_str(), toString().c_str()); + return HCIStatusCode::INVALID_HCI_COMMAND_PARAMETERS; + } + { + const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor + HCIConnectionRef conn = findTrackerConnection(conn_handle); + if( nullptr == conn ) { + // called w/o being connected through this HCIHandler + ERR_PRINT("HCIHandler::le_read_phy: Not tracked handle %s (address%s) (drop) - %s", + jau::to_hexstring(conn_handle).c_str(), + peerAddressAndType.toString().c_str(), toString().c_str()); + return HCIStatusCode::INVALID_HCI_COMMAND_PARAMETERS; + } else if( !conn->equals(peerAddressAndType) ) { + ERR_PRINT("HCIHandler::le_read_phy: Mismatch given address%s and tracked %s (drop) - %s", + peerAddressAndType.toString().c_str(), + conn->toString().c_str(), toString().c_str()); + return HCIStatusCode::INVALID_HCI_COMMAND_PARAMETERS; + } + DBG_PRINT("HCIHandler::le_read_phy: address%s, handle %s, %s - %s", + peerAddressAndType.toString().c_str(), + jau::to_hexstring(conn_handle).c_str(), + conn->toString().c_str(), toString().c_str()); + } + struct hci_cp_le_read_phy { + __le16 handle; + } __packed; + struct hci_rp_le_read_phy { + __u8 status; + __le16 handle; + __u8 tx_phys; + __u8 rx_phys; + } __packed; + + HCIStatusCode status; + + HCIStructCommand<hci_cp_le_read_phy> req0(HCIOpcode::LE_READ_PHY); + hci_cp_le_read_phy * cp = req0.getWStruct(); + cp->handle = jau::cpu_to_le(conn_handle); + const hci_rp_le_read_phy * ev_phy; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_phy, &status); + + if( nullptr == ev || nullptr == ev_phy || HCIStatusCode::SUCCESS != status ) { + ERR_PRINT("HCIHandler::le_read_phy: LE_READ_PHY: 0x%x (%s) - %s", + number(status), to_string(status).c_str(), toString().c_str()); + } else { + const uint16_t conn_handle_rcvd = jau::le_to_cpu(ev_phy->handle); + if( conn_handle != conn_handle_rcvd ) { + ERR_PRINT("HCIHandler::le_read_phy: Mismatch given address%s conn_handle (req) %s != %s (res) (drop) - %s", + peerAddressAndType.toString().c_str(), + jau::to_hexstring(conn_handle).c_str(), jau::to_hexstring(conn_handle_rcvd).c_str(), toString().c_str()); + return HCIStatusCode::INTERNAL_FAILURE; + } + switch( ev_phy->tx_phys ) { + case 0x01: resTx = LE_PHYs::LE_1M; break; + case 0x02: resTx = LE_PHYs::LE_2M; break; + case 0x03: resTx = LE_PHYs::LE_CODED; break; + } + switch( ev_phy->rx_phys ) { + case 0x01: resRx = LE_PHYs::LE_1M; break; + case 0x02: resRx = LE_PHYs::LE_2M; break; + case 0x03: resRx = LE_PHYs::LE_CODED; break; + } + } + return status; +} + std::unique_ptr<HCIEvent> HCIHandler::processCommandStatus(HCICommand &req, HCIStatusCode *status) noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor diff --git a/src/direct_bt/HCITypes.cpp b/src/direct_bt/HCITypes.cpp index 340a1766..6e701b16 100644 --- a/src/direct_bt/HCITypes.cpp +++ b/src/direct_bt/HCITypes.cpp @@ -175,6 +175,7 @@ std::string to_string(const HCIOGF op) noexcept { X(SET_EVENT_MASK) \ X(RESET) \ X(READ_LOCAL_VERSION) \ + X(READ_LOCAL_COMMANDS) \ X(LE_SET_EVENT_MASK) \ X(LE_READ_BUFFER_SIZE) \ X(LE_READ_LOCAL_FEATURES) \ @@ -194,7 +195,13 @@ std::string to_string(const HCIOGF op) noexcept { X(LE_DEL_FROM_WHITE_LIST) \ X(LE_CONN_UPDATE) \ X(LE_READ_REMOTE_FEATURES) \ - X(LE_ENABLE_ENC) + X(LE_ENABLE_ENC) \ + X(LE_READ_PHY) \ + X(LE_SET_DEFAULT_PHY) \ + X(LE_SET_EXT_SCAN_PARAMS) \ + X(LE_SET_EXT_SCAN_ENABLE) \ + X(LE_EXT_CREATE_CONN) + #define HCI_OPCODE_CASE_TO_STRING(V) case HCIOpcode::V: return #V; @@ -260,7 +267,7 @@ std::string to_string(const HCIEventType op) noexcept { X(LE_DATA_LENGTH_CHANGE) \ X(LE_READ_LOCAL_P256_PUBKEY_COMPLETE) \ X(LE_GENERATE_DHKEY_COMPLETE) \ - X(LE_ENHANCED_CONN_COMPLETE) \ + X(LE_EXT_CONN_COMPLETE) \ X(LE_DIRECT_ADV_REPORT) \ X(LE_PHY_UPDATE_COMPLETE) \ X(LE_EXT_ADV_REPORT) \ @@ -348,8 +355,10 @@ std::unique_ptr<HCIACLData> HCIACLData::getSpecialized(const uint8_t * buffer, j } const jau::nsize_t paramSize = buffer_size >= number(HCIConstSizeT::ACL_HDR_SIZE) ? jau::get_uint16(buffer, 3) : 0; if( buffer_size < number(HCIConstSizeT::ACL_HDR_SIZE) + paramSize ) { - WARN_PRINT("HCIACLData::getSpecialized: length mismatch %u < ACL_HDR_SIZE(%u) + %u", - buffer_size, number(HCIConstSizeT::ACL_HDR_SIZE), paramSize); + if( jau::environment::get().verbose ) { + WARN_PRINT("HCIACLData::getSpecialized: length mismatch %u < ACL_HDR_SIZE(%u) + %u", + buffer_size, number(HCIConstSizeT::ACL_HDR_SIZE), paramSize); + } return nullptr; } return std::make_unique<HCIACLData>(buffer, buffer_size); |