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