diff options
author | Sven Gothel <[email protected]> | 2020-06-04 04:11:12 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-06-04 04:11:12 +0200 |
commit | 73f5b4fc21d7a14aa174f2c48657ca3f2229dda1 (patch) | |
tree | 1b455d3e699600eb9f0c1ccc09d10ffd8052929b /api | |
parent | 1c449ba2a4cede1e942ff9df181bf96a408d2c67 (diff) |
Introduce HCITypes + HCIHandler: Providing C++ basics for full HCI support; Misc..
Introduce HCITypes + HCIHandler: Providing C++ basics for full HCI support
============================================================================
- HCITypes defines the HCI command and event data structures,
similar to MgmtTypes and ATTPDUTypes.
For more coding efficiency we use template command and event classes, allowing to reuse
the HCIIoctl struct types being wrapped in the C++ classes
for flow and lifecycle control.
HCITypes further includes all essential 'enum class' command opcodes, event types etc,
ensuring type safety.
- HCIHandler defines the command/event workflow similar to GATTHandler and DBTManager.
One HCIHandler per DBTAdapter is being used, each HCIHandler using its
event reader thread and ringbuffer.
Socket event filtering and manual le-meta filtering ensures reduced workload (DoS safety).
Similar to HCITypes, we use template function to utilize
the template command and event classes, reuising the HCIIoctl struct types...
HCIHandler can be extended for more functionality as well as used to implement
our own DBTManager, i.e. Reducing HCIEvents to MgmtEvents with callback support.
At least this is a viable option now, if so desired (non-linux, etc).
- HCIComm: Contains the core I/O functionality only, dropped all semantic HCI code.
The latter resides in HCIHandler now.
- DBTAdapter now uses HCIHander instead of HCIComm, renamed field to simply 'hci'
Misc..
=======
- reduced all ringbuffer capacity to 128 from 256
- Moved Mgmt*EventCallback from DBTManager to MgmtTypes,
as we might want to reuse the MgmtTypes incl callbacks
for alternative DBTManager implementations.
- Added 'enum class' to underlying number type conversion pattern:
<NumberType> number('enum class type')
Avoiding the many static_cast<NumberType>(..) directives.
- align all Type -> String method names: 'get<Type>String(Type)'
- BasicTypes: Add safe uint32_t bitfield mask helper
Diffstat (limited to 'api')
-rw-r--r-- | api/direct_bt/BTTypes.hpp | 7 | ||||
-rw-r--r-- | api/direct_bt/BasicTypes.hpp | 20 | ||||
-rw-r--r-- | api/direct_bt/DBTAdapter.hpp | 19 | ||||
-rw-r--r-- | api/direct_bt/DBTDevice.hpp | 2 | ||||
-rw-r--r-- | api/direct_bt/DBTManager.hpp | 48 | ||||
-rw-r--r-- | api/direct_bt/DBTTypes.hpp | 6 | ||||
-rw-r--r-- | api/direct_bt/GATTHandler.hpp | 3 | ||||
-rw-r--r-- | api/direct_bt/HCIComm.hpp | 173 | ||||
-rw-r--r-- | api/direct_bt/HCIHandler.hpp | 200 | ||||
-rw-r--r-- | api/direct_bt/HCITypes.hpp | 815 | ||||
-rw-r--r-- | api/direct_bt/MgmtTypes.hpp | 43 | ||||
-rw-r--r-- | api/direct_bt/OctetTypes.hpp | 3 |
12 files changed, 1108 insertions, 231 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 { |