diff options
Diffstat (limited to 'api/direct_bt')
-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 { |