aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/direct_bt/BTTypes.hpp7
-rw-r--r--api/direct_bt/BasicTypes.hpp20
-rw-r--r--api/direct_bt/DBTAdapter.hpp19
-rw-r--r--api/direct_bt/DBTDevice.hpp2
-rw-r--r--api/direct_bt/DBTManager.hpp48
-rw-r--r--api/direct_bt/DBTTypes.hpp6
-rw-r--r--api/direct_bt/GATTHandler.hpp3
-rw-r--r--api/direct_bt/HCIComm.hpp173
-rw-r--r--api/direct_bt/HCIHandler.hpp200
-rw-r--r--api/direct_bt/HCITypes.hpp815
-rw-r--r--api/direct_bt/MgmtTypes.hpp43
-rw-r--r--api/direct_bt/OctetTypes.hpp3
-rw-r--r--examples/direct_bt_scanner00/dbt_scanner00.cpp8
-rw-r--r--examples/direct_bt_scanner01/dbt_scanner01.cpp10
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp8
-rw-r--r--examples/java/ScannerTinyB10.java2
-rw-r--r--java/jni/direct_bt/DBTAdapter.cxx4
-rw-r--r--src/direct_bt/BTTypes.cpp21
-rw-r--r--src/direct_bt/CMakeLists.txt2
-rw-r--r--src/direct_bt/DBTAdapter.cpp36
-rw-r--r--src/direct_bt/DBTDevice.cpp30
-rw-r--r--src/direct_bt/DBTManager.cpp3
-rw-r--r--src/direct_bt/DBTTypes.cpp6
-rw-r--r--src/direct_bt/GATTHandler.cpp9
-rw-r--r--src/direct_bt/GATTNumbers.cpp2
-rw-r--r--src/direct_bt/HCIComm.cpp581
-rw-r--r--src/direct_bt/HCIHandler.cpp598
-rw-r--r--src/direct_bt/HCITypes.cpp299
28 files changed, 2080 insertions, 878 deletions
diff --git a/api/direct_bt/BTTypes.hpp b/api/direct_bt/BTTypes.hpp
index 19c03e82..cb48303e 100644
--- a/api/direct_bt/BTTypes.hpp
+++ b/api/direct_bt/BTTypes.hpp
@@ -43,6 +43,7 @@ namespace direct_bt {
BT_MODE_BREDR = 2,
BT_MODE_LE = 3
};
+ std::string BTModeString(const BTMode v);
/**
* HCI Whitelist connection type.
@@ -315,7 +316,7 @@ namespace direct_bt {
OUTDOOR_SPORTS_ACTIVITY_LOCATION_POD = 5187,
OUTDOOR_SPORTS_ACTIVITY_LOCATION_AND_NAVIGATION_POD = 5188
};
- std::string AppearanceCatToString(const AppearanceCat v);
+ std::string getAppearanceCatString(const AppearanceCat v);
// *************************************************
// *************************************************
@@ -382,8 +383,8 @@ namespace direct_bt {
}
inline bool isEIRDataTypeSet(const EIRDataType mask, const EIRDataType bit) { return EIRDataType::NONE != ( mask & bit ); }
inline void setEIRDataTypeSet(EIRDataType &mask, const EIRDataType bit) { mask = mask | bit; }
- std::string eirDataBitToString(const EIRDataType bit);
- std::string eirDataMaskToString(const EIRDataType mask);
+ std::string getEIRDataBitString(const EIRDataType bit);
+ std::string getEIRDataMaskString(const EIRDataType mask);
/**
* Collection of 'Advertising Data' (AD)
diff --git a/api/direct_bt/BasicTypes.hpp b/api/direct_bt/BasicTypes.hpp
index 844357ac..231d8e03 100644
--- a/api/direct_bt/BasicTypes.hpp
+++ b/api/direct_bt/BasicTypes.hpp
@@ -309,6 +309,24 @@ namespace direct_bt {
return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p);
}
+ inline void set_bit_uint32(const int nr, uint32_t &mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, 32, E_FILE_LINE); }
+ mask |= 1 << (nr & 31);
+ }
+
+ inline void clear_bit_uint32(const int nr, uint32_t &mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, 32, E_FILE_LINE); }
+ mask |= ~(1 << (nr & 31));
+ }
+
+ inline uint32_t test_bit_uint32(const int nr, const uint32_t mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, 32, E_FILE_LINE); }
+ return mask & (1 << (nr & 31));
+ }
+
/**
* Returns a C++ String taken from buffer with maximum length of min(max_len, max_len).
* <p>
@@ -377,7 +395,7 @@ namespace direct_bt {
* Otherwise orders MSB left -> LSB right, usual for readable integer values.
* </p>
*/
- std::string bytesHexString(const uint8_t * bytes, const int offset, const int length, const bool lsbFirst, const bool leading0X);
+ std::string bytesHexString(const uint8_t * bytes, const int offset, const int length, const bool lsbFirst, const bool leading0X=true);
std::string int32SeparatedString(const int32_t v, const char separator=',');
std::string uint32SeparatedString(const uint32_t v, const char separator=',');
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp
index 0c0e43ee..a7dc43b3 100644
--- a/api/direct_bt/DBTAdapter.hpp
+++ b/api/direct_bt/DBTAdapter.hpp
@@ -39,7 +39,7 @@
#include "DBTDevice.hpp"
-#include "HCIComm.hpp"
+#include "HCIHandler.hpp"
#include "DBTManager.hpp"
namespace direct_bt {
@@ -157,7 +157,7 @@ namespace direct_bt {
NameAndShortName localName;
ScanType currentScanType = ScanType::SCAN_TYPE_NONE;
- std::shared_ptr<HCIComm> hciComm;
+ std::shared_ptr<HCIHandler> hci;
std::vector<std::shared_ptr<DBTDevice>> connectedDevices;
std::vector<std::shared_ptr<DBTDevice>> discoveredDevices; // all discovered devices
std::vector<std::shared_ptr<DBTDevice>> sharedDevices; // all active shared devices
@@ -207,6 +207,7 @@ namespace direct_bt {
void performDeviceConnected(std::shared_ptr<DBTDevice> device, uint64_t timestamp);
public:
+ const BTMode btMode;
const int dev_id;
/**
@@ -289,18 +290,18 @@ namespace direct_bt {
DBTManager& getManager() const { return mgmt; }
/**
- * Returns a reference to the already opened HCIComm
- * or the newly opened HCIComm instance, otherwise nullptr if no success.
+ * Returns a reference to the already opened HCIHandler
+ * or the newly opened HCIHandler instance, otherwise nullptr if no success.
*/
- std::shared_ptr<HCIComm> openHCI();
+ std::shared_ptr<HCIHandler> openHCI();
/**
- * Returns the {@link #openHCI()} HCIComm or {@code nullptr} if closed.
+ * Returns the {@link #openHCI()} HCIHandler or {@code nullptr} if closed.
*/
- std::shared_ptr<HCIComm> getHCI() const;
+ std::shared_ptr<HCIHandler> getHCI() const;
/**
- * Closes the HCIComm instance
+ * Closes the HCIHandler instance
*/
bool closeHCI();
@@ -330,7 +331,7 @@ namespace direct_bt {
bool addDeviceToWhitelist(const EUI48 &address, const BDAddressType address_type,
const HCIWhitelistConnectType ctype,
const uint16_t conn_interval_min=0x000F, const uint16_t conn_interval_max=0x000F,
- const uint16_t conn_latency=0x0000, const uint16_t timeout=HCI_LE_CONN_TIMEOUT_MS/10);
+ const uint16_t conn_latency=0x0000, const uint16_t timeout=number(HCIConstInt::LE_CONN_TIMEOUT_MS)/10);
/** Remove the given device from the adapter's autoconnect whitelist. */
diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp
index 325ed9c5..91866987 100644
--- a/api/direct_bt/DBTDevice.hpp
+++ b/api/direct_bt/DBTDevice.hpp
@@ -192,7 +192,7 @@ namespace direct_bt {
const HCIAddressType own_mac_type=HCIAddressType::HCIADDR_LE_PUBLIC,
const uint16_t le_scan_interval=48, const uint16_t le_scan_window=48,
const uint16_t conn_interval_min=0x000F, const uint16_t conn_interval_max=0x000F,
- const uint16_t conn_latency=0x0000, const uint16_t supervision_timeout=HCI_LE_CONN_TIMEOUT_MS/10);
+ const uint16_t conn_latency=0x0000, const uint16_t supervision_timeout=number(HCIConstInt::LE_CONN_TIMEOUT_MS)/10);
/**
* Establish a HCI BDADDR_BREDR connection to this device.
diff --git a/api/direct_bt/DBTManager.hpp b/api/direct_bt/DBTManager.hpp
index 15e46098..c2e1e7a4 100644
--- a/api/direct_bt/DBTManager.hpp
+++ b/api/direct_bt/DBTManager.hpp
@@ -42,47 +42,9 @@
#include "JavaUplink.hpp"
#include "MgmtTypes.hpp"
#include "LFRingbuffer.hpp"
-#include "FunctionDef.hpp"
namespace direct_bt {
- typedef FunctionDef<bool, std::shared_ptr<MgmtEvent>> MgmtEventCallback;
-
- class MgmtAdapterEventCallback {
- private:
- /** Unique adapter index filter or <code>-1</code> to listen for all adapter. */
- int dev_id;
- /** MgmtEventCallback instance */
- MgmtEventCallback callback;
-
- public:
- MgmtAdapterEventCallback(int _dev_id, const MgmtEventCallback & _callback)
- : dev_id(_dev_id), callback(_callback) {}
-
- MgmtAdapterEventCallback(const MgmtAdapterEventCallback &o) = default;
- MgmtAdapterEventCallback(MgmtAdapterEventCallback &&o) = default;
- MgmtAdapterEventCallback& operator=(const MgmtAdapterEventCallback &o) = default;
- MgmtAdapterEventCallback& operator=(MgmtAdapterEventCallback &&o) = default;
-
- /** Unique adapter index filter or <code>-1</code> to listen for all adapter. */
- int getDevID() const { return dev_id; }
-
- /** MgmtEventCallback reference */
- MgmtEventCallback& getCallback() { return callback; }
-
- bool operator==(const MgmtAdapterEventCallback& rhs) const
- { return dev_id == rhs.dev_id && callback == rhs.callback; }
-
- bool operator!=(const MgmtAdapterEventCallback& rhs) const
- { return !(*this == rhs); }
-
- std::string toString() const {
- return "MgmtAdapterEventCallback[dev_id "+std::to_string(dev_id)+", "+callback.toString()+"]";
- }
- };
-
- typedef std::vector<MgmtAdapterEventCallback> MgmtAdapterEventCallbackList;
-
/**
* A thread safe singleton handler of the Linux Kernel's BlueZ manager control channel.
* <p>
@@ -108,7 +70,7 @@ namespace direct_bt {
MGMT_READER_THREAD_POLL_TIMEOUT = 3000,
/** 1s timeout for mgmt command replies */
MGMT_COMMAND_REPLY_TIMEOUT = 1000,
- MGMTEVT_RING_CAPACITY = 256
+ MGMTEVT_RING_CAPACITY = 128
};
static const pid_t pidSelf;
@@ -200,7 +162,9 @@ namespace direct_bt {
return comm.isOpen();
}
- std::string toString() const override { return "MgmtHandler["+std::to_string(adapterInfos.size())+" adapter, "+javaObjectToString()+"]"; }
+ std::string toString() const override {
+ return "MgmtHandler["+BTModeString(btMode)+", "+std::to_string(adapterInfos.size())+" adapter, "+javaObjectToString()+"]";
+ }
/** retrieve information gathered at startup */
@@ -257,7 +221,7 @@ namespace direct_bt {
*/
bool uploadConnParam(const int dev_id, const EUI48 &address, const BDAddressType address_type,
const uint16_t conn_interval_min=0x000F, const uint16_t conn_interval_max=0x000F,
- const uint16_t conn_latency=0x0000, const uint16_t timeout=HCI_LE_CONN_TIMEOUT_MS/10);
+ const uint16_t conn_latency=0x0000, const uint16_t timeout=number(HCIConstInt::LE_CONN_TIMEOUT_MS)/10);
/**
* Returns true, if the adapter's device is already whitelisted.
@@ -287,7 +251,7 @@ namespace direct_bt {
const BDAddressType own_mac_type=BDADDR_LE_PUBLIC,
const uint16_t interval=0x0004, const uint16_t window=0x0004,
const uint16_t min_interval=0x000F, const uint16_t max_interval=0x000F,
- const uint16_t latency=0x0000, const uint16_t supervision_timeout=HCI_LE_CONN_TIMEOUT_MS/10);
+ const uint16_t latency=0x0000, const uint16_t supervision_timeout=number(HCIConstInt::LE_CONN_TIMEOUT_MS)/10);
bool disconnect(const bool ioErrorCause,
const int dev_id, const EUI48 &peer_bdaddr, const BDAddressType peer_mac_type,
diff --git a/api/direct_bt/DBTTypes.hpp b/api/direct_bt/DBTTypes.hpp
index 23bff919..08f8efa6 100644
--- a/api/direct_bt/DBTTypes.hpp
+++ b/api/direct_bt/DBTTypes.hpp
@@ -167,8 +167,8 @@ namespace direct_bt {
}
inline bool isAdapterSettingSet(const AdapterSetting mask, const AdapterSetting bit) { return AdapterSetting::NONE != ( mask & bit ); }
inline void setAdapterSettingSet(AdapterSetting &mask, const AdapterSetting bit) { mask = mask | bit; }
- std::string adapterSettingBitToString(const AdapterSetting settingBit);
- std::string adapterSettingsToString(const AdapterSetting settingBitMask);
+ std::string getAdapterSettingBitString(const AdapterSetting settingBit);
+ std::string getAdapterSettingsString(const AdapterSetting settingBitMask);
class AdapterInfo
{
@@ -226,7 +226,7 @@ namespace direct_bt {
std::string toString() const {
return "Adapter[id "+std::to_string(dev_id)+", address "+address.toString()+", version "+std::to_string(version)+
", manuf "+std::to_string(manufacturer)+
- ", settings[sup "+adapterSettingsToString(supported_setting)+", cur "+adapterSettingsToString(current_setting)+
+ ", settings[sup "+getAdapterSettingsString(supported_setting)+", cur "+getAdapterSettingsString(current_setting)+
"], name '"+name+"', shortName '"+short_name+"']";
}
};
diff --git a/api/direct_bt/GATTHandler.hpp b/api/direct_bt/GATTHandler.hpp
index 30f7f0d7..a915eec8 100644
--- a/api/direct_bt/GATTHandler.hpp
+++ b/api/direct_bt/GATTHandler.hpp
@@ -88,7 +88,7 @@ namespace direct_bt {
/** 500ms timeout for l2cap command replies */
L2CAP_COMMAND_REPLY_TIMEOUT = 500,
- ATTPDU_RING_CAPACITY = 256
+ ATTPDU_RING_CAPACITY = 128
};
static std::string getStateString(const State state);
@@ -121,7 +121,6 @@ namespace direct_bt {
void l2capReaderThreadImpl();
- std::shared_ptr<const AttPDUMsg> receiveNext();
void send(const AttPDUMsg & msg);
std::shared_ptr<const AttPDUMsg> sendWithReply(const AttPDUMsg & msg);
diff --git a/api/direct_bt/HCIComm.hpp b/api/direct_bt/HCIComm.hpp
index 2f7d1800..514ea5ee 100644
--- a/api/direct_bt/HCIComm.hpp
+++ b/api/direct_bt/HCIComm.hpp
@@ -35,6 +35,7 @@
#include "BTTypes.hpp"
#include "BTIoctl.hpp"
#include "HCIIoctl.hpp"
+#include "HCITypes.hpp"
/**
* - - - - - - - - - - - - - - -
@@ -45,97 +46,6 @@
*/
namespace direct_bt {
- enum HCIDefaults : int {
- /** 3s poll timeout for HCI readout */
- HCI_TO_SEND_REQ_POLL_MS = 3000,
- /** 10s le connection timeout, supervising max is 32s (v5.2 Vol 4, Part E - 7.8.12) */
- HCI_LE_CONN_TIMEOUT_MS = 10000
- };
-
- enum HCI_Event_Types : uint8_t {
- LE_Advertising_Report = 0x3E
- };
-
- /**
- * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes
- * <p>
- * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 2 Error code descriptions
- * </p>
- */
- enum class HCIErrorCode : uint8_t {
- SUCCESS = 0x00,
- UNKNOWN_HCI_COMMAND = 0x01,
- UNKNOWN_CONNECTION_IDENTIFIER = 0x02,
- HARDWARE_FAILURE = 0x03,
- PAGE_TIMEOUT = 0x04,
- AUTHENTICATION_FAILURE = 0x05,
- PIN_OR_KEY_MISSING = 0x06,
- MEMORY_CAPACITY_EXCEEDED = 0x07,
- CONNECTION_TIMEOUT = 0x08,
- CONNECTION_LIMIT_EXCEEDED = 0x09,
- SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED = 0x0a,
- CONNECTION_ALREADY_EXISTS = 0x0b,
- COMMAND_DISALLOWED = 0x0c,
- CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0d,
- CONNECTION_REJECTED_SECURITY = 0x0e,
- CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0f,
- CONNECTION_ACCEPT_TIMEOUT_EXCEEDED = 0x10,
- UNSUPPORTED_FEATURE_OR_PARAM_VALUE = 0x11,
- INVALID_HCI_COMMAND_PARAMETERS = 0x12,
- REMOTE_USER_TERMINATED_CONNECTION = 0x13,
- REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
- REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
- CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
- REPEATED_ATTEMPTS = 0x17,
- PAIRING_NOT_ALLOWED = 0x18,
- UNKNOWN_LMP_PDU = 0x19,
- UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1a,
- SCO_OFFSET_REJECTED = 0x1b,
- SCO_INTERVAL_REJECTED = 0x1c,
- SCO_AIR_MODE_REJECTED = 0x1d,
- INVALID_LMP_OR_LL_PARAMETERS = 0x1e,
- UNSPECIFIED_ERROR = 0x1f,
- UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE = 0x20,
- ROLE_CHANGE_NOT_ALLOWED = 0x21,
- LMP_OR_LL_RESPONSE_TIMEOUT = 0x22,
- LMP_OR_LL_COLLISION = 0x23,
- LMP_PDU_NOT_ALLOWED = 0x24,
- ENCRYPTION_MODE_NOT_ACCEPTED = 0x25,
- LINK_KEY_CANNOT_BE_CHANGED = 0x26,
- REQUESTED_QOS_NOT_SUPPORTED = 0x27,
- INSTANT_PASSED = 0x28,
- PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29,
- DIFFERENT_TRANSACTION_COLLISION = 0x2a,
- QOS_UNACCEPTABLE_PARAMETER = 0x2c,
- QOS_REJECTED = 0x2d,
- CHANNEL_ASSESSMENT_NOT_SUPPORTED = 0x2e,
- INSUFFICIENT_SECURITY = 0x2f,
- PARAMETER_OUT_OF_RANGE = 0x30,
- ROLE_SWITCH_PENDING = 0x32,
- RESERVED_SLOT_VIOLATION = 0x34,
- ROLE_SWITCH_FAILED = 0x35,
- EIR_TOO_LARGE = 0x36,
- SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x37,
- HOST_BUSY_PAIRING = 0x38,
- CONNECTION_REJECTED_NO_SUITABLE_CHANNEL = 0x39,
- CONTROLLER_BUSY = 0x3a,
- UNACCEPTABLE_CONNECTION_PARAM = 0x3b,
- ADVERTISING_TIMEOUT = 0x3c,
- CONNECTION_TERMINATED_MIC_FAILURE = 0x3d,
- CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT = 0x3e,
- MAX_CONNECTION_FAILED = 0x3f,
- COARSE_CLOCK_ADJ_REJECTED = 0x40,
- TYPE0_SUBMAP_NOT_DEFINED = 0x41,
- UNKNOWN_ADVERTISING_IDENTIFIER = 0x42,
- LIMIT_REACHED = 0x43,
- OPERATION_CANCELLED_BY_HOST = 0x44,
- PACKET_TOO_LONG = 0x45,
-
- INTERNAL_FAILURE = 0xfe,
- UNKNOWN = 0xff
- };
- std::string getHCIErrorCodeString(const HCIErrorCode ec);
-
class HCIComm {
private:
static int hci_open_dev(const uint16_t dev_id, const uint16_t channel);
@@ -146,30 +56,17 @@ namespace direct_bt {
const uint16_t dev_id;
const uint16_t channel;
int _dd; // the hci socket
- bool le_scanning;
-
- bool send_cmd(const uint16_t opcode, const void *command, const uint8_t command_len);
- HCIErrorCode send_req(const uint16_t opcode, const void *command, const uint8_t command_len,
- const uint16_t exp_event, void *response, const uint8_t response_len);
-
- bool le_set_scan_enable(const uint8_t enable, const uint8_t filter_dup);
- bool le_set_scan_parameters(const uint8_t type, const uint16_t interval,
- const uint16_t window, const uint8_t own_type,
- const uint8_t filter_policy);
public:
- HCIComm(const uint16_t dev_id, const uint16_t channel, const int timeoutMS=HCIDefaults::HCI_TO_SEND_REQ_POLL_MS)
- : timeoutMS(timeoutMS), dev_id(dev_id), channel(channel), _dd(-1), le_scanning(false) {
+ HCIComm(const uint16_t dev_id, const uint16_t channel, const int timeoutMS=number(HCIConstInt::TO_SEND_REQ_POLL_MS))
+ : timeoutMS(timeoutMS), dev_id(dev_id), channel(channel), _dd(-1) {
_dd = hci_open_dev(dev_id, channel);
}
/**
- * Releases this instance after {@link #le_disable_scan()} and {@link #close()}.
- * <p>
- * Since no connection handles are being stored, {@link #le_disconnect(..)} can't be issued.
- * </p>
+ * Releases this instance after issuing {@link #close()}.
*/
- ~HCIComm() { le_disable_scan(); close(); }
+ ~HCIComm() { close(); }
void close();
@@ -188,66 +85,6 @@ namespace direct_bt {
/** Generic write */
int write(const uint8_t* buffer, const int size);
- /**
- * Enable scanning for LE devices, i.e. starting discovery.
- * <p>
- * It is recommended to utilize the DBTManager manager channel for device discovery!
- * </p>
- */
- bool le_enable_scan(const HCIAddressType own_type=HCIAddressType::HCIADDR_LE_PUBLIC,
- const uint16_t interval=0x0004, const uint16_t window=0x0004);
- /**
- * Disable scanning for LE devices.
- * <p>
- * It is recommended to utilize the DBTManager manager channel to handle scanning!
- * </p>
- */
- void le_disable_scan();
-
- /**
- * Establish a connection to the given LE peer.
- * <p>
- * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command
- * </p>
- * <p>
- * Even if not utilizing a HCI channel, it has been observed that maintaining such
- * enhanced performance on subsequent communication, i.e. GATT over L2CAP.
- * </p>
- * <p>
- * Set window to the same value as the interval, enables continuous scanning.
- * </p>
- *
- * @param handle_return
- * @param peer_bdaddr
- * @param peer_mac_type
- * @param own_mac_type
- * @param le_scan_interval in units of 0.625ms, default value 48 for 30ms, min value 4 for 2.5ms -> 0x4000 for 10.24s
- * @param le_scan_window in units of 0.625ms, default value 48 for 30ms, min value 4 for 2.5ms -> 0x4000 for 10.24s. Shall be <= le_scan_interval
- * @param conn_interval_min in units of 1.25ms, default value 15 for 19.75ms
- * @param conn_interval_max in units of 1.25ms, default value 15 for 19.75ms
- * @param conn_latency slave latency in units of connection events, default value 0
- * @param supervision_timeout in units of 10ms, default value 1000 for 10000ms or 10s.
- * @return
- */
- HCIErrorCode le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
- const HCIAddressType peer_mac_type=HCIAddressType::HCIADDR_LE_PUBLIC,
- const HCIAddressType own_mac_type=HCIAddressType::HCIADDR_LE_PUBLIC,
- const uint16_t le_scan_interval=48, const uint16_t le_scan_window=48,
- const uint16_t conn_interval_min=0x000F, const uint16_t conn_interval_max=0x000F,
- const uint16_t conn_latency=0x0000, const uint16_t supervision_timeout=HCI_LE_CONN_TIMEOUT_MS/10);
-
- /**
- * Establish a connection to the given BREDR (non LE).
- */
- HCIErrorCode create_conn(uint16_t * handle_return, const EUI48 &bdaddr,
- const uint16_t pkt_type=HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5,
- const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01);
-
- /**
- * Disconnect an established connection.
- */
- bool disconnect(const uint16_t conn_handle, const HCIErrorCode reason=HCIErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
-
private:
static inline void set_bit(int nr, void *addr)
{
diff --git a/api/direct_bt/HCIHandler.hpp b/api/direct_bt/HCIHandler.hpp
new file mode 100644
index 00000000..e5e87371
--- /dev/null
+++ b/api/direct_bt/HCIHandler.hpp
@@ -0,0 +1,200 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef HCI_HANDLER_HPP_
+#define HCI_HANDLER_HPP_
+
+#include <cstring>
+#include <string>
+#include <cstdint>
+#include <array>
+
+#include <mutex>
+#include <atomic>
+#include <thread>
+
+#include "BTTypes.hpp"
+#include "BTIoctl.hpp"
+#include "OctetTypes.hpp"
+#include "HCIComm.hpp"
+#include "JavaUplink.hpp"
+#include "HCITypes.hpp"
+#include "MgmtTypes.hpp"
+#include "LFRingbuffer.hpp"
+
+/**
+ * - - - - - - - - - - - - - - -
+ *
+ * Module HCIHandler:
+ *
+ * - BT Core Spec v5.2: Vol 4, Part E Host Controller Interface (HCI)
+ */
+namespace direct_bt {
+
+ /**
+ * A thread safe singleton handler of the HCI control channel to one controller (BT adapter)
+ * <p>
+ * Implementation utilizes a lock free ringbuffer receiving data within its separate thread.
+ * </p>
+ */
+ class HCIHandler {
+ public:
+ enum Defaults : int {
+ HCI_MAX_MTU = static_cast<uint8_t>(HCIConstU8::PACKET_MAX_SIZE),
+
+ /** 10s poll timeout for HCI reader thread */
+ HCI_READER_THREAD_POLL_TIMEOUT = 10000,
+ /** 3s timeout for HCI command replies. This timeout is rather longer, as it may include waiting for pending command complete. */
+ HCI_COMMAND_REPLY_TIMEOUT = 3000,
+ HCI_EVT_RING_CAPACITY = 128,
+ /** Maximum number of packets to wait for until matching a sequential command. Won't block as timeout will limit. */
+ HCI_READ_PACKET_MAX_RETRY = HCI_EVT_RING_CAPACITY
+ };
+
+ static const pid_t pidSelf;
+
+ private:
+ const BTMode btMode;
+ const uint16_t dev_id;
+ POctets rbuffer;
+ HCIComm comm;
+ const int replyTimeoutMS;
+ std::recursive_mutex mtx;
+ uint32_t metaev_filter_mask;
+
+ inline void filter_clear_metaevs() { metaev_filter_mask=0; }
+ inline void filter_all_metaevs() { metaev_filter_mask=0xffff; }
+ inline void filter_set_metaev(HCIMetaEventType mec) { set_bit_uint32(number(mec)-1, metaev_filter_mask); }
+ inline bool filter_test_metaev(HCIMetaEventType mec) { return 0 != test_bit_uint32(number(mec)-1, metaev_filter_mask); }
+
+ LFRingbuffer<std::shared_ptr<HCIEvent>, nullptr> hciEventRing;
+ std::atomic<pthread_t> hciReaderThreadId;
+ std::atomic<bool> hciReaderRunning;
+ std::atomic<bool> hciReaderShallStop;
+
+ void hciReaderThreadImpl();
+
+ bool sendCommand(HCICommand &req);
+ std::shared_ptr<HCIEvent> getNextReply(HCICommand &req, int & retryCount);
+ std::shared_ptr<HCIEvent> sendWithReply(HCICommand &req);
+
+ std::shared_ptr<HCIEvent> sendWithCmdCompleteReply(HCICommand &req, HCICommandCompleteEvent **res);
+ std::shared_ptr<HCIEvent> sendWithCmdStatusReply(HCICommand &req, HCICommandStatusEvent **res);
+
+ template<typename hci_cmd_event_struct>
+ std::shared_ptr<HCIEvent> processCmdCompleteCommand(HCIOpcode opc, const hci_cmd_event_struct **res, HCIErrorCode *status);
+
+ template<typename hci_command_struct, typename hci_cmd_event_struct>
+ std::shared_ptr<HCIEvent> processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIEventType evc, const hci_cmd_event_struct **res,
+ HCIErrorCode *status);
+
+ template<typename hci_command_struct, typename hci_cmd_event_struct>
+ std::shared_ptr<HCIEvent> processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIMetaEventType mec, const hci_cmd_event_struct **res,
+ HCIErrorCode *status);
+
+ HCIHandler(const HCIHandler&) = delete;
+ void operator=(const HCIHandler&) = delete;
+
+ public:
+ HCIHandler(const BTMode btMode, const uint16_t dev_id, const int replyTimeoutMS=Defaults::HCI_COMMAND_REPLY_TIMEOUT);
+
+ /**
+ * Releases this instance after issuing {@link #close()}.
+ */
+ ~HCIHandler() { close(); }
+
+ void close();
+
+ BTMode getBTMode() { return btMode; }
+
+ /** Returns true if this mgmt instance is open and hence valid, otherwise false */
+ bool isOpen() const {
+ return comm.isOpen();
+ }
+
+ std::string toString() const { return "HCIHandler[BTMode "+BTModeString(btMode)+", dev_id "+std::to_string(dev_id)+"]"; }
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.3.2 Reset command
+ */
+ HCIErrorCode reset();
+
+ /**
+ * Establish a connection to the given LE peer.
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command
+ * </p>
+ * <p>
+ * Even if not utilizing a HCI channel, it has been observed that maintaining such
+ * enhanced performance on subsequent communication, i.e. GATT over L2CAP.
+ * </p>
+ * <p>
+ * Set window to the same value as the interval, enables continuous scanning.
+ * </p>
+ *
+ * @param handle_return
+ * @param peer_bdaddr
+ * @param peer_mac_type
+ * @param own_mac_type
+ * @param le_scan_interval in units of 0.625ms, default value 48 for 30ms, min value 4 for 2.5ms -> 0x4000 for 10.24s
+ * @param le_scan_window in units of 0.625ms, default value 48 for 30ms, min value 4 for 2.5ms -> 0x4000 for 10.24s. Shall be <= le_scan_interval
+ * @param conn_interval_min in units of 1.25ms, default value 15 for 19.75ms
+ * @param conn_interval_max in units of 1.25ms, default value 15 for 19.75ms
+ * @param conn_latency slave latency in units of connection events, default value 0
+ * @param supervision_timeout in units of 10ms, default value 1000 for 10000ms or 10s.
+ * @return
+ */
+ HCIErrorCode le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
+ const HCIAddressType peer_mac_type=HCIAddressType::HCIADDR_LE_PUBLIC,
+ const HCIAddressType own_mac_type=HCIAddressType::HCIADDR_LE_PUBLIC,
+ const uint16_t le_scan_interval=48, const uint16_t le_scan_window=48,
+ const uint16_t conn_interval_min=0x000F, const uint16_t conn_interval_max=0x000F,
+ const uint16_t conn_latency=0x0000, const uint16_t supervision_timeout=number(HCIConstInt::LE_CONN_TIMEOUT_MS)/10);
+
+ /**
+ * Establish a connection to the given BREDR (non LE).
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command
+ * </p>
+ */
+ HCIErrorCode create_conn(uint16_t * handle_return, const EUI48 &bdaddr,
+ const uint16_t pkt_type=HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5,
+ const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01);
+
+ /**
+ * Disconnect an established connection.
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command
+ * </p>
+ */
+ HCIErrorCode disconnect(const uint16_t conn_handle, const HCIErrorCode reason=HCIErrorCode::REMOTE_USER_TERMINATED_CONNECTION);
+ };
+
+} // namespace direct_bt
+
+#endif /* DBT_HANDLER_HPP_ */
+
diff --git a/api/direct_bt/HCITypes.hpp b/api/direct_bt/HCITypes.hpp
new file mode 100644
index 00000000..e07783f2
--- /dev/null
+++ b/api/direct_bt/HCITypes.hpp
@@ -0,0 +1,815 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef HCI_TYPES_HPP_
+#define HCI_TYPES_HPP_
+
+#include <cstring>
+#include <string>
+#include <cstdint>
+
+#include <mutex>
+
+#include "BTTypes.hpp"
+#include "BTIoctl.hpp"
+#include "OctetTypes.hpp"
+#include "HCIIoctl.hpp"
+
+/**
+ * - - - - - - - - - - - - - - -
+ *
+ * Module HCITypes:
+ *
+ * - BT Core Spec v5.2: Vol 4, Part E Host Controller Interface (HCI): 7 HCI commands and events
+ *
+ */
+namespace direct_bt {
+
+ class HCIException : public RuntimeException {
+ protected:
+ HCIException(std::string const type, std::string const m, const char* file, int line) noexcept
+ : RuntimeException(type, m, file, line) {}
+
+ public:
+ HCIException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("HCIException", m, file, line) {}
+ };
+
+ class HCIPacketException : public HCIException {
+ public:
+ HCIPacketException(std::string const m, const char* file, int line) noexcept
+ : HCIException("HCIPacketException", m, file, line) {}
+ };
+ class HCIOpcodeException : public HCIException {
+ public:
+ HCIOpcodeException(std::string const m, const char* file, int line) noexcept
+ : HCIException("HCIOpcodeException", m, file, line) {}
+ };
+
+ enum class HCIConstInt : int {
+ /** 3s poll timeout for complete HCI replies */
+ TO_SEND_REQ_POLL_MS = 3000,
+ /** 10s le connection timeout, supervising max is 32s (v5.2 Vol 4, Part E - 7.8.12) */
+ LE_CONN_TIMEOUT_MS = 10000
+ };
+ inline int number(const HCIConstInt rhs) {
+ return static_cast<int>(rhs);
+ }
+
+ enum class HCIConstU16 : uint16_t {
+ INDEX_NONE = 0xFFFF,
+ /* Net length w/o null-termination */
+ MAX_NAME_LENGTH = 248,
+ MAX_SHORT_NAME_LENGTH = 10,
+ MAX_AD_LENGTH = 31
+ };
+ inline uint16_t number(const HCIConstU16 rhs) {
+ return static_cast<uint16_t>(rhs);
+ }
+
+ /**
+ * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes
+ * <p>
+ * BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 2 Error code descriptions
+ * </p>
+ */
+ enum class HCIErrorCode : uint8_t {
+ SUCCESS = 0x00,
+ UNKNOWN_HCI_COMMAND = 0x01,
+ UNKNOWN_CONNECTION_IDENTIFIER = 0x02,
+ HARDWARE_FAILURE = 0x03,
+ PAGE_TIMEOUT = 0x04,
+ AUTHENTICATION_FAILURE = 0x05,
+ PIN_OR_KEY_MISSING = 0x06,
+ MEMORY_CAPACITY_EXCEEDED = 0x07,
+ CONNECTION_TIMEOUT = 0x08,
+ CONNECTION_LIMIT_EXCEEDED = 0x09,
+ SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED = 0x0a,
+ CONNECTION_ALREADY_EXISTS = 0x0b,
+ COMMAND_DISALLOWED = 0x0c,
+ CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0d,
+ CONNECTION_REJECTED_SECURITY = 0x0e,
+ CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0f,
+ CONNECTION_ACCEPT_TIMEOUT_EXCEEDED = 0x10,
+ UNSUPPORTED_FEATURE_OR_PARAM_VALUE = 0x11,
+ INVALID_HCI_COMMAND_PARAMETERS = 0x12,
+ REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+ CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
+ REPEATED_ATTEMPTS = 0x17,
+ PAIRING_NOT_ALLOWED = 0x18,
+ UNKNOWN_LMP_PDU = 0x19,
+ UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1a,
+ SCO_OFFSET_REJECTED = 0x1b,
+ SCO_INTERVAL_REJECTED = 0x1c,
+ SCO_AIR_MODE_REJECTED = 0x1d,
+ INVALID_LMP_OR_LL_PARAMETERS = 0x1e,
+ UNSPECIFIED_ERROR = 0x1f,
+ UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE = 0x20,
+ ROLE_CHANGE_NOT_ALLOWED = 0x21,
+ LMP_OR_LL_RESPONSE_TIMEOUT = 0x22,
+ LMP_OR_LL_COLLISION = 0x23,
+ LMP_PDU_NOT_ALLOWED = 0x24,
+ ENCRYPTION_MODE_NOT_ACCEPTED = 0x25,
+ LINK_KEY_CANNOT_BE_CHANGED = 0x26,
+ REQUESTED_QOS_NOT_SUPPORTED = 0x27,
+ INSTANT_PASSED = 0x28,
+ PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29,
+ DIFFERENT_TRANSACTION_COLLISION = 0x2a,
+ QOS_UNACCEPTABLE_PARAMETER = 0x2c,
+ QOS_REJECTED = 0x2d,
+ CHANNEL_ASSESSMENT_NOT_SUPPORTED = 0x2e,
+ INSUFFICIENT_SECURITY = 0x2f,
+ PARAMETER_OUT_OF_RANGE = 0x30,
+ ROLE_SWITCH_PENDING = 0x32,
+ RESERVED_SLOT_VIOLATION = 0x34,
+ ROLE_SWITCH_FAILED = 0x35,
+ EIR_TOO_LARGE = 0x36,
+ SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x37,
+ HOST_BUSY_PAIRING = 0x38,
+ CONNECTION_REJECTED_NO_SUITABLE_CHANNEL = 0x39,
+ CONTROLLER_BUSY = 0x3a,
+ UNACCEPTABLE_CONNECTION_PARAM = 0x3b,
+ ADVERTISING_TIMEOUT = 0x3c,
+ CONNECTION_TERMINATED_MIC_FAILURE = 0x3d,
+ CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT = 0x3e,
+ MAX_CONNECTION_FAILED = 0x3f,
+ COARSE_CLOCK_ADJ_REJECTED = 0x40,
+ TYPE0_SUBMAP_NOT_DEFINED = 0x41,
+ UNKNOWN_ADVERTISING_IDENTIFIER = 0x42,
+ LIMIT_REACHED = 0x43,
+ OPERATION_CANCELLED_BY_HOST = 0x44,
+ PACKET_TOO_LONG = 0x45,
+
+ INTERNAL_FAILURE = 0xfe,
+ UNKNOWN = 0xff
+ };
+ inline uint8_t number(const HCIErrorCode rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+ std::string getHCIErrorCodeString(const HCIErrorCode ec);
+
+ enum class HCIConstU8 : uint8_t {
+ /** HCIPacketType::COMMAND header size including HCIPacketType */
+ COMMAND_HDR_SIZE = 1+3,
+ /** HCIPacketType::ACLDATA header size including HCIPacketType */
+ ACL_HDR_SIZE = 1+4,
+ /** HCIPacketType::SCODATA header size including HCIPacketType */
+ SCO_HDR_SIZE = 1+3,
+ /** HCIPacketType::EVENT header size including HCIPacketType */
+ EVENT_HDR_SIZE = 1+2,
+ /** Total packet size, guaranteed to be handled by adapter. */
+ PACKET_MAX_SIZE = 255
+ };
+ inline uint8_t number(const HCIConstU8 rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+
+ enum class HCIPacketType : uint8_t {
+ COMMAND = 0x01,
+ ACLDATA = 0x02,
+ SCODATA = 0x03,
+ EVENT = 0x04,
+ DIAG = 0xf0,
+ VENDOR = 0xff
+ };
+ inline uint8_t number(const HCIPacketType rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+ std::string getHCIPacketTypeString(const HCIPacketType op);
+
+ enum class HCIOGF : uint8_t {
+ /** link control commands */
+ LINK_CTL = 0x01,
+ /** link policy commands */
+ LINK_POLICY = 0x02,
+ /** controller baseband commands */
+ BREDR_CTL = 0x03,
+ /** LE controller commands */
+ LE_CTL = 0x08
+ };
+ inline uint8_t number(const HCIOGF rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+ std::string getHCIOGFString(const HCIOGF op);
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7 Events
+ */
+ enum class HCIEventType : uint8_t {
+ INVALID = 0x00,
+ INQUIRY_COMPLETE = 0x01,
+ INQUIRY_RESULT = 0x02,
+ CONN_COMPLETE = 0x03,
+ CONN_REQUEST = 0x04,
+ DISCONN_COMPLETE = 0x05,
+ AUTH_COMPLETE = 0x06,
+ REMOTE_NAME = 0x07,
+ ENCRYPT_CHANGE = 0x08,
+ CHANGE_LINK_KEY_COMPLETE = 0x09,
+ REMOTE_FEATURES = 0x0b,
+ REMOTE_VERSION = 0x0c,
+ QOS_SETUP_COMPLETE = 0x0d,
+ CMD_COMPLETE = 0x0e,
+ CMD_STATUS = 0x0f,
+ HARDWARE_ERROR = 0x10,
+ ROLE_CHANGE = 0x12,
+ NUM_COMP_PKTS = 0x13,
+ MODE_CHANGE = 0x14,
+ PIN_CODE_REQ = 0x16,
+ LINK_KEY_REQ = 0x17,
+ LINK_KEY_NOTIFY = 0x18,
+ CLOCK_OFFSET = 0x1c,
+ PKT_TYPE_CHANGE = 0x1d,
+ DISCONN_PHY_LINK_COMPLETE = 0x42,
+ DISCONN_LOGICAL_LINK_COMPLETE = 0x46,
+ LE_META = 0x3e,
+ AMP_Receiver_Report = 0x4b
+
+ // etc etc - incomplete
+ };
+ inline uint8_t number(const HCIEventType rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+ std::string getHCIEventTypeString(const HCIEventType op);
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event
+ */
+ enum class HCIMetaEventType : uint8_t {
+ INVALID = 0x00,
+ LE_CONN_COMPLETE = 0x01,/**< LE_CONN_COMPLETE */
+ LE_ADVERTISING_REPORT = 0x02,/**< LE_ADVERTISING_REPORT */
+ LE_CONN_UPDATE_COMPLETE = 0x03,/**< LE_CONN_UPDATE_COMPLETE */
+ LE_REMOTE_FEAT_COMPLETE = 0x04,/**< LE_REMOTE_FEAT_COMPLETE */
+ LE_LTKEY_REQUEST = 0x05,/**< LE_LTKEY_REQUEST */
+ LE_REMOTE_CONN_PARAM_REQ = 0x06,/**< LE_REMOTE_CONN_PARAM_REQ */
+ 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_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 */
+ LE_PERIODIC_ADV_SYNC_ESTABLISHED = 0x0E,/**< LE_PERIODIC_ADV_SYNC_ESTABLISHED */
+ LE_PERIODIC_ADV_REPORT = 0x0F,/**< LE_PERIODIC_ADV_REPORT */
+ LE_PERIODIC_ADV_SYNC_LOST = 0x10,/**< LE_PERIODIC_ADV_SYNC_LOST */
+ LE_SCAN_TIMEOUT = 0x11,/**< LE_SCAN_TIMEOUT */
+ LE_ADV_SET_TERMINATED = 0x12,/**< LE_ADV_SET_TERMINATED */
+ LE_SCAN_REQ_RECEIVED = 0x13,/**< LE_SCAN_REQ_RECEIVED */
+ LE_CHANNEL_SEL_ALGO = 0x14,/**< LE_CHANNEL_SEL_ALGO */
+ LE_CONNLESS_IQ_REPORT = 0x15,/**< LE_CONNLESS_IQ_REPORT */
+ LE_CONN_IQ_REPORT = 0x16,/**< LE_CONN_IQ_REPORT */
+ LE_CTE_REQ_FAILED = 0x17,/**< LE_CTE_REQ_FAILED */
+ LE_PERIODIC_ADV_SYNC_TRANSFER_RECV = 0x18,/**< LE_PERIODIC_ADV_SYNC_TRANSFER_RECV */
+ LE_CIS_ESTABLISHED = 0x19,/**< LE_CIS_ESTABLISHED */
+ LE_CIS_REQUEST = 0x1A,/**< LE_CIS_REQUEST */
+ LE_CREATE_BIG_COMPLETE = 0x1B,/**< LE_CREATE_BIG_COMPLETE */
+ LE_TERMINATE_BIG_COMPLETE = 0x1C,/**< LE_TERMINATE_BIG_COMPLETE */
+ LE_BIG_SYNC_ESTABLISHED = 0x1D,/**< LE_BIG_SYNC_ESTABLISHED */
+ LE_BIG_SYNC_LOST = 0x1E,/**< LE_BIG_SYNC_LOST */
+ LE_REQUEST_PEER_SCA_COMPLETE = 0x1F,/**< LE_REQUEST_PEER_SCA_COMPLETE */
+ LE_PATH_LOSS_THRESHOLD = 0x20,/**< LE_PATH_LOSS_THRESHOLD */
+ LE_TRANSMIT_POWER_REPORTING = 0x21,/**< LE_TRANSMIT_POWER_REPORTING */
+ LE_BIGINFO_ADV_REPORT = 0x22 /**< LE_BIGINFO_ADV_REPORT */
+ };
+ inline uint8_t number(const HCIMetaEventType rhs) {
+ return static_cast<uint8_t>(rhs);
+ }
+ std::string getHCIMetaEventTypeString(const HCIMetaEventType op);
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1 Link Controller commands
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.3 Controller & Baseband commands
+ * </p>
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.4 Informational paramters
+ * </p>
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8 LE Controller commands
+ * </p>
+ */
+ enum class HCIOpcode : uint16_t {
+ SPECIAL = 0x0000,/**< SPECIAL */
+ CREATE_CONN = 0x0405,
+ DISCONNECT = 0x0406,
+ SET_EVENT_MASK = 0x0C01,/**< SET_EVENT_MASK */
+ RESET = 0x0C03,
+ READ_LOCAL_VERSION = 0x1001,
+ LE_SET_EVENT_MASK = 0x2001,/**< LE_SET_EVENT_MASK */
+ LE_READ_BUFFER_SIZE = 0x2002,
+ LE_READ_LOCAL_FEATURES = 0x2003,
+ LE_SET_RANDOM_ADDR = 0x2005,
+ LE_SET_ADV_PARAM = 0x2006,
+ LE_READ_ADV_TX_POWER = 0x2007,
+ LE_SET_ADV_DATA = 0x2008,
+ LE_SET_SCAN_RSP_DATA = 0x2009,
+ LE_SET_ADV_ENABLE = 0x200a,
+ LE_SET_SCAN_PARAM = 0x200b,
+ LE_SET_SCAN_ENABLE = 0x200c,
+ LE_CREATE_CONN = 0x200d, /**< LE_CREATE_CONN */
+ LE_CREATE_CONN_CANCEL = 0x200e,
+ LE_READ_WHITE_LIST_SIZE = 0x200f,
+ LE_CLEAR_WHITE_LIST = 0x2010,
+ LE_ADD_TO_WHITE_LIST = 0x2011,
+ LE_DEL_FROM_WHITE_LIST = 0x2012,
+ LE_CONN_UPDATE = 0x2013,
+ LE_READ_REMOTE_FEATURES = 0x2016,
+ LE_START_ENC = 0x2019
+ // etc etc - incomplete
+ };
+ inline uint16_t number(const HCIOpcode rhs) {
+ return static_cast<uint16_t>(rhs);
+ }
+ std::string getHCIOpcodeString(const HCIOpcode op);
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 5.4 Exchange of HCI-specific information
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.1 HCI Command packet
+ * </p>
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.4 HCI Event packet
+ * </p>
+ * <pre>
+ * </pre>
+ */
+ class HCIPacket
+ {
+ protected:
+ POctets pdu;
+
+ inline static void checkPacketType(const HCIPacketType type) {
+ switch(type) {
+ case HCIPacketType::COMMAND:
+ case HCIPacketType::ACLDATA:
+ case HCIPacketType::SCODATA:
+ case HCIPacketType::EVENT:
+ case HCIPacketType::DIAG:
+ case HCIPacketType::VENDOR:
+ return; // OK
+ default:
+ throw HCIPacketException("Unsupported packet type "+uint8HexString(number(type)), E_FILE_LINE);
+ }
+ }
+
+ public:
+ HCIPacket(const HCIPacketType type, const uint8_t total_packet_size)
+ : pdu(total_packet_size)
+ {
+ pdu.put_uint8 (0, number(type));
+ }
+ HCIPacket(const uint8_t *packet_data, const uint8_t total_packet_size)
+ : pdu(total_packet_size)
+ {
+ if( total_packet_size > 0 ) {
+ memcpy(pdu.get_wptr(), packet_data, total_packet_size);
+ }
+ checkPacketType(getPacketType());
+ }
+ virtual ~HCIPacket() {}
+
+ int getTotalSize() const { return pdu.getSize(); }
+
+ /** Return the underlying octets read only */
+ TROOctets & getPDU() { return pdu; }
+
+ HCIPacketType getPacketType() { return static_cast<HCIPacketType>(pdu.get_uint8(0)); }
+
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.1 HCI Command packet
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8 LE Controller Commands
+ * </p>
+ * <pre>
+ __le16 opcode; // OCF & OGF
+ __u8 plen;
+ * </pre>
+ */
+ class HCICommand : public HCIPacket
+ {
+ protected:
+ inline static void checkOpcode(const HCIOpcode has, const HCIOpcode min, const HCIOpcode max)
+ {
+ if( has < min || has > max ) {
+ throw HCIOpcodeException("Has opcode "+uint16HexString(number(has))+
+ ", not within range ["+uint16HexString(number(min))+
+ ".."+uint16HexString(number(max))+"]", E_FILE_LINE);
+ }
+ }
+
+ virtual std::string baseString() const {
+ return "opcode="+uint16HexString(number(getOpcode()))+" "+getOpcodeString();
+ }
+ virtual std::string valueString() const {
+ const int psz = getParamSize();
+ const std::string ps = psz > 0 ? bytesHexString(getParam(), 0, psz, true /* lsbFirst */, true /* leading0X */) : "";
+ return "param[size "+std::to_string(getParamSize())+", data "+ps+"], tsz "+std::to_string(getTotalSize());
+ }
+
+ public:
+
+ HCICommand(const HCIOpcode opc, const uint8_t param_size)
+ : HCIPacket(HCIPacketType::COMMAND, number(HCIConstU8::COMMAND_HDR_SIZE)+param_size)
+ {
+ checkOpcode(opc, HCIOpcode::SPECIAL, HCIOpcode::LE_START_ENC);
+
+ pdu.put_uint16(1, static_cast<uint16_t>(opc));
+ pdu.put_uint8(3, param_size);
+ }
+
+ HCICommand(const HCIOpcode opc, const uint16_t param_size, const uint8_t* param)
+ : HCICommand(opc, param_size)
+ {
+ if( param_size > 0 ) {
+ memcpy(pdu.get_wptr(number(HCIConstU8::COMMAND_HDR_SIZE)), param, param_size);
+ }
+ }
+ virtual ~HCICommand() {}
+
+ HCIOpcode getOpcode() const { return static_cast<HCIOpcode>( pdu.get_uint16(1) ); }
+ std::string getOpcodeString() const { return getHCIOpcodeString(getOpcode()); }
+ uint8_t getParamSize() const { return pdu.get_uint8(3); }
+ const uint8_t* getParam() const { return pdu.get_ptr(number(HCIConstU8::COMMAND_HDR_SIZE)); }
+
+ std::string toString() const {
+ return "HCICommand["+baseString()+", "+valueString()+"]";
+ }
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command
+ * <pre>
+ Size 3
+ __le16 handle;
+ __u8 reason;
+ * </pre>
+ */
+ class HCIDisconnectCmd : public HCICommand
+ {
+ public:
+ HCIDisconnectCmd(const uint16_t handle, HCIErrorCode reason)
+ : HCICommand(HCIOpcode::DISCONNECT, 3)
+ {
+ pdu.put_uint16(number(HCIConstU8::COMMAND_HDR_SIZE),handle);
+ pdu.put_uint8(number(HCIConstU8::COMMAND_HDR_SIZE)+2, number(reason));
+ }
+ };
+
+ /**
+ * Generic HCICommand wrapper for any HCI IOCTL structure
+ * @tparam hcistruct the template typename, e.g. 'hci_cp_create_conn' for 'struct hci_cp_create_conn'
+ */
+ template<typename hcistruct>
+ class HCIStructCommand : public HCICommand
+ {
+ public:
+ HCIStructCommand(HCIOpcode opc, const hcistruct &cp)
+ : HCICommand(opc, sizeof(hcistruct), (const uint8_t *)(&cp))
+ {
+ }
+ const hcistruct * getStruct() const { return (const hcistruct *)(getParam()); }
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.4 HCI Event packet
+ * <p>
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7 Events
+ * </p>
+ * <pre>
+ __u8 evt;
+ __u8 plen;
+ * </pre>
+ */
+ class HCIEvent : public HCIPacket
+ {
+ protected:
+ uint64_t ts_creation;
+
+ inline static void checkEventType(const HCIEventType has, const HCIEventType min, const HCIEventType max)
+ {
+ if( has < min || has > max ) {
+ throw HCIOpcodeException("Has evcode "+uint8HexString(number(has))+
+ ", not within range ["+uint8HexString(number(min))+
+ ".."+uint8HexString(number(max))+"]", E_FILE_LINE);
+ }
+ }
+ inline static void checkEventType(const HCIEventType has, const HCIEventType exp)
+ {
+ if( has != exp ) {
+ throw HCIOpcodeException("Has evcode "+uint8HexString(number(has))+
+ ", not matching "+uint8HexString(number(exp)), E_FILE_LINE);
+ }
+ }
+
+ virtual std::string baseString() const {
+ return "event="+uint8HexString(number(getEventType()))+" "+getEventTypeString();
+ }
+ virtual std::string valueString() const {
+ const int d_sz = getParamSize();
+ const std::string d_str = d_sz > 0 ? bytesHexString(getParam(), 0, d_sz, true /* lsbFirst */, true /* leading0X */) : "";
+ return "data[size "+std::to_string(d_sz)+", data "+d_str+"], tsz "+std::to_string(getTotalSize());
+ }
+
+ public:
+
+ /**
+ * Return a newly created specialized instance pointer to base class.
+ * <p>
+ * Returned memory reference is managed by caller (delete etc)
+ * </p>
+ */
+ static HCIEvent* getSpecialized(const uint8_t * buffer, int const buffer_size);
+
+ /** Persistent memory, w/ ownership ..*/
+ HCIEvent(const uint8_t* buffer, const int buffer_len)
+ : HCIPacket(buffer, buffer_len), ts_creation(getCurrentMilliseconds())
+ {
+ checkEventType(getEventType(), HCIEventType::INQUIRY_COMPLETE, HCIEventType::AMP_Receiver_Report);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+getParamSize());
+ }
+ HCIEvent(const HCIEventType evt, const uint16_t param_size=0)
+ : HCIPacket(HCIPacketType::EVENT, number(HCIConstU8::EVENT_HDR_SIZE)+param_size), ts_creation(getCurrentMilliseconds())
+ {
+ checkEventType(evt, HCIEventType::INQUIRY_COMPLETE, HCIEventType::AMP_Receiver_Report);
+ pdu.put_uint8(1, number(evt));
+ pdu.put_uint8(2, param_size);
+ }
+ HCIEvent(const HCIEventType evt, const uint8_t* param, const uint16_t param_size)
+ : HCIPacket(HCIPacketType::EVENT, number(HCIConstU8::EVENT_HDR_SIZE)+param_size), ts_creation(getCurrentMilliseconds())
+ {
+ checkEventType(evt, HCIEventType::INQUIRY_COMPLETE, HCIEventType::AMP_Receiver_Report);
+ pdu.put_uint8(1, number(evt));
+ pdu.put_uint8(2, param_size);
+ if( param_size > 0 ) {
+ memcpy(pdu.get_wptr(number(HCIConstU8::EVENT_HDR_SIZE)), param, param_size);
+ }
+ }
+ virtual ~HCIEvent() {}
+
+ uint64_t getTimestamp() const { return ts_creation; }
+
+ HCIEventType getEventType() const { return static_cast<HCIEventType>( pdu.get_uint8(1) ); }
+ std::string getEventTypeString() const { return getHCIEventTypeString(getEventType()); }
+ bool isEvent(HCIEventType t) const { return t == getEventType(); }
+
+ /**
+ * The meta subevent type
+ */
+ virtual HCIMetaEventType getMetaEventType() const { return HCIMetaEventType::INVALID; }
+ std::string getMetaEventTypeString() const { return getHCIMetaEventTypeString(getMetaEventType()); }
+ bool isMetaEvent(HCIMetaEventType t) const { return t == getMetaEventType(); }
+
+ uint8_t getParamSize() const { return pdu.get_uint8(2); }
+ const uint8_t* getParam() const { return pdu.get_ptr(number(HCIConstU8::EVENT_HDR_SIZE)); }
+
+ virtual bool validate(const HCICommand & cmd) const { (void)cmd; return true; }
+
+ std::string toString() const {
+ return "HCIEvent["+baseString()+", "+valueString()+"]";
+ }
+ };
+
+ /**
+ * Generic HCIEvent wrapper for any HCI IOCTL 'command complete' alike event struct having a HCIErrorCode uint8_t status field.
+ * @tparam hcistruct the template typename, e.g. 'hci_ev_conn_complete' for 'struct hci_ev_conn_complete'
+ */
+ template<typename hcistruct>
+ class HCIStructCmdCompleteEvt : public HCIEvent
+ {
+ public:
+ HCIStructCmdCompleteEvt(const HCIEventType ec, const uint8_t* buffer, const int buffer_len)
+ : HCIEvent(buffer, buffer_len)
+ {
+ checkEventType(getEventType(), ec);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+sizeof(hcistruct));
+ }
+ bool isTypeAndSizeValid(const HCIEventType ec) const {
+ return isEvent(ec) &&
+ pdu.is_range_valid(0, number(HCIConstU8::EVENT_HDR_SIZE)+sizeof(hcistruct));
+ }
+ const hcistruct * getStruct() const { return (const hcistruct *)(getParam()); }
+ HCIErrorCode getStatus() const { return static_cast<HCIErrorCode>( getStruct()->status ); }
+ };
+
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.5 Disconnection Complete event
+ * <p>
+ * Size 4
+ __u8 status;
+ __le16 handle;
+ __u8 reason;
+ * </p>
+ */
+ class HCIDisconnectionCompleteEvent : public HCIEvent
+ {
+ protected:
+ std::string baseString() const override {
+ return HCIEvent::baseString()+
+ ", status "+uint8HexString(static_cast<uint8_t>(getStatus()), true)+" "+getHCIErrorCodeString(getStatus())+
+ ", handle "+std::to_string(getHandle())+
+ ", reason "+uint8HexString(static_cast<uint8_t>(getReason()), true)+" "+getHCIErrorCodeString(getReason());
+ }
+
+ public:
+ HCIDisconnectionCompleteEvent(const uint8_t* buffer, const int buffer_len)
+ : HCIEvent(buffer, buffer_len)
+ {
+ checkEventType(getEventType(), HCIEventType::DISCONN_COMPLETE);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+4);
+ }
+
+ HCIErrorCode getStatus() const { return static_cast<HCIErrorCode>( pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)) ); }
+ uint16_t getHandle() const { return pdu.get_uint16(number(HCIConstU8::EVENT_HDR_SIZE)+1); }
+ HCIErrorCode getReason() const { return static_cast<HCIErrorCode>( pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)+3) ); }
+
+ bool validate(const HCICommand & cmd) const override {
+ return cmd.getOpcode() == HCIOpcode::DISCONNECT;
+ }
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.14 Command Complete event
+ * <p>
+ * Size 3 + return size
+ __u8 ncmd;
+ __le16 opcode;
+ Return_Paramters of variable length, usually with '__u8 status' first.
+ * </p>
+ */
+ class HCICommandCompleteEvent : public HCIEvent
+ {
+ protected:
+ std::string baseString() const override {
+ return HCIEvent::baseString()+", opcode="+uint16HexString(static_cast<uint16_t>(getOpcode()))+
+ " "+getHCIOpcodeString(getOpcode())+
+ ", ncmd "+std::to_string(getNumCommandPackets());
+ }
+
+ public:
+ HCICommandCompleteEvent(const uint8_t* buffer, const int buffer_len)
+ : HCIEvent(buffer, buffer_len)
+ {
+ checkEventType(getEventType(), HCIEventType::CMD_COMPLETE);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+3);
+ }
+
+ /**
+ * The Number of HCI Command packets which are allowed to be sent to the Controller from the Host.
+ * <p>
+ * Range: 0 to 255
+ * </p>
+ */
+ uint8_t getNumCommandPackets() const { return pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)+0); }
+
+ /**
+ * The associated command
+ */
+ HCIOpcode getOpcode() const { return static_cast<HCIOpcode>( pdu.get_uint16(number(HCIConstU8::EVENT_HDR_SIZE)+1) ); }
+
+ uint8_t getReturnParamSize() const { return getParamSize() - 3; }
+ const uint8_t* getReturnParam() const { return pdu.get_ptr(number(HCIConstU8::EVENT_HDR_SIZE)+3); }
+ HCIErrorCode getReturnStatus(const int returnParamOffset=0) const {
+ const uint8_t returnParamSize = getReturnParamSize();
+ if( returnParamSize < returnParamOffset + 1 /* status size */ ) {
+ return HCIErrorCode::UNKNOWN;
+ }
+ return static_cast<HCIErrorCode>( pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE) + 3 + returnParamOffset) );
+ }
+
+ bool validate(const HCICommand & cmd) const override {
+ return cmd.getOpcode() == getOpcode();
+ }
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.15 Command Status event
+ * <p>
+ * Size 4
+ __u8 status;
+ __u8 ncmd;
+ __le16 opcode;
+ * </p>
+ */
+ class HCICommandStatusEvent : public HCIEvent
+ {
+ protected:
+ std::string baseString() const override {
+ return HCIEvent::baseString()+", opcode="+uint16HexString(static_cast<uint16_t>(getOpcode()))+
+ " "+getHCIOpcodeString(getOpcode())+
+ ", ncmd "+std::to_string(getNumCommandPackets())+
+ ", status "+uint8HexString(static_cast<uint8_t>(getStatus()), true)+" "+getHCIErrorCodeString(getStatus());
+ }
+
+ public:
+ HCICommandStatusEvent(const uint8_t* buffer, const int buffer_len)
+ : HCIEvent(buffer, buffer_len)
+ {
+ checkEventType(getEventType(), HCIEventType::CMD_STATUS);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+4);
+ }
+
+ HCIErrorCode getStatus() const { return static_cast<HCIErrorCode>( pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)) ); }
+
+ /**
+ * The Number of HCI Command packets which are allowed to be sent to the Controller from the Host.
+ * <p>
+ * Range: 0 to 255
+ * </p>
+ */
+ uint8_t getNumCommandPackets() const { return pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)+1); }
+
+ /**
+ * The associated command
+ */
+ HCIOpcode getOpcode() const { return static_cast<HCIOpcode>( pdu.get_uint16(number(HCIConstU8::EVENT_HDR_SIZE)+1+1) ); }
+
+ bool validate(const HCICommand & cmd) const override {
+ return cmd.getOpcode() == getOpcode();
+ }
+ };
+
+ /**
+ * BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event
+ * <p>
+ * Size 1
+ __u8 subevent;
+ * </p>
+ */
+ class HCIMetaEvent : public HCIEvent
+ {
+ protected:
+ static void checkMetaType(const HCIMetaEventType has, const HCIMetaEventType exp)
+ {
+ if( has != exp ) {
+ throw HCIOpcodeException("Has meta "+uint8HexString(number(has))+
+ ", not matching "+uint8HexString(number(exp)), E_FILE_LINE);
+ }
+ }
+
+ virtual std::string baseString() const override {
+ return "event="+uint8HexString(number(getMetaEventType()))+" "+getMetaEventTypeString()+" (le-meta)";
+ }
+
+ public:
+ HCIMetaEvent(const uint8_t* buffer, const int buffer_len)
+ : HCIEvent(buffer, buffer_len)
+ {
+ checkEventType(getEventType(), HCIEventType::LE_META);
+ }
+
+ HCIMetaEventType getMetaEventType() const override { return static_cast<HCIMetaEventType>( pdu.get_uint8(number(HCIConstU8::EVENT_HDR_SIZE)) ); }
+ };
+
+ /**
+ * Generic HCIMetaEvent wrapper for any HCI IOCTL 'command complete' alike meta event struct having a HCIErrorCode uint8_t status field.
+ * @tparam hcistruct the template typename, e.g. 'hci_ev_le_conn_complete' for 'struct hci_ev_le_conn_complete'
+ */
+ template<typename hcistruct>
+ class HCIStructCmdCompleteMetaEvt : public HCIMetaEvent
+ {
+ public:
+ HCIStructCmdCompleteMetaEvt(const HCIMetaEventType mc, const uint8_t* buffer, const int buffer_len)
+ : HCIMetaEvent(buffer, buffer_len)
+ {
+ checkMetaType(getMetaEventType(), mc);
+ pdu.check_range(0, number(HCIConstU8::EVENT_HDR_SIZE)+1+sizeof(hcistruct));
+ }
+ bool isTypeAndSizeValid(const HCIMetaEventType mc) const {
+ return isMetaEvent(mc) &&
+ pdu.is_range_valid(0, number(HCIConstU8::EVENT_HDR_SIZE)+1+sizeof(hcistruct));
+ }
+ const hcistruct * getStruct() const { return (const hcistruct *)( pdu.get_ptr(number(HCIConstU8::EVENT_HDR_SIZE)+1) ); }
+ HCIErrorCode getStatus() const { return static_cast<HCIErrorCode>( getStruct()->status ); }
+ };
+
+} // namespace direct_bt
+
+#endif /* HCI_TYPES_HPP_ */
diff --git a/api/direct_bt/MgmtTypes.hpp b/api/direct_bt/MgmtTypes.hpp
index 818cf5e8..9e0b0d43 100644
--- a/api/direct_bt/MgmtTypes.hpp
+++ b/api/direct_bt/MgmtTypes.hpp
@@ -39,6 +39,8 @@
#include "DBTTypes.hpp"
+#include "FunctionDef.hpp"
+
namespace direct_bt {
class MgmtException : public RuntimeException {
@@ -747,7 +749,7 @@ namespace direct_bt {
protected:
std::string baseString() const override {
- return MgmtEvent::baseString()+", settings="+adapterSettingsToString(getSettings());
+ return MgmtEvent::baseString()+", settings="+getAdapterSettingsString(getSettings());
}
public:
@@ -1142,7 +1144,7 @@ namespace direct_bt {
std::string valueString() const override {
return getAddress().toString()+", version "+std::to_string(getVersion())+
", manuf "+std::to_string(getManufacturer())+
- ", settings[sup "+adapterSettingsToString(getSupportedSetting())+", cur "+adapterSettingsToString(getCurrentSetting())+
+ ", settings[sup "+getAdapterSettingsString(getSupportedSetting())+", cur "+getAdapterSettingsString(getCurrentSetting())+
"], name '"+getName()+"', shortName '"+getShortName()+"'";
}
@@ -1170,6 +1172,43 @@ namespace direct_bt {
};
+ typedef FunctionDef<bool, std::shared_ptr<MgmtEvent>> MgmtEventCallback;
+
+ class MgmtAdapterEventCallback {
+ private:
+ /** Unique adapter index filter or <code>-1</code> to listen for all adapter. */
+ int dev_id;
+ /** MgmtEventCallback instance */
+ MgmtEventCallback callback;
+
+ public:
+ MgmtAdapterEventCallback(int _dev_id, const MgmtEventCallback & _callback)
+ : dev_id(_dev_id), callback(_callback) {}
+
+ MgmtAdapterEventCallback(const MgmtAdapterEventCallback &o) = default;
+ MgmtAdapterEventCallback(MgmtAdapterEventCallback &&o) = default;
+ MgmtAdapterEventCallback& operator=(const MgmtAdapterEventCallback &o) = default;
+ MgmtAdapterEventCallback& operator=(MgmtAdapterEventCallback &&o) = default;
+
+ /** Unique adapter index filter or <code>-1</code> to listen for all adapter. */
+ int getDevID() const { return dev_id; }
+
+ /** MgmtEventCallback reference */
+ MgmtEventCallback& getCallback() { return callback; }
+
+ bool operator==(const MgmtAdapterEventCallback& rhs) const
+ { return dev_id == rhs.dev_id && callback == rhs.callback; }
+
+ bool operator!=(const MgmtAdapterEventCallback& rhs) const
+ { return !(*this == rhs); }
+
+ std::string toString() const {
+ return "MgmtAdapterEventCallback[dev_id "+std::to_string(dev_id)+", "+callback.toString()+"]";
+ }
+ };
+
+ typedef std::vector<MgmtAdapterEventCallback> MgmtAdapterEventCallbackList;
+
} // namespace direct_bt
#endif /* MGMT_TYPES_HPP_ */
diff --git a/api/direct_bt/OctetTypes.hpp b/api/direct_bt/OctetTypes.hpp
index 8664fe82..fd6dae98 100644
--- a/api/direct_bt/OctetTypes.hpp
+++ b/api/direct_bt/OctetTypes.hpp
@@ -84,6 +84,9 @@ namespace direct_bt {
throw IndexOutOfBoundsException(i, count, _size, E_FILE_LINE);
}
}
+ inline bool is_range_valid(const int i, const int count) const {
+ return 0 <= i && i+count <= _size;
+ }
int getSize() const { return _size; }
uint8_t get_uint8(const int i) const {
diff --git a/examples/direct_bt_scanner00/dbt_scanner00.cpp b/examples/direct_bt_scanner00/dbt_scanner00.cpp
index 1c86bce6..56702d2f 100644
--- a/examples/direct_bt_scanner00/dbt_scanner00.cpp
+++ b/examples/direct_bt_scanner00/dbt_scanner00.cpp
@@ -48,9 +48,9 @@ class MyAdapterStatusListener : public AdapterStatusListener {
void adapterSettingsChanged(DBTAdapter const &a, const AdapterSetting oldmask, const AdapterSetting newmask,
const AdapterSetting changedmask, const uint64_t timestamp) override {
fprintf(stderr, "****** Native Adapter SETTINGS_CHANGED: %s -> %s, changed %s\n",
- adapterSettingsToString(oldmask).c_str(),
- adapterSettingsToString(newmask).c_str(),
- adapterSettingsToString(changedmask).c_str());
+ getAdapterSettingsString(oldmask).c_str(),
+ getAdapterSettingsString(newmask).c_str(),
+ getAdapterSettingsString(changedmask).c_str());
fprintf(stderr, "Status DBTAdapter:\n");
fprintf(stderr, "%s\n", a.toString().c_str());
(void)timestamp;
@@ -73,7 +73,7 @@ class MyAdapterStatusListener : public AdapterStatusListener {
(void)timestamp;
}
void deviceUpdated(std::shared_ptr<DBTDevice> device, const EIRDataType updateMask, const uint64_t timestamp) override {
- fprintf(stderr, "****** UPDATED: %s of %s\n", eirDataMaskToString(updateMask).c_str(), device->toString(true).c_str());
+ fprintf(stderr, "****** UPDATED: %s of %s\n", getEIRDataMaskString(updateMask).c_str(), device->toString(true).c_str());
(void)timestamp;
}
void deviceConnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
diff --git a/examples/direct_bt_scanner01/dbt_scanner01.cpp b/examples/direct_bt_scanner01/dbt_scanner01.cpp
index 7050de32..cffbafbe 100644
--- a/examples/direct_bt_scanner01/dbt_scanner01.cpp
+++ b/examples/direct_bt_scanner01/dbt_scanner01.cpp
@@ -47,9 +47,9 @@ class MyAdapterStatusListener : public AdapterStatusListener {
void adapterSettingsChanged(DBTAdapter const &a, const AdapterSetting oldmask, const AdapterSetting newmask,
const AdapterSetting changedmask, const uint64_t timestamp) override {
fprintf(stderr, "****** Native Adapter SETTINGS_CHANGED: %s -> %s, changed %s\n",
- adapterSettingsToString(oldmask).c_str(),
- adapterSettingsToString(newmask).c_str(),
- adapterSettingsToString(changedmask).c_str());
+ getAdapterSettingsString(oldmask).c_str(),
+ getAdapterSettingsString(newmask).c_str(),
+ getAdapterSettingsString(changedmask).c_str());
fprintf(stderr, "Status DBTAdapter:\n");
fprintf(stderr, "%s\n", a.toString().c_str());
(void)timestamp;
@@ -72,7 +72,7 @@ class MyAdapterStatusListener : public AdapterStatusListener {
(void)timestamp;
}
void deviceUpdated(std::shared_ptr<DBTDevice> device, const EIRDataType updateMask, const uint64_t timestamp) override {
- fprintf(stderr, "****** UPDATED: %s of %s\n", eirDataMaskToString(updateMask).c_str(), device->toString(true).c_str());
+ fprintf(stderr, "****** UPDATED: %s of %s\n", getEIRDataMaskString(updateMask).c_str(), device->toString(true).c_str());
(void)timestamp;
}
void deviceConnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
@@ -188,7 +188,7 @@ int main(int argc, char *argv[])
const int64_t t0 = getCurrentMilliseconds();
if( doHCI_Connect ) {
- std::shared_ptr<HCIComm> hci = adapter.openHCI();
+ std::shared_ptr<HCIHandler> hci = adapter.openHCI();
if( nullptr == hci || !hci->isOpen() ) {
fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str());
exit(1);
diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp
index 6ef64543..0161af35 100644
--- a/examples/direct_bt_scanner10/dbt_scanner10.cpp
+++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp
@@ -101,9 +101,9 @@ class MyAdapterStatusListener : public AdapterStatusListener {
void adapterSettingsChanged(DBTAdapter const &a, const AdapterSetting oldmask, const AdapterSetting newmask,
const AdapterSetting changedmask, const uint64_t timestamp) override {
fprintf(stderr, "****** SETTINGS_CHANGED: %s -> %s, changed %s\n",
- adapterSettingsToString(oldmask).c_str(),
- adapterSettingsToString(newmask).c_str(),
- adapterSettingsToString(changedmask).c_str());
+ getAdapterSettingsString(oldmask).c_str(),
+ getAdapterSettingsString(newmask).c_str(),
+ getAdapterSettingsString(changedmask).c_str());
fprintf(stderr, "Status DBTAdapter:\n");
fprintf(stderr, "%s\n", a.toString().c_str());
(void)timestamp;
@@ -137,7 +137,7 @@ class MyAdapterStatusListener : public AdapterStatusListener {
void deviceUpdated(std::shared_ptr<DBTDevice> device, const EIRDataType updateMask, const uint64_t timestamp) override {
if( SHOW_UPDATE_EVENTS ) {
- fprintf(stderr, "****** UPDATED: %s of %s\n", eirDataMaskToString(updateMask).c_str(), device->toString(true).c_str());
+ fprintf(stderr, "****** UPDATED: %s of %s\n", getEIRDataMaskString(updateMask).c_str(), device->toString(true).c_str());
}
(void)timestamp;
}
diff --git a/examples/java/ScannerTinyB10.java b/examples/java/ScannerTinyB10.java
index b7d721a0..74c9497e 100644
--- a/examples/java/ScannerTinyB10.java
+++ b/examples/java/ScannerTinyB10.java
@@ -138,7 +138,7 @@ public class ScannerTinyB10 {
deviceProcessingTask.setDaemon(true); // detach thread
deviceProcessingTask.start();
} else {
- System.err.println("****** CONNECTED-1: NOP %s" + device.toString());
+ System.err.println("****** CONNECTED-1: NOP " + device.toString());
}
}
diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx
index 8da84686..f4d0a21d 100644
--- a/java/jni/direct_bt/DBTAdapter.cxx
+++ b/java/jni/direct_bt/DBTAdapter.cxx
@@ -616,9 +616,9 @@ jobject Java_direct_1bt_tinyb_DBTAdapter_connectDevice(JNIEnv *env, jobject obj,
device->toString().c_str());
}
- std::shared_ptr<direct_bt::HCIComm> hci = adapter->openHCI();
+ std::shared_ptr<direct_bt::HCIHandler> hci = adapter->openHCI();
if( nullptr == hci ) {
- throw BluetoothException("Couldn't get or open adapter's HCIComm "+adapter->toString(), E_FILE_LINE);
+ throw BluetoothException("Couldn't get or open adapter's HCI "+adapter->toString(), E_FILE_LINE);
}
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
diff --git a/src/direct_bt/BTTypes.cpp b/src/direct_bt/BTTypes.cpp
index 522a2448..d4fc1a13 100644
--- a/src/direct_bt/BTTypes.cpp
+++ b/src/direct_bt/BTTypes.cpp
@@ -106,6 +106,15 @@ static inline const int8_t * const_uint8_to_const_int8_ptr(const uint8_t* p) {
return static_cast<const int8_t *>( static_cast<void *>( const_cast<uint8_t*>( p ) ) );
}
+std::string direct_bt::BTModeString(const BTMode v) {
+ switch(v) {
+ case BTMode::BT_MODE_DUAL: return "BT_MODE_DUAL";
+ case BTMode::BT_MODE_BREDR: return "BT_MODE_BREDR";
+ case BTMode::BT_MODE_LE: return "BT_MODE_LE";
+ }
+ return "Unknown BT_MODE";
+}
+
#define APPEARANCECAT_ENUM(X) \
X(UNKNOWN) \
X(GENERIC_PHONE) \
@@ -168,7 +177,7 @@ static inline const int8_t * const_uint8_to_const_int8_ptr(const uint8_t* p) {
#define APPEARANCE_CASE_TO_STRING(V) case AppearanceCat::V: return #V;
-std::string direct_bt::AppearanceCatToString(const AppearanceCat v) {
+std::string direct_bt::getAppearanceCatString(const AppearanceCat v) {
switch(v) {
APPEARANCECAT_ENUM(APPEARANCE_CASE_TO_STRING)
default: ; // fall through intended
@@ -217,7 +226,7 @@ std::string ManufactureSpecificData::toString() const {
X(EIRDataType,DEVICE_ID) \
X(EIRDataType,SERVICE_UUID)
-std::string direct_bt::eirDataBitToString(const EIRDataType bit) {
+std::string direct_bt::getEIRDataBitString(const EIRDataType bit) {
switch(bit) {
EIRDATATYPE_ENUM(CASE2_TO_STRING)
default: ; // fall through intended
@@ -225,7 +234,7 @@ std::string direct_bt::eirDataBitToString(const EIRDataType bit) {
return "Unknown EIRDataType Bit";
}
-std::string direct_bt::eirDataMaskToString(const EIRDataType mask) {
+std::string direct_bt::getEIRDataMaskString(const EIRDataType mask) {
const uint32_t one = 1;
bool has_pre = false;
std::string out("[");
@@ -233,7 +242,7 @@ std::string direct_bt::eirDataMaskToString(const EIRDataType mask) {
const EIRDataType settingBit = static_cast<EIRDataType>( one << i );
if( EIRDataType::NONE != ( mask & settingBit ) ) {
if( has_pre ) { out.append(", "); }
- out.append(eirDataBitToString(settingBit));
+ out.append(getEIRDataBitString(settingBit));
has_pre = true;
}
}
@@ -276,7 +285,7 @@ void EInfoReport::addService(std::shared_ptr<uuid_t> const &uuid)
}
std::string EInfoReport::eirDataMaskToString() const {
- return std::string("DataSet"+ direct_bt::eirDataMaskToString(eir_data_mask) );
+ return std::string("DataSet"+ direct_bt::getEIRDataMaskString(eir_data_mask) );
}
std::string EInfoReport::toString() const {
std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]";
@@ -286,7 +295,7 @@ std::string EInfoReport::toString() const {
", evt-type "+std::to_string(evt_type)+", rssi "+std::to_string(rssi)+
", tx-power "+std::to_string(tx_power)+
", dev-class "+uint32HexString(device_class, true)+
- ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+
+ ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+
"), hash["+hash.toString()+
"], randomizer["+randomizer.toString()+
"], device-id[source "+uint16HexString(did_source, true)+
diff --git a/src/direct_bt/CMakeLists.txt b/src/direct_bt/CMakeLists.txt
index 5a8637c1..f16905c9 100644
--- a/src/direct_bt/CMakeLists.txt
+++ b/src/direct_bt/CMakeLists.txt
@@ -15,6 +15,8 @@ set (direct_bt_LIB_SRCS
${PROJECT_SOURCE_DIR}/src/direct_bt/UUID.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/BTTypes.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/HCIComm.cpp
+ ${PROJECT_SOURCE_DIR}/src/direct_bt/HCITypes.cpp
+ ${PROJECT_SOURCE_DIR}/src/direct_bt/HCIHandler.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/L2CAPComm.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/MgmtTypes.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/DBTManager.cpp
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index 78ad8131..5d364833 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -124,19 +124,19 @@ bool DBTAdapter::validateDevInfo() {
}
DBTAdapter::DBTAdapter()
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(nullptr != mgmt.getDefaultAdapterInfo() ? 0 : -1)
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(nullptr != mgmt.getDefaultAdapterInfo() ? 0 : -1)
{
valid = validateDevInfo();
}
DBTAdapter::DBTAdapter(EUI48 &mac)
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(mgmt.findAdapterInfoIdx(mac))
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(mgmt.findAdapterInfoIdx(mac))
{
valid = validateDevInfo();
}
DBTAdapter::DBTAdapter(const int dev_id)
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(dev_id)
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(dev_id)
{
valid = validateDevInfo();
}
@@ -177,33 +177,33 @@ void DBTAdapter::setBondable(bool value) {
mgmt.setMode(dev_id, MgmtOpcode::SET_BONDABLE, value ? 1 : 0);
}
-std::shared_ptr<HCIComm> DBTAdapter::openHCI()
+std::shared_ptr<HCIHandler> DBTAdapter::openHCI()
{
const std::lock_guard<std::recursive_mutex> lock(mtx_hci); // RAII-style acquire and relinquish via destructor
if( !valid ) {
return nullptr;
}
- if( nullptr != hciComm ) {
- if( hciComm->isOpen() ) {
+ if( nullptr != hci ) {
+ if( hci->isOpen() ) {
DBG_PRINT("DBTAdapter::openHCI: Already open");
- return hciComm;
+ return hci;
}
- hciComm = nullptr;
+ hci = nullptr;
}
- HCIComm *s = new HCIComm(dev_id, HCI_CHANNEL_RAW, HCIDefaults::HCI_TO_SEND_REQ_POLL_MS);
+ HCIHandler *s = new HCIHandler(btMode, dev_id, HCIHandler::Defaults::HCI_COMMAND_REPLY_TIMEOUT);
if( !s->isOpen() ) {
delete s;
- ERR_PRINT("Could not open HCIComm: %s", toString().c_str());
+ ERR_PRINT("Could not open HCIHandler: %s of %s", s->toString().c_str(), toString().c_str());
return nullptr;
}
- hciComm = std::shared_ptr<HCIComm>( s );
- return hciComm;
+ hci = std::shared_ptr<HCIHandler>( s );
+ return hci;
}
-std::shared_ptr<HCIComm> DBTAdapter::getHCI() const {
+std::shared_ptr<HCIHandler> DBTAdapter::getHCI() const {
const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTAdapter*>(this)->mtx_hci); // RAII-style acquire and relinquish via destructor
- return hciComm;
+ return hci;
}
bool DBTAdapter::closeHCI()
@@ -211,13 +211,13 @@ bool DBTAdapter::closeHCI()
const std::lock_guard<std::recursive_mutex> lock(mtx_hci); // RAII-style acquire and relinquish via destructor
DBG_PRINT("DBTAdapter::closeHCI: ...");
- if( nullptr == hciComm || !hciComm->isOpen() ) {
+ if( nullptr == hci || !hci->isOpen() ) {
DBG_PRINT("DBTAdapter::closeHCI: Not open");
return false;
}
disconnectAllDevices(); // FIXME ????
- hciComm->close();
- hciComm = nullptr;
+ hci->close();
+ hci = nullptr;
DBG_PRINT("DBTAdapter::closeHCI: XXX");
return true;
}
@@ -423,7 +423,7 @@ std::shared_ptr<DBTDevice> DBTAdapter::findSharedDevice (EUI48 const & mac) cons
}
std::string DBTAdapter::toString() const {
- std::string out("Adapter["+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+", "+javaObjectToString()+"]");
+ std::string out("Adapter["+BTModeString(btMode)+", "+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+", "+javaObjectToString()+"]");
std::vector<std::shared_ptr<DBTDevice>> devices = getDiscoveredDevices();
if(devices.size() > 0 ) {
out.append("\n");
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index 44af3b03..848aa33c 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -123,7 +123,7 @@ std::string DBTDevice::toString(bool includeDiscoveredServices) const {
"'], age "+std::to_string(t0-ts_creation)+" ms, lup "+std::to_string(t0-ts_update)+
" ms, connected "+std::to_string(isConnected)+", rssi "+std::to_string(getRSSI())+
", tx-power "+std::to_string(tx_power)+
- ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+
+ ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+
"), "+msdstr+", "+javaObjectToString()+"]");
if(includeDiscoveredServices && services.size() > 0 ) {
out.append("\n");
@@ -250,9 +250,9 @@ uint16_t DBTDevice::connectLE(HCIAddressType peer_mac_type, HCIAddressType own_m
}
const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor
- std::shared_ptr<HCIComm> hciComm = adapter.openHCI();
- if( nullptr == hciComm || !hciComm->isOpen() ) {
- ERR_PRINT("DBTDevice::connectLE: Opening adapter's HCIComm failed: %s", toString().c_str());
+ std::shared_ptr<HCIHandler> hci = adapter.openHCI();
+ if( nullptr == hci || !hci->isOpen() ) {
+ ERR_PRINT("DBTDevice::connectLE: Opening adapter's HCI failed: %s", toString().c_str());
return 0;
}
if( !isLEAddressType() ) {
@@ -265,10 +265,10 @@ uint16_t DBTDevice::connectLE(HCIAddressType peer_mac_type, HCIAddressType own_m
DBTManager & mngr = adapter.getManager();
mngr.create_connection(adapter.dev_id, address, addressType);
}
- HCIErrorCode status = hciComm->le_create_conn(&hciConnHandle, address,
- peer_mac_type, own_mac_type,
- le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
- conn_latency, supervision_timeout);
+ HCIErrorCode status = hci->le_create_conn(&hciConnHandle, address,
+ peer_mac_type, own_mac_type,
+ le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
+ conn_latency, supervision_timeout);
#if 0
if( HCIErrorCode::CONNECTION_ALREADY_EXISTS == status ) {
INFO_PRINT("DBTDevice::connectLE: Connection already exists: status 0x%2.2X (%s) on %s",
@@ -302,9 +302,9 @@ uint16_t DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_o
return 0;
}
const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor
- std::shared_ptr<HCIComm> hciComm = adapter.openHCI();
- if( nullptr == hciComm || !hciComm->isOpen() ) {
- ERR_PRINT("DBTDevice::connectBREDR: Opening adapter's HCIComm failed: %s", toString().c_str());
+ std::shared_ptr<HCIHandler> hci = adapter.openHCI();
+ if( nullptr == hci || !hci->isOpen() ) {
+ ERR_PRINT("DBTDevice::connectBREDR: Opening adapter's HCI failed: %s", toString().c_str());
return 0;
}
if( !isBREDRAddressType() ) {
@@ -318,7 +318,7 @@ uint16_t DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_o
mngr.create_connection(adapter.dev_id, address, addressType);
}
- HCIErrorCode status = hciComm->create_conn(&hciConnHandle, address, pkt_type, clock_offset, role_switch);
+ HCIErrorCode status = hci->create_conn(&hciConnHandle, address, pkt_type, clock_offset, role_switch);
if ( HCIErrorCode::SUCCESS != status ) {
ERR_PRINT("DBTDevice::connectBREDR: Could not create connection: status 0x%2.2X (%s), errno %d %s on %s",
static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno), toString().c_str());
@@ -366,7 +366,7 @@ void DBTDevice::disconnect(const bool sentFromManager, const bool ioErrorCause,
disconnectGATT();
const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor
- std::shared_ptr<HCIComm> hciComm = adapter.getHCI();
+ std::shared_ptr<HCIHandler> hci = adapter.getHCI();
if( !isConnected ) {
DBG_PRINT("DBTDevice::disconnect: Skip disconnect: Not connected: %s", toString().c_str());
@@ -384,12 +384,12 @@ void DBTDevice::disconnect(const bool sentFromManager, const bool ioErrorCause,
goto skip_hci_disconnect;
}
- if( nullptr == hciComm || !hciComm->isOpen() ) {
+ if( nullptr == hci || !hci->isOpen() ) {
DBG_PRINT("DBTDevice::disconnect: Skip HCI disconnect: HCI not Open: %s", toString().c_str());
goto skip_hci_disconnect;
}
- if( !hciComm->disconnect(hciConnHandle, reason) ) {
+ if( HCIErrorCode::SUCCESS != hci->disconnect(hciConnHandle, reason) ) {
DBG_PRINT("DBTDevice::disconnect: handle 0x%X, errno %d %s", hciConnHandle, errno, strerror(errno));
}
diff --git a/src/direct_bt/DBTManager.cpp b/src/direct_bt/DBTManager.cpp
index d40db50b..546e1b96 100644
--- a/src/direct_bt/DBTManager.cpp
+++ b/src/direct_bt/DBTManager.cpp
@@ -106,8 +106,9 @@ void DBTManager::mgmtReaderThreadImpl() {
}
}
- INFO_PRINT("DBTManager::reader: Ended");
+ INFO_PRINT("DBTManager::reader: Ended. Ring has %d entries flushed", mgmtEventRing.getSize());
mgmtReaderRunning = false;
+ mgmtEventRing.clear();
}
void DBTManager::sendMgmtEvent(std::shared_ptr<MgmtEvent> event) {
diff --git a/src/direct_bt/DBTTypes.cpp b/src/direct_bt/DBTTypes.cpp
index a880570b..6b5db0e4 100644
--- a/src/direct_bt/DBTTypes.cpp
+++ b/src/direct_bt/DBTTypes.cpp
@@ -72,7 +72,7 @@ using namespace direct_bt;
X(AdapterSetting,STATIC_ADDRESS) \
X(AdapterSetting,PHY_CONFIGURATION)
-std::string direct_bt::adapterSettingBitToString(const AdapterSetting settingBit) {
+std::string direct_bt::getAdapterSettingBitString(const AdapterSetting settingBit) {
switch(settingBit) {
SETTING_ENUM(CASE2_TO_STRING)
default: ; // fall through intended
@@ -80,7 +80,7 @@ std::string direct_bt::adapterSettingBitToString(const AdapterSetting settingBit
return "Unknown Setting Bit";
}
-std::string direct_bt::adapterSettingsToString(const AdapterSetting settingMask) {
+std::string direct_bt::getAdapterSettingsString(const AdapterSetting settingMask) {
const uint32_t one = 1;
bool has_pre = false;
std::string out("[");
@@ -88,7 +88,7 @@ std::string direct_bt::adapterSettingsToString(const AdapterSetting settingMask)
const AdapterSetting settingBit = static_cast<AdapterSetting>( one << i );
if( AdapterSetting::NONE != ( settingMask & settingBit ) ) {
if( has_pre ) { out.append(", "); }
- out.append(adapterSettingBitToString(settingBit));
+ out.append(getAdapterSettingBitString(settingBit));
has_pre = true;
}
}
diff --git a/src/direct_bt/GATTHandler.cpp b/src/direct_bt/GATTHandler.cpp
index f6ec93a2..23763982 100644
--- a/src/direct_bt/GATTHandler.cpp
+++ b/src/direct_bt/GATTHandler.cpp
@@ -253,8 +253,9 @@ void GATTHandler::l2capReaderThreadImpl() {
}
}
- INFO_PRINT("l2capReaderThreadImpl Ended");
+ INFO_PRINT("l2capReaderThreadImpl Ended. Ring has %d entries flushed", attPDURing.getSize());
l2capReaderRunning = false;
+ attPDURing.clear();
disconnect(ioErrorCause);
}
@@ -381,7 +382,7 @@ std::shared_ptr<const AttPDUMsg> GATTHandler::sendWithReply(const AttPDUMsg & ms
send( msg );
// Ringbuffer read is thread safe
- std::shared_ptr<const AttPDUMsg> res = receiveNext();
+ std::shared_ptr<const AttPDUMsg> res = attPDURing.getBlocking(replyTimeoutMS);
if( nullptr == res ) {
errno = ETIMEDOUT;
ERR_PRINT("GATTHandler::send: nullptr result (timeout): req %s to %s", msg.toString().c_str(), deviceString.c_str());
@@ -391,10 +392,6 @@ std::shared_ptr<const AttPDUMsg> GATTHandler::sendWithReply(const AttPDUMsg & ms
return res;
}
-std::shared_ptr<const AttPDUMsg> GATTHandler::receiveNext() {
- return attPDURing.getBlocking(replyTimeoutMS);
-}
-
uint16_t GATTHandler::exchangeMTU(const uint16_t clientMaxMTU) {
/***
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.3.1 Exchange MTU (Server configuration)
diff --git a/src/direct_bt/GATTNumbers.cpp b/src/direct_bt/GATTNumbers.cpp
index 11287968..bbcb944f 100644
--- a/src/direct_bt/GATTNumbers.cpp
+++ b/src/direct_bt/GATTNumbers.cpp
@@ -368,7 +368,7 @@ std::string PeriphalPreferredConnectionParameters::toString() const {
}
std::string GenericAccess::toString() const {
- return "'"+deviceName+"'[appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+"), "+
+ return "'"+deviceName+"'[appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+"), "+
prefConnParam.toString()+"]";
}
diff --git a/src/direct_bt/HCIComm.cpp b/src/direct_bt/HCIComm.cpp
index d1e8b2d2..aedbad01 100644
--- a/src/direct_bt/HCIComm.cpp
+++ b/src/direct_bt/HCIComm.cpp
@@ -50,87 +50,6 @@ extern "C" {
namespace direct_bt {
-#define HCI_ERROR_CODE(X) \
- X(SUCCESS) \
- X(UNKNOWN_HCI_COMMAND) \
- X(UNKNOWN_CONNECTION_IDENTIFIER) \
- X(HARDWARE_FAILURE) \
- X(PAGE_TIMEOUT) \
- X(AUTHENTICATION_FAILURE) \
- X(PIN_OR_KEY_MISSING) \
- X(MEMORY_CAPACITY_EXCEEDED) \
- X(CONNECTION_TIMEOUT) \
- X(CONNECTION_LIMIT_EXCEEDED) \
- X(SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED) \
- X(CONNECTION_ALREADY_EXISTS) \
- X(COMMAND_DISALLOWED) \
- X(CONNECTION_REJECTED_LIMITED_RESOURCES) \
- X(CONNECTION_REJECTED_SECURITY) \
- X(CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR) \
- X(CONNECTION_ACCEPT_TIMEOUT_EXCEEDED) \
- X(UNSUPPORTED_FEATURE_OR_PARAM_VALUE) \
- X(INVALID_HCI_COMMAND_PARAMETERS) \
- X(REMOTE_USER_TERMINATED_CONNECTION) \
- X(REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES) \
- X(REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF) \
- X(CONNECTION_TERMINATED_BY_LOCAL_HOST) \
- X(REPEATED_ATTEMPTS) \
- X(PAIRING_NOT_ALLOWED) \
- X(UNKNOWN_LMP_PDU) \
- X(UNSUPPORTED_REMOTE_OR_LMP_FEATURE) \
- X(SCO_OFFSET_REJECTED) \
- X(SCO_INTERVAL_REJECTED) \
- X(SCO_AIR_MODE_REJECTED) \
- X(INVALID_LMP_OR_LL_PARAMETERS) \
- X(UNSPECIFIED_ERROR) \
- X(UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE) \
- X(ROLE_CHANGE_NOT_ALLOWED) \
- X(LMP_OR_LL_RESPONSE_TIMEOUT) \
- X(LMP_OR_LL_COLLISION) \
- X(LMP_PDU_NOT_ALLOWED) \
- X(ENCRYPTION_MODE_NOT_ACCEPTED) \
- X(LINK_KEY_CANNOT_BE_CHANGED) \
- X(REQUESTED_QOS_NOT_SUPPORTED) \
- X(INSTANT_PASSED) \
- X(PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) \
- X(DIFFERENT_TRANSACTION_COLLISION) \
- X(QOS_UNACCEPTABLE_PARAMETER) \
- X(QOS_REJECTED) \
- X(CHANNEL_ASSESSMENT_NOT_SUPPORTED) \
- X(INSUFFICIENT_SECURITY) \
- X(PARAMETER_OUT_OF_RANGE) \
- X(ROLE_SWITCH_PENDING) \
- X(RESERVED_SLOT_VIOLATION) \
- X(ROLE_SWITCH_FAILED) \
- X(EIR_TOO_LARGE) \
- X(SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST) \
- X(HOST_BUSY_PAIRING) \
- X(CONNECTION_REJECTED_NO_SUITABLE_CHANNEL) \
- X(CONTROLLER_BUSY) \
- X(UNACCEPTABLE_CONNECTION_PARAM) \
- X(ADVERTISING_TIMEOUT) \
- X(CONNECTION_TERMINATED_MIC_FAILURE) \
- X(CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT) \
- X(MAX_CONNECTION_FAILED) \
- X(COARSE_CLOCK_ADJ_REJECTED) \
- X(TYPE0_SUBMAP_NOT_DEFINED) \
- X(UNKNOWN_ADVERTISING_IDENTIFIER) \
- X(LIMIT_REACHED) \
- X(OPERATION_CANCELLED_BY_HOST) \
- X(PACKET_TOO_LONG) \
- X(INTERNAL_FAILURE) \
- X(UNKNOWN)
-
-#define HCI_ERROR_CODE_CASE_TO_STRING(V) case HCIErrorCode::V: return #V;
-
-std::string getHCIErrorCodeString(const HCIErrorCode ec) {
- switch(ec) {
- HCI_ERROR_CODE(HCI_ERROR_CODE_CASE_TO_STRING)
- default: ; // fall through intended
- }
- return "Unknown HCI error code";
-}
-
int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel)
{
sockaddr_hci a;
@@ -263,504 +182,4 @@ errout:
return -1;
}
-bool HCIComm::send_cmd(const uint16_t opcode, const void *command, const uint8_t command_len)
-{
- if( 0 > _dd ) {
- ERR_PRINT("hci_send_cmd: device not open");
- return false;
- }
- const uint8_t type = HCI_COMMAND_PKT;
- hci_command_hdr hc;
- struct iovec iv[3];
- int ivn;
- int bw=0;
-
- hc.opcode = cpu_to_le(opcode);
- hc.plen= command_len;
-
- iv[0].iov_base = (void*)&type;
- iv[0].iov_len = 1;
- iv[1].iov_base = (void*)&hc;
- iv[1].iov_len = HCI_COMMAND_HDR_SIZE;
- ivn = 2;
-
- if (command_len) {
- iv[2].iov_base = (void*)command;
- iv[2].iov_len = command_len;
- ivn = 3;
- }
-
-#ifdef VERBOSE_ON
- {
- fprintf(stderr, "hci_send_cmd: type 0x%2.2X, opcode 0x%X, plen %d\n", type, hc.opcode, command_len);
- std::string paramstr = command_len > 0 ? bytesHexString((uint8_t*)command, 0, command_len, false /* lsbFirst */, true /* leading0X */) : "";
- fprintf(stderr, "hci_send_cmd: param: %s\n", paramstr.c_str());
- }
-#endif
-
- while ( ( bw = ::writev(_dd, iv, ivn) ) < 0 ) {
- ERR_PRINT("hci_send_cmd: writev res %d", bw);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- return false;
- }
- DBG_PRINT("hci_send_cmd: writev: %d", bw);
- return true;
-}
-
-#define _HCI_PKT_TRY_COUNT 10
-
-HCIErrorCode HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t command_len,
- const uint16_t exp_event, void *response, const uint8_t response_len)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_send_req: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- uint8_t buf[HCI_MAX_EVENT_SIZE];
- const uint16_t opcode_le16 = cpu_to_le(opcode);
- hci_ufilter nf, of;
- socklen_t olen;
- int err, tryCount=0;
-
- DBG_PRINT("hci_send_req: opcode 0x%X ", opcode_le16);
-
- olen = sizeof(of);
- if (getsockopt(_dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
- ERR_PRINT("hci_send_req");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
-
- filter_clear(&nf);
- filter_set_ptype(HCI_EVENT_PKT, &nf);
- filter_set_event(HCI_EV_CMD_STATUS, &nf);
- filter_set_event(HCI_EV_CMD_COMPLETE, &nf);
- filter_set_event(HCI_EV_LE_META, &nf);
- filter_set_event(exp_event, &nf);
- filter_set_opcode(opcode_le16, &nf);
- if (setsockopt(_dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
- ERR_PRINT("hci_send_req");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
-
- int _timeoutMS = timeoutMS;
- HCIErrorCode res = HCIErrorCode::INTERNAL_FAILURE;
-
- if ( !send_cmd(opcode, command, command_len) ) {
- ERR_PRINT("hci_send_req");
- goto failed;
- }
-
- // Reading up to 10 packets for HCI responses,
- while ( _HCI_PKT_TRY_COUNT > tryCount++ ) {
- if ( _timeoutMS ) {
- struct pollfd p;
- int n;
-
- p.fd = _dd; p.events = POLLIN;
-#if 0
- sigset_t sigmask;
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGALRM);
- struct timespec timeout_ts;
- timeout_ts.tv_sec=0;
- timeout_ts.tv_nsec=(long)_timeoutMS*1000000L;
- while ((n = ppoll(&p, 1, &timeout_ts, &sigmask)) < 0) {
-#else
- while ((n = poll(&p, 1, _timeoutMS)) < 0) {
-#endif
- ERR_PRINT("HCIComm::send_req(dev_id %d, channel %d): poll: res %d", dev_id, channel, n);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- goto failed;
- }
-
- if (!n) {
- DBG_PRINT("hci_send_req: poll: timeout");
- errno = ETIMEDOUT;
- goto failed;
- }
-
- _timeoutMS -= _timeoutMS/_HCI_PKT_TRY_COUNT; // reduce timeout consecutively
- if ( _timeoutMS < 0 ) {
- _timeoutMS = 0;
- }
- }
-
- int len;
-
- while ((len = ::read(_dd, buf, sizeof(buf))) < 0) {
- ERR_PRINT("HCIComm::send_req(dev_id %d, channel %d): read: res %d", dev_id, channel, len);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- goto failed;
- }
-
- const hci_event_hdr *hdr = static_cast<hci_event_hdr *>(static_cast<void *>(buf + 1));
- const uint8_t * ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
- len -= (1 + HCI_EVENT_HDR_SIZE);
-
- DBG_PRINT("hci_send_req: read: %d bytes, evt 0x%2.2X", len, hdr->evt);
-
- switch (hdr->evt) {
- case HCI_EV_CMD_STATUS: {
- const hci_ev_cmd_status *cs = static_cast<const hci_ev_cmd_status *>(static_cast<const void *>( ptr ));
- const HCIErrorCode status = static_cast<HCIErrorCode>(cs->status);
-
- DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: opcode 0x%X, exp_event 0x%X, status 0x%2.2X (%s), rlen %d/%d",
- cs->opcode, exp_event,
- cs->status, getHCIErrorCodeString(status).c_str(),
- response_len, len);
-
- if (cs->opcode != opcode_le16) {
- continue; // next packet
- }
-
- if (exp_event != HCI_EV_CMD_STATUS) {
- if ( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- DBG_PRINT("hci_send_req: event exp 0x%X != has 0x%X, error status 0x%2.2X (%s)", exp_event,
- hdr->evt, cs->status, getHCIErrorCodeString(status).c_str());
- res = status;
- goto failed;
- }
- continue; // next packet
- }
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- case HCI_EV_CMD_COMPLETE: {
- const hci_ev_cmd_complete *cc = static_cast<const hci_ev_cmd_complete *>(static_cast<const void *>( ptr ));
-
- DBG_PRINT("hci_send_req: HCI_EV_CMD_COMPLETE: opcode 0x%X (equal %d), exp_event 0x%X, r-len %d/%d",
- cc->opcode, (cc->opcode == opcode_le16), exp_event, response_len, len);
-
- if (cc->opcode != opcode_le16) {
- continue; // next packet
- }
-
- ptr += sizeof(hci_ev_cmd_complete);
- len -= sizeof(hci_ev_cmd_complete);
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_CMD_COMPLETE: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- case HCI_EV_LE_META: {
- const hci_ev_le_meta *me = static_cast<const hci_ev_le_meta *>(static_cast<const void *>( ptr ));
-
- DBG_PRINT("hci_send_req: HCI_EV_LE_META: subevent 0x%X, exp_event 0x%X (equal %d), r-len %d/%d",
- me->subevent, exp_event, (me->subevent == exp_event), response_len, len);
-
- if (me->subevent != exp_event) {
- continue; // next packet
- }
-
- ptr += 1;
- len -= 1;
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_LE_META: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- default: {
- DBG_PRINT("hci_send_req: DEFAULT: evt 0x%X, exp_event 0x%X (equal %d), r-len %d/%d",
- hdr->evt, exp_event, (hdr->evt == exp_event), response_len, len);
-
- if (hdr->evt != exp_event) {
- continue; // next packet
- }
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: DEFAULT: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
- } // switch event
- } // while packets ...
- errno = ETIMEDOUT;
-
-failed:
- err = errno;
- setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
- errno = err;
- return res;
-
-done:
- setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
- return HCIErrorCode::SUCCESS;
-}
-
-bool HCIComm::disconnect(const uint16_t le_conn_handle, const HCIErrorCode reason)
-{
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command */
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- DBG_PRINT("hci_disconnect: handle 0x%x, reason 0x%x (%s)",
- le_conn_handle, static_cast<uint8_t>(reason), getHCIErrorCodeString(reason).c_str());
- if( 0 > _dd ) {
- return true;
- }
- if( 0 == le_conn_handle ) {
- return true;
- }
- hci_ev_disconn_complete rp;
- hci_cp_disconnect cp;
-
- bzero(&cp, sizeof(cp));
- cp.handle = le_conn_handle;
- cp.reason = static_cast<uint8_t>(reason);
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_DISCONNECT), &cp, sizeof(cp),
- HCI_EV_DISCONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_disconnect: 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_disconnect: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-bool HCIComm::le_set_scan_enable(const uint8_t enable, const uint8_t filter_dup) {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_set_scan_enable(%d): device not open", enable);
- return false;
- }
- hci_cp_le_set_scan_enable cp;
- HCIErrorCode status;
-
- bzero(&cp, sizeof(cp));
- cp.enable = enable;
- cp.filter_dup = filter_dup;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_ENABLE), &cp, sizeof(cp),
- 0, &status, sizeof(status) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_set_scan_enable(%d): 0x%2.2X (%s), errno %d %s",
- enable, static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_set_scan_enable(%d): error status 0x%2.2X (%s), errno %d %s",
- enable, static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-bool HCIComm::le_set_scan_parameters(const uint8_t type, const uint16_t interval,
- const uint16_t window, const uint8_t own_type,
- const uint8_t filter)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_set_scan_parameters: device not open");
- return false;
- }
- hci_cp_le_set_scan_param cp;
- HCIErrorCode status;
-
- bzero(&cp, sizeof(cp));
- cp.type = type;
- cp.interval = cpu_to_le(interval);
- cp.window = cpu_to_le(window);
- cp.own_address_type = own_type;
- cp.filter_policy = filter;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_PARAM), &cp, sizeof(cp),
- 0, &status, sizeof(status) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_set_scan_parameters: 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_set_scan_parameters: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-void HCIComm::le_disable_scan() {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- return;
- }
- if( !le_scanning ) {
- return;
- }
- const uint8_t filter_dup = 0x01;
-
- if( !le_set_scan_enable(0x00, filter_dup) ) {
- ERR_PRINT("Stop scan failed");
- } else {
- le_scanning = false;
- }
-}
-
-bool HCIComm::le_enable_scan(const HCIAddressType own_type,
- const uint16_t interval, const uint16_t window) {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- DBG_PRINT("hci_le_enable_scan: device not open");
- return false;
- }
- if( le_scanning ) {
- DBG_PRINT("hci_le_enable_scan: device already le-scanning");
- return false;
- }
- const uint8_t scan_type = 0x01;
- // const uint8_t filter_type = 0;
- const uint8_t filter_policy = 0x00;
- const uint8_t filter_dup = 0x01;
-
- bool ok = le_set_scan_parameters(scan_type, interval, window,
- own_type, filter_policy);
- if ( !ok ) {
- ERR_PRINT("Set scan parameters failed");
- return false;
- }
-
- ok = le_set_scan_enable(0x01, filter_dup);
- if ( !ok ) {
- ERR_PRINT("Start scan failed");
- return false;
- }
- le_scanning = true;
- return true;
-}
-
-HCIErrorCode HCIComm::le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
- const HCIAddressType peer_mac_type,
- const HCIAddressType own_mac_type,
- const uint16_t le_scan_interval, const uint16_t le_scan_window,
- const uint16_t conn_interval_min, const uint16_t conn_interval_max,
- const uint16_t conn_latency, const uint16_t supervision_timeout)
-{
-
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command */
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event: 7.7.65.1 LE Connection Complete event */
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( nullptr != handle_return ) {
- *handle_return = 0;
- }
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_create_conn: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- hci_cp_le_create_conn cp;
- hci_ev_le_conn_complete rp;
-
- const uint16_t min_ce_length = 0x0000;
- const uint16_t max_ce_length = 0x0000;
- const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr*
-
- bzero((void*)&cp, sizeof(cp));
- cp.scan_interval = cpu_to_le(le_scan_interval);
- cp.scan_window = cpu_to_le(le_scan_window);
- cp.filter_policy = initiator_filter;
- cp.peer_addr_type = peer_mac_type;
- cp.peer_addr = peer_bdaddr;
- cp.own_address_type = own_mac_type;
- cp.conn_interval_min = cpu_to_le(conn_interval_min);
- cp.conn_interval_max = cpu_to_le(conn_interval_max);
- cp.conn_latency = cpu_to_le(conn_latency);
- cp.supervision_timeout = cpu_to_le(supervision_timeout);
- cp.min_ce_len = cpu_to_le(min_ce_length);
- cp.max_ce_len = cpu_to_le(max_ce_length);
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_CREATE_CONN), &cp, sizeof(cp),
- HCI_EV_LE_CONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::COMMAND_DISALLOWED == res ) {
- WARN_PRINT("hci_le_create_conn: COMMAND_DISALLOWED reply, connect may still be in progress. error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return status;
- }
- DBG_PRINT("hci_le_create_conn: Success: handle 0x%x", rp.handle);
- if( nullptr != handle_return ) {
- *handle_return = rp.handle;
- }
- return HCIErrorCode::SUCCESS;
-}
-
-HCIErrorCode HCIComm::create_conn(uint16_t * handle_return, const EUI48 &bdaddr, const uint16_t pkt_type,
- const uint16_t clock_offset, const uint8_t role_switch)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( nullptr != handle_return ) {
- *handle_return = 0;
- }
- if( 0 > _dd ) {
- ERR_PRINT("hci_create_conn: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- hci_cp_create_conn cp;
- hci_ev_conn_complete rp;
-
- bzero((void*)&cp, sizeof(cp));
- cp.bdaddr = bdaddr;
- cp.pkt_type = cpu_to_le((uint16_t)(pkt_type & (uint16_t)ACL_PTYPE_MASK)); /* TODO OK excluding SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) ? */
- cp.pscan_rep_mode = 0x02; /* TODO magic? */
- cp.pscan_mode = 0x00; /* TODO magic? */
- cp.clock_offset = cpu_to_le(clock_offset);
- cp.role_switch = role_switch;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_CREATE_CONN), &cp, sizeof(cp),
- HCI_EV_CONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return status;
- }
- if( nullptr != handle_return ) {
- *handle_return = rp.handle;
- }
- return HCIErrorCode::SUCCESS;
-}
-
} /* namespace direct_bt */
diff --git a/src/direct_bt/HCIHandler.cpp b/src/direct_bt/HCIHandler.cpp
new file mode 100644
index 00000000..5b4688a1
--- /dev/null
+++ b/src/direct_bt/HCIHandler.cpp
@@ -0,0 +1,598 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+#include <cstdio>
+
+#include <algorithm>
+
+// #define SHOW_LE_ADVERTISING 1
+
+// #define PERF_PRINT_ON 1
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
+#include "BTIoctl.hpp"
+
+#include "HCIIoctl.hpp"
+#include "HCIComm.hpp"
+#include "HCIHandler.hpp"
+#include "DBTTypes.hpp"
+#include "BasicAlgos.hpp"
+
+extern "C" {
+ #include <inttypes.h>
+ #include <unistd.h>
+ #include <poll.h>
+ #include <signal.h>
+}
+
+using namespace direct_bt;
+
+const pid_t HCIHandler::pidSelf = getpid();
+
+void HCIHandler::hciReaderThreadImpl() {
+ hciReaderShallStop = false;
+ hciReaderRunning = true;
+ INFO_PRINT("HCIHandler::reader: Started");
+
+ while( !hciReaderShallStop ) {
+ int len;
+ if( !comm.isOpen() ) {
+ // not open
+ ERR_PRINT("HCIHandler::reader: Not connected");
+ hciReaderShallStop = true;
+ break;
+ }
+
+ len = comm.read(rbuffer.get_wptr(), rbuffer.getSize());
+ if( 0 < len ) {
+ const uint16_t paramSize = len >= 3 ? rbuffer.get_uint8(2) : 0;
+ if( len < number(HCIConstU8::EVENT_HDR_SIZE) + paramSize ) {
+ WARN_PRINT("HCIHandler::reader: length mismatch %d < %d + %d",
+ len, number(HCIConstU8::EVENT_HDR_SIZE), paramSize);
+ continue; // discard data
+ }
+ std::shared_ptr<HCIEvent> event( HCIEvent::getSpecialized(rbuffer.get_ptr(), len) );
+ if( nullptr == event ) {
+ // not an event ...
+ ERR_PRINT("HCIHandler::reader: drop non-event %s", bytesHexString(rbuffer.get_ptr(), 0, len, true /* lsbFirst*/).c_str());
+ continue;
+ }
+ const HCIMetaEventType mec = event->getMetaEventType();
+ if( HCIMetaEventType::INVALID != mec && !filter_test_metaev(mec) ) {
+ // DROP
+ DBG_PRINT("HCIHandler::reader: drop %s", event->toString().c_str());
+ continue; // next packet
+ }
+#ifdef SHOW_LE_ADVERTISING
+ if( event->isMetaEvent(HCIMetaEventType::LE_ADVERTISING_REPORT) ) {
+ std::vector<std::shared_ptr<EInfoReport>> eirlist = EInfoReport::read_ad_reports(event->getParam(), event->getParamSize());
+ int i=0;
+ for_each_idx(eirlist, [&](std::shared_ptr<EInfoReport> &eir) {
+ INFO_PRINT("LE_ADV[%d]: %s", i, eir->toString().c_str());
+ i++;
+ });
+ continue; // next packet
+ }
+#endif /* SHOW_LE_ADVERTISING */
+ if( hciEventRing.isFull() ) {
+ std::shared_ptr<HCIEvent> ev = hciEventRing.get();
+ INFO_PRINT("HCIHandler::reader: full ring, dropping oldest %s",
+ ( nullptr != ev ) ? ev->toString().c_str() : "nil");
+ }
+ DBG_PRINT("HCIHandler::reader: got %s", event->toString().c_str());
+ hciEventRing.putBlocking( event );
+ } else if( ETIMEDOUT != errno && !hciReaderShallStop ) { // expected exits
+ ERR_PRINT("HCIHandler::reader: HCIComm error");
+ }
+ }
+ INFO_PRINT("HCIHandler::reader: Ended. Ring has %d entries flushed", hciEventRing.getSize());
+ hciReaderRunning = false;
+ hciEventRing.clear();
+}
+
+bool HCIHandler::sendCommand(HCICommand &req) {
+ const std::lock_guard<std::recursive_mutex> lock(comm.mutex()); // RAII-style acquire and relinquish via destructor
+ TROOctets & pdu = req.getPDU();
+ if ( comm.write( pdu.get_ptr(), pdu.getSize() ) < 0 ) {
+ ERR_PRINT("HCIHandler::sendWithReply: HCIComm write error, req %s", req.toString().c_str());
+ return false;
+ }
+ return true;
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::getNextReply(HCICommand &req, int & retryCount) {
+ // Ringbuffer read is thread safe
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = hciEventRing.getBlocking(replyTimeoutMS);
+ if( nullptr == ev ) {
+ errno = ETIMEDOUT;
+ DBG_PRINT("HCIHandler::getNextReply: nullptr result (timeout -> abort): req %s", req.toString().c_str());
+ return nullptr;
+ } else if( !ev->validate(req) ) {
+ // This could occur due to an earlier timeout w/ a nullptr == res (see above),
+ // i.e. the pending reply processed here and naturally not-matching.
+ retryCount++;
+ DBG_PRINT("HCIHandler::getNextReply: res mismatch (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ } else {
+ DBG_PRINT("HCIHandler::getNextReply: res: %s, req %s", ev->toString().c_str(), req.toString().c_str());
+ return ev;
+ }
+ }
+ return nullptr;
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithReply(HCICommand &req) {
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ return getNextReply(req, retryCount);
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithCmdCompleteReply(HCICommand &req, HCICommandCompleteEvent **res) {
+ *res = nullptr;
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+
+ int retryCount = 0;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = getNextReply(req, retryCount);
+ if( nullptr == ev ) {
+ return nullptr; // timeout
+ } else if( !ev->isEvent(HCIEventType::CMD_COMPLETE) ) {
+ DBG_PRINT("HCIHandler::sendWithCmdCompleteReply: !CMD_COMPLETE (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ continue; // next packet
+ } else {
+ *res = static_cast<HCICommandCompleteEvent*>(ev.get());
+ return ev;
+ }
+ }
+ return nullptr; // max retry
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithCmdStatusReply(HCICommand &req, HCICommandStatusEvent **res) {
+ *res = nullptr;
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+
+ int retryCount = 0;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = getNextReply(req, retryCount);
+ if( nullptr == ev ) {
+ return nullptr; // timeout
+ } else if( !ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ DBG_PRINT("HCIHandler::sendWithCmdStatusReply: !CMD_STATUS (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ continue; // next packet
+ } else {
+ *res = static_cast<HCICommandStatusEvent*>(ev.get());
+ return ev;
+ }
+ }
+ return nullptr; // max retry
+}
+
+HCIHandler::HCIHandler(const BTMode btMode, const uint16_t dev_id, const int replyTimeoutMS)
+:btMode(btMode), dev_id(dev_id), rbuffer(HCI_MAX_MTU),
+ comm(dev_id, HCI_CHANNEL_RAW, Defaults::HCI_READER_THREAD_POLL_TIMEOUT), replyTimeoutMS(replyTimeoutMS),
+ hciEventRing(HCI_EVT_RING_CAPACITY), hciReaderRunning(false), hciReaderShallStop(false)
+{
+ INFO_PRINT("HCIHandler.ctor: pid %d", HCIHandler::pidSelf);
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::open: Could not open hci control channel");
+ return;
+ }
+
+ std::thread hciReaderThread = std::thread(&HCIHandler::hciReaderThreadImpl, this);
+ hciReaderThreadId = hciReaderThread.native_handle();
+ // Avoid 'terminate called without an active exception'
+ // as l2capReaderThread may end due to I/O errors.
+ hciReaderThread.detach();
+
+ PERF_TS_T0();
+
+ // Mandatory socket filter (not adapter filter!)
+ {
+ hci_ufilter nf, of;
+ socklen_t olen;
+
+ olen = sizeof(of);
+ if (getsockopt(comm.dd(), SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+ ERR_PRINT("HCIHandler::ctor: getsockopt");
+ goto fail;
+ }
+ // uint16_t opcode_le16 = 0;
+ HCIComm::filter_clear(&nf);
+ HCIComm::filter_set_ptype(number(HCIPacketType::EVENT), &nf); // only EVENTs
+#if 0
+ HCIComm::filter_all_events(&nf); // all events
+#else
+ HCIComm::filter_set_event(number(HCIEventType::CONN_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::CMD_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::CMD_STATUS), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::HARDWARE_ERROR), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::LE_META), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_PHY_LINK_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_LOGICAL_LINK_COMPLETE), &nf);
+#endif
+ HCIComm::filter_set_opcode(0, &nf); // all opcode
+ if (setsockopt(comm.dd(), SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ ERR_PRINT("HCIHandler::ctor: setsockopt");
+ goto fail;
+ }
+ }
+ // Mandatory own LE_META filter
+ {
+ filter_clear_metaevs();
+ // filter_all_metaevs();
+ filter_set_metaev(HCIMetaEventType::LE_CONN_COMPLETE);
+#ifdef SHOW_LE_ADVERTISING
+ filter_set_metaev(HCIMetaEventType::LE_ADVERTISING_REPORT);
+#endif
+ }
+ {
+ HCICommand req0(HCIOpcode::READ_LOCAL_VERSION, 0);
+ const hci_rp_read_local_version * ev_lv;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processCmdCompleteCommand<hci_rp_read_local_version>(
+ HCIOpcode::READ_LOCAL_VERSION, &ev_lv, &status);
+ if( nullptr == ev || nullptr == ev_lv ) {
+ ERR_PRINT("HCIHandler::ctor: failed READ_LOCAL_VERSION: 0x%x (%s)", number(status), getHCIErrorCodeString(status).c_str());
+ goto fail;
+ }
+ INFO_PRINT("HCIHandler::ctor: LOCAL_VERSION: %d.%d, manuf 0x%x, lmp %d.%d",
+ ev_lv->hci_ver, le_to_cpu(ev_lv->hci_rev), le_to_cpu(ev_lv->manufacturer),
+ ev_lv->lmp_ver, le_to_cpu(ev_lv->lmp_subver));
+ }
+
+ PERF_TS_TD("HCIHandler::open.ok");
+ return;
+
+fail:
+ close();
+ PERF_TS_TD("HCIHandler::open.fail");
+ return;
+}
+
+void HCIHandler::close() {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ DBG_PRINT("HCIHandler::close: Start");
+
+ const pthread_t tid_self = pthread_self();
+ const pthread_t tid_reader = hciReaderThreadId;
+ hciReaderThreadId = 0;
+ const bool is_reader = tid_reader == tid_self;
+ DBG_PRINT("HCIHandler.disconnect: Start hciReader[running %d, shallStop %d, isReader %d, tid %p)",
+ hciReaderRunning.load(), hciReaderShallStop.load(), is_reader, (void*)tid_reader);
+ if( hciReaderRunning ) {
+ hciReaderShallStop = true;
+ if( !is_reader && 0 != tid_reader ) {
+ int kerr;
+ if( 0 != ( kerr = pthread_kill(tid_reader, SIGALRM) ) ) {
+ ERR_PRINT("HCIHandler::disconnect: pthread_kill %p FAILED: %d", (void*)tid_reader, kerr);
+ }
+ }
+ }
+ comm.close();
+ DBG_PRINT("HCIHandler::close: End");
+}
+
+HCIErrorCode HCIHandler::reset() {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::reset: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ HCICommand req0(HCIOpcode::RESET, 0);
+ HCICommandCompleteEvent * ev_cc;
+ std::shared_ptr<HCIEvent> ev = sendWithCmdCompleteReply(req0, &ev_cc);
+ if( nullptr == ev || nullptr == ev_cc ) {
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ return ev_cc->getReturnStatus(0);
+}
+
+HCIErrorCode HCIHandler::le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
+ const HCIAddressType peer_mac_type,
+ const HCIAddressType own_mac_type,
+ const uint16_t le_scan_interval, const uint16_t le_scan_window,
+ const uint16_t conn_interval_min, const uint16_t conn_interval_max,
+ const uint16_t conn_latency, const uint16_t supervision_timeout) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( nullptr != handle_return ) {
+ *handle_return = 0;
+ }
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::le_create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ hci_cp_le_create_conn cp;
+
+ const uint16_t min_ce_length = 0x0000;
+ const uint16_t max_ce_length = 0x0000;
+ const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr*
+
+ bzero((void*)&cp, sizeof(cp));
+ cp.scan_interval = cpu_to_le(le_scan_interval);
+ cp.scan_window = cpu_to_le(le_scan_window);
+ cp.filter_policy = initiator_filter;
+ cp.peer_addr_type = peer_mac_type;
+ cp.peer_addr = peer_bdaddr;
+ cp.own_address_type = own_mac_type;
+ cp.conn_interval_min = cpu_to_le(conn_interval_min);
+ cp.conn_interval_max = cpu_to_le(conn_interval_max);
+ cp.conn_latency = cpu_to_le(conn_latency);
+ cp.supervision_timeout = cpu_to_le(supervision_timeout);
+ cp.min_ce_len = cpu_to_le(min_ce_length);
+ cp.max_ce_len = cpu_to_le(max_ce_length);
+
+ const hci_ev_le_conn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_le_create_conn, hci_ev_le_conn_complete>(
+ HCIOpcode::LE_CREATE_CONN, cp, HCIMetaEventType::LE_CONN_COMPLETE, &ev_cc, &status);
+
+ if( HCIErrorCode::SUCCESS != status ) {
+ return status;
+ }
+ if( nullptr != handle_return ) {
+ *handle_return = ev_cc->handle;
+ }
+ return HCIErrorCode::SUCCESS;
+}
+
+HCIErrorCode HCIHandler::create_conn(uint16_t * handle_return, const EUI48 &bdaddr,
+ const uint16_t pkt_type,
+ const uint16_t clock_offset, const uint8_t role_switch) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( nullptr != handle_return ) {
+ *handle_return = 0;
+ }
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ hci_cp_create_conn cp;
+
+ bzero((void*)&cp, sizeof(cp));
+ cp.bdaddr = bdaddr;
+ cp.pkt_type = cpu_to_le((uint16_t)(pkt_type & (uint16_t)ACL_PTYPE_MASK)); /* TODO OK excluding SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) ? */
+ cp.pscan_rep_mode = 0x02; /* TODO magic? */
+ cp.pscan_mode = 0x00; /* TODO magic? */
+ cp.clock_offset = cpu_to_le(clock_offset);
+ cp.role_switch = role_switch;
+
+ const hci_ev_conn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_create_conn, hci_ev_conn_complete>(
+ HCIOpcode::CREATE_CONN, cp, HCIEventType::CONN_COMPLETE, &ev_cc, &status);
+
+ if( HCIErrorCode::SUCCESS != status ) {
+ return status;
+ }
+ if( nullptr != handle_return ) {
+ *handle_return = ev_cc->handle;
+ }
+ return HCIErrorCode::SUCCESS;
+}
+
+HCIErrorCode HCIHandler::disconnect(const uint16_t conn_handle, const HCIErrorCode reason) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ if( 0 == conn_handle ) {
+ return HCIErrorCode::SUCCESS;
+ }
+ hci_cp_disconnect cp;
+
+ bzero(&cp, sizeof(cp));
+ cp.handle = conn_handle;
+ cp.reason = number(reason);
+
+ const hci_ev_disconn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_disconnect, hci_ev_disconn_complete>(
+ HCIOpcode::DISCONNECT, cp, HCIEventType::DISCONN_COMPLETE, &ev_cc, &status);
+ return status;
+}
+
+
+template<typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processCmdCompleteCommand(HCIOpcode opc, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ const HCIEventType evc = HCIEventType::CMD_COMPLETE;
+ HCICommand req0(opc, 0);
+ HCICommandCompleteEvent * ev_cc;
+ std::shared_ptr<HCIEvent> ev = sendWithCmdCompleteReply(req0, &ev_cc);
+ if( nullptr == ev ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr; // timeout
+ } else if( nullptr == ev_cc ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ const uint8_t returnParamSize = ev_cc->getReturnParamSize();
+ if( returnParamSize < sizeof(hci_cmd_event_struct) ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ *res = (const hci_cmd_event_struct*)(ev_cc->getReturnParam());
+ *status = static_cast<HCIErrorCode>((*res)->status);
+ DBG_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ return ev;
+}
+
+template<typename hci_command_struct, typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIEventType evc, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ HCIStructCommand<hci_command_struct> req0(opc, cp);
+ if( !sendCommand(req0) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ std::shared_ptr<HCIEvent> ev = nullptr;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ ev = getNextReply(req0, retryCount);
+ if( nullptr == ev ) {
+ break; // timeout, leave loop
+ } else if( ev->isEvent(evc) ) {
+ break; // gotcha, leave loop
+ } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ // pending command .. wait for result
+ HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
+ if( HCIErrorCode::SUCCESS != ev_cs->getStatus() ) {
+ *status = ev_cs->getStatus();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cs->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ continue; // next packet
+ } else {
+ continue; // next packet
+ }
+ }
+ if( nullptr == ev ) {
+ // timeout exit
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr;
+ }
+ typedef HCIStructCmdCompleteEvt<hci_cmd_event_struct> HCIConnCompleteEvt;
+ HCIConnCompleteEvt * ev_cc = static_cast<HCIConnCompleteEvt*>(ev.get());
+ if( ev_cc->isTypeAndSizeValid(evc) ) {
+ *status = ev_cc->getStatus();
+ *res = ev_cc->getStruct();
+ DBG_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ } else {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ }
+ return ev;
+}
+
+template<typename hci_command_struct, typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIMetaEventType mec, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ HCIStructCommand<hci_command_struct> req0(opc, cp);
+ if( !sendCommand(req0) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ std::shared_ptr<HCIEvent> ev = nullptr;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ ev = getNextReply(req0, retryCount);
+ if( nullptr == ev ) {
+ break; // timeout, leave loop
+ } else if( ev->isMetaEvent(mec) ) {
+ break; // gotcha, leave loop
+ } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ // pending command .. wait for result
+ HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
+ if( HCIErrorCode::SUCCESS != ev_cs->getStatus() ) {
+ *status = ev_cs->getStatus();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cs->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ continue; // next packet
+ } else {
+ continue; // next packet
+ }
+ }
+ if( nullptr == ev ) {
+ // timeout exit
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr;
+ }
+ typedef HCIStructCmdCompleteMetaEvt<hci_cmd_event_struct> HCIConnCompleteMetaEvt;
+ HCIConnCompleteMetaEvt * ev_cc = static_cast<HCIConnCompleteMetaEvt*>(ev.get());
+ if( ev_cc->isTypeAndSizeValid(mec) ) {
+ *status = ev_cc->getStatus();
+ *res = ev_cc->getStruct();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ } else {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Type or size mismatch: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ }
+ return ev;
+}
diff --git a/src/direct_bt/HCITypes.cpp b/src/direct_bt/HCITypes.cpp
new file mode 100644
index 00000000..e5d066dd
--- /dev/null
+++ b/src/direct_bt/HCITypes.cpp
@@ -0,0 +1,299 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+#include <cstdio>
+
+#include <algorithm>
+
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
+#include "HCITypes.hpp"
+
+extern "C" {
+ #include <inttypes.h>
+ #include <unistd.h>
+ #include <sys/param.h>
+ #include <sys/uio.h>
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <poll.h>
+}
+
+namespace direct_bt {
+
+#define HCI_ERROR_CODE(X) \
+ X(SUCCESS) \
+ X(UNKNOWN_HCI_COMMAND) \
+ X(UNKNOWN_CONNECTION_IDENTIFIER) \
+ X(HARDWARE_FAILURE) \
+ X(PAGE_TIMEOUT) \
+ X(AUTHENTICATION_FAILURE) \
+ X(PIN_OR_KEY_MISSING) \
+ X(MEMORY_CAPACITY_EXCEEDED) \
+ X(CONNECTION_TIMEOUT) \
+ X(CONNECTION_LIMIT_EXCEEDED) \
+ X(SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED) \
+ X(CONNECTION_ALREADY_EXISTS) \
+ X(COMMAND_DISALLOWED) \
+ X(CONNECTION_REJECTED_LIMITED_RESOURCES) \
+ X(CONNECTION_REJECTED_SECURITY) \
+ X(CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR) \
+ X(CONNECTION_ACCEPT_TIMEOUT_EXCEEDED) \
+ X(UNSUPPORTED_FEATURE_OR_PARAM_VALUE) \
+ X(INVALID_HCI_COMMAND_PARAMETERS) \
+ X(REMOTE_USER_TERMINATED_CONNECTION) \
+ X(REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES) \
+ X(REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF) \
+ X(CONNECTION_TERMINATED_BY_LOCAL_HOST) \
+ X(REPEATED_ATTEMPTS) \
+ X(PAIRING_NOT_ALLOWED) \
+ X(UNKNOWN_LMP_PDU) \
+ X(UNSUPPORTED_REMOTE_OR_LMP_FEATURE) \
+ X(SCO_OFFSET_REJECTED) \
+ X(SCO_INTERVAL_REJECTED) \
+ X(SCO_AIR_MODE_REJECTED) \
+ X(INVALID_LMP_OR_LL_PARAMETERS) \
+ X(UNSPECIFIED_ERROR) \
+ X(UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE) \
+ X(ROLE_CHANGE_NOT_ALLOWED) \
+ X(LMP_OR_LL_RESPONSE_TIMEOUT) \
+ X(LMP_OR_LL_COLLISION) \
+ X(LMP_PDU_NOT_ALLOWED) \
+ X(ENCRYPTION_MODE_NOT_ACCEPTED) \
+ X(LINK_KEY_CANNOT_BE_CHANGED) \
+ X(REQUESTED_QOS_NOT_SUPPORTED) \
+ X(INSTANT_PASSED) \
+ X(PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) \
+ X(DIFFERENT_TRANSACTION_COLLISION) \
+ X(QOS_UNACCEPTABLE_PARAMETER) \
+ X(QOS_REJECTED) \
+ X(CHANNEL_ASSESSMENT_NOT_SUPPORTED) \
+ X(INSUFFICIENT_SECURITY) \
+ X(PARAMETER_OUT_OF_RANGE) \
+ X(ROLE_SWITCH_PENDING) \
+ X(RESERVED_SLOT_VIOLATION) \
+ X(ROLE_SWITCH_FAILED) \
+ X(EIR_TOO_LARGE) \
+ X(SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST) \
+ X(HOST_BUSY_PAIRING) \
+ X(CONNECTION_REJECTED_NO_SUITABLE_CHANNEL) \
+ X(CONTROLLER_BUSY) \
+ X(UNACCEPTABLE_CONNECTION_PARAM) \
+ X(ADVERTISING_TIMEOUT) \
+ X(CONNECTION_TERMINATED_MIC_FAILURE) \
+ X(CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT) \
+ X(MAX_CONNECTION_FAILED) \
+ X(COARSE_CLOCK_ADJ_REJECTED) \
+ X(TYPE0_SUBMAP_NOT_DEFINED) \
+ X(UNKNOWN_ADVERTISING_IDENTIFIER) \
+ X(LIMIT_REACHED) \
+ X(OPERATION_CANCELLED_BY_HOST) \
+ X(PACKET_TOO_LONG) \
+ X(INTERNAL_FAILURE) \
+ X(UNKNOWN)
+
+#define HCI_ERROR_CODE_CASE_TO_STRING(V) case HCIErrorCode::V: return #V;
+
+std::string getHCIErrorCodeString(const HCIErrorCode ec) {
+ switch(ec) {
+ HCI_ERROR_CODE(HCI_ERROR_CODE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIErrorCode";
+}
+
+std::string getHCIPacketTypeString(const HCIPacketType op) {
+ switch(op) {
+ case HCIPacketType::COMMAND: return "COMMAND";
+ case HCIPacketType::ACLDATA: return "ACLDATA";
+ case HCIPacketType::SCODATA: return "SCODATA";
+ case HCIPacketType::EVENT: return "EVENT";
+ case HCIPacketType::DIAG: return "DIAG";
+ case HCIPacketType::VENDOR: return "VENDOR";
+ }
+ return "Unknown HCIPacketType";
+}
+
+std::string getHCIOGFString(const HCIOGF op) {
+ (void)op;
+ return "";
+}
+
+#define HCI_OPCODE(X) \
+ X(SPECIAL) \
+ X(CREATE_CONN) \
+ X(DISCONNECT) \
+ X(SET_EVENT_MASK) \
+ X(RESET) \
+ X(READ_LOCAL_VERSION) \
+ X(LE_SET_EVENT_MASK) \
+ X(LE_READ_BUFFER_SIZE) \
+ X(LE_READ_LOCAL_FEATURES) \
+ X(LE_SET_RANDOM_ADDR) \
+ X(LE_SET_ADV_PARAM) \
+ X(LE_READ_ADV_TX_POWER) \
+ X(LE_SET_ADV_DATA) \
+ X(LE_SET_SCAN_RSP_DATA) \
+ X(LE_SET_ADV_ENABLE) \
+ X(LE_SET_SCAN_PARAM) \
+ X(LE_SET_SCAN_ENABLE) \
+ X(LE_CREATE_CONN) \
+ X(LE_CREATE_CONN_CANCEL) \
+ X(LE_READ_WHITE_LIST_SIZE) \
+ X(LE_CLEAR_WHITE_LIST) \
+ X(LE_ADD_TO_WHITE_LIST) \
+ X(LE_DEL_FROM_WHITE_LIST) \
+ X(LE_CONN_UPDATE) \
+ X(LE_READ_REMOTE_FEATURES) \
+ X(LE_START_ENC)
+
+#define HCI_OPCODE_CASE_TO_STRING(V) case HCIOpcode::V: return #V;
+
+std::string getHCIOpcodeString(const HCIOpcode op) {
+ switch(op) {
+ HCI_OPCODE(HCI_OPCODE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIOpcode";
+}
+
+#define HCI_EVENTTYPE(X) \
+ X(INVALID) \
+ X(INQUIRY_COMPLETE) \
+ X(INQUIRY_RESULT) \
+ X(CONN_COMPLETE) \
+ X(CONN_REQUEST) \
+ X(DISCONN_COMPLETE) \
+ X(AUTH_COMPLETE) \
+ X(REMOTE_NAME) \
+ X(ENCRYPT_CHANGE) \
+ X(CHANGE_LINK_KEY_COMPLETE) \
+ X(REMOTE_FEATURES) \
+ X(REMOTE_VERSION) \
+ X(QOS_SETUP_COMPLETE) \
+ X(CMD_COMPLETE) \
+ X(CMD_STATUS) \
+ X(HARDWARE_ERROR) \
+ X(ROLE_CHANGE) \
+ X(NUM_COMP_PKTS) \
+ X(MODE_CHANGE) \
+ X(PIN_CODE_REQ) \
+ X(LINK_KEY_REQ) \
+ X(LINK_KEY_NOTIFY) \
+ X(CLOCK_OFFSET) \
+ X(PKT_TYPE_CHANGE) \
+ X(DISCONN_PHY_LINK_COMPLETE) \
+ X(DISCONN_LOGICAL_LINK_COMPLETE) \
+ X(LE_META)
+
+#define HCI_EVENTTYPE_CASE_TO_STRING(V) case HCIEventType::V: return #V;
+
+std::string getHCIEventTypeString(const HCIEventType op) {
+ switch(op) {
+ HCI_EVENTTYPE(HCI_EVENTTYPE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIEventType";
+}
+
+#define HCI_METATYPE(X) \
+ X(INVALID) \
+ X(LE_CONN_COMPLETE) \
+ X(LE_ADVERTISING_REPORT) \
+ X(LE_CONN_UPDATE_COMPLETE) \
+ X(LE_REMOTE_FEAT_COMPLETE) \
+ X(LE_LTKEY_REQUEST) \
+ X(LE_REMOTE_CONN_PARAM_REQ) \
+ X(LE_DATA_LENGTH_CHANGE) \
+ X(LE_READ_LOCAL_P256_PUBKEY_COMPLETE) \
+ X(LE_GENERATE_DHKEY_COMPLETE) \
+ X(LE_ENHANCED_CONN_COMPLETE) \
+ X(LE_DIRECT_ADV_REPORT) \
+ X(LE_PHY_UPDATE_COMPLETE) \
+ X(LE_EXT_ADV_REPORT) \
+ X(LE_PERIODIC_ADV_SYNC_ESTABLISHED) \
+ X(LE_PERIODIC_ADV_REPORT) \
+ X(LE_PERIODIC_ADV_SYNC_LOST) \
+ X(LE_SCAN_TIMEOUT) \
+ X(LE_ADV_SET_TERMINATED) \
+ X(LE_SCAN_REQ_RECEIVED) \
+ X(LE_CHANNEL_SEL_ALGO) \
+ X(LE_CONNLESS_IQ_REPORT) \
+ X(LE_CONN_IQ_REPORT) \
+ X(LE_CTE_REQ_FAILED) \
+ X(LE_PERIODIC_ADV_SYNC_TRANSFER_RECV) \
+ X(LE_CIS_ESTABLISHED) \
+ X(LE_CIS_REQUEST) \
+ X(LE_CREATE_BIG_COMPLETE) \
+ X(LE_TERMINATE_BIG_COMPLETE) \
+ X(LE_BIG_SYNC_ESTABLISHED) \
+ X(LE_BIG_SYNC_LOST) \
+ X(LE_REQUEST_PEER_SCA_COMPLETE) \
+ X(LE_PATH_LOSS_THRESHOLD) \
+ X(LE_TRANSMIT_POWER_REPORTING) \
+ X(LE_BIGINFO_ADV_REPORT)
+
+#define HCI_METATYPE_CASE_TO_STRING(V) case HCIMetaEventType::V: return #V;
+
+std::string getHCIMetaEventTypeString(const HCIMetaEventType op) {
+ switch(op) {
+ HCI_METATYPE(HCI_METATYPE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIMetaType";
+}
+
+HCIEvent* HCIEvent::getSpecialized(const uint8_t * buffer, int const buffer_size) {
+ const HCIPacketType pc = static_cast<HCIPacketType>( get_uint8(buffer, 0) );
+ if( HCIPacketType::EVENT != pc ) {
+ return nullptr;
+ }
+ const HCIEventType ec = static_cast<HCIEventType>( get_uint8(buffer, 1) );
+
+ switch( ec ) {
+ case HCIEventType::DISCONN_COMPLETE:
+ return new HCIDisconnectionCompleteEvent(buffer, buffer_size);
+ case HCIEventType::CMD_COMPLETE:
+ return new HCICommandCompleteEvent(buffer, buffer_size);
+ case HCIEventType::CMD_STATUS:
+ return new HCICommandStatusEvent(buffer, buffer_size);
+ case HCIEventType::LE_META:
+ // No need to HCIMetaType specializations as we use HCIStructCmdCompleteMetaEvt template
+ // based on HCIMetaEvent.
+ return new HCIMetaEvent(buffer, buffer_size);
+ default:
+ // No further specialization, use HCIStructCmdCompleteEvt template
+ return new HCIEvent(buffer, buffer_size);
+ }
+}
+
+} /* namespace direct_bt */