summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md17
-rw-r--r--api/direct_bt/BTAdapter.hpp18
-rw-r--r--api/direct_bt/BTDevice.hpp15
-rw-r--r--api/direct_bt/BTTypes0.hpp274
-rw-r--r--api/direct_bt/HCIHandler.hpp139
-rw-r--r--api/direct_bt/HCITypes.hpp20
-rw-r--r--api/direct_bt/MgmtTypes.hpp4
-rw-r--r--api/direct_bt/SMPHandler.hpp20
-rw-r--r--doc/log/dbt_scanner10-bt5_intel-01.btmonbin0 -> 19316 bytes
-rw-r--r--doc/log/dbt_scanner10-bt5_intel-01.log.txt292
-rw-r--r--doc/log/dbt_scanner10-bt5_intel.txt14
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp25
-rw-r--r--examples/java/DBTScanner10.java19
-rwxr-xr-xscripts/run-dbt_scanner10.sh2
-rw-r--r--src/direct_bt/BTAdapter.cpp45
-rw-r--r--src/direct_bt/BTDevice.cpp46
-rw-r--r--src/direct_bt/BTTypes0.cpp324
-rw-r--r--src/direct_bt/HCIHandler.cpp350
-rw-r--r--src/direct_bt/HCITypes.cpp17
19 files changed, 1434 insertions, 207 deletions
diff --git a/README.md b/README.md
index 9dc4ce5f..22230a31 100644
--- a/README.md
+++ b/README.md
@@ -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
new file mode 100644
index 00000000..a0491e28
--- /dev/null
+++ b/doc/log/dbt_scanner10-bt5_intel-01.btmon
Binary files differ
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);