/* * Author: Sven Gothel * 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 GATT_HANDLER_HPP_ #define GATT_HANDLER_HPP_ #include #include #include #include #include #include #include #include #include "UUID.hpp" #include "BTTypes.hpp" #include "L2CAPComm.hpp" #include "ATTPDUTypes.hpp" #include "GATTTypes.hpp" #include "LFRingbuffer.hpp" /** * - - - - - - - - - - - - - - - * * Module GATTHandler: * * - BT Core Spec v5.2: Vol 3, Part G Generic Attribute Protocol (GATT) * - BT Core Spec v5.2: Vol 3, Part G GATT: 2.6 GATT Profile Hierarchy * - BT Core Spec v5.2: Vol 3, Part G GATT: 3.4 Summary of GATT Profile Attribute Types */ namespace direct_bt { class DBTDevice; // forward /** * A thread safe GATT handler associated to one device via one L2CAP connection. *

* Implementation utilizes a lock free ringbuffer receiving data within its separate thread. *

*/ class GATTHandler { public: enum class Defaults : int { /* BT Core Spec v5.2: Vol 3, Part F 3.2.8: Maximum length of an attribute value. */ MAX_ATT_MTU = 512, /* BT Core Spec v5.2: Vol 3, Part G GATT: 5.2.1 ATT_MTU */ MIN_ATT_MTU = 23, /** 3s poll timeout for l2cap reader thread */ L2CAP_READER_THREAD_POLL_TIMEOUT = 3000, /** 500ms timeout for l2cap command replies */ L2CAP_COMMAND_REPLY_TIMEOUT = 500, /** Medium ringbuffer capacity... */ ATTPDU_RING_CAPACITY = 128 }; static inline int number(const Defaults d) { return static_cast(d); } private: /** GATTHandle's device weak back-reference */ std::weak_ptr wbr_device; const std::string deviceString; std::recursive_mutex mtx_write; std::recursive_mutex mtx_command; POctets rbuffer; L2CAPComm l2cap; const int replyTimeoutMS; std::atomic isConnected; // reflects state std::atomic hasIOError; // reflects state LFRingbuffer, nullptr> attPDURing; std::atomic l2capReaderThreadId; std::atomic l2capReaderRunning; std::atomic l2capReaderShallStop; std::mutex mtx_l2capReaderInit; std::condition_variable cv_l2capReaderInit; /** send immediate confirmation of indication events from device, defaults to true. */ bool sendIndicationConfirmation = true; std::vector> eventListenerList; std::recursive_mutex mtx_eventListenerList; uint16_t serverMTU; uint16_t usedMTU; std::vector services; std::shared_ptr getDevice() const { return wbr_device.lock(); } bool validateConnected(); void l2capReaderThreadImpl(); void send(const AttPDUMsg & msg); std::shared_ptr sendWithReply(const AttPDUMsg & msg); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 3.4.2 MTU Exchange *

* Returns the server-mtu if successful, otherwise 0. *

*/ uint16_t exchangeMTU(const uint16_t clientMaxMTU=number(Defaults::MAX_ATT_MTU)); public: GATTHandler(const std::shared_ptr & device, const int replyTimeoutMS = number(Defaults::L2CAP_COMMAND_REPLY_TIMEOUT)); ~GATTHandler(); bool getIsConnected() const { return isConnected; } bool getHasIOError() const { return hasIOError; } std::string getStateString() const { return L2CAPComm::getStateString(isConnected, hasIOError); } /** * After successful l2cap connection, the MTU will be exchanged. * See getServerMTU() and getUsedMTU(), the latter is in use. */ bool connect(); /** * Disconnect this GATTHandler and optionally the associated device * @param disconnectDevice if true, associated device will also be disconnected, otherwise not. * @param ioErrorCause if true, reason for disconnection is an IO error * @return true if successful, otherwise false */ bool disconnect(const bool disconnectDevice, const bool ioErrorCause); bool isOpen() const { return isConnected && l2cap.isOpen(); } uint16_t getServerMTU() const { return serverMTU; } uint16_t getUsedMTU() const { return usedMTU; } /** * Find and return the GATTCharacterisicsDecl within internal primary services * via given characteristic value handle. *

* Returns nullptr if not found. *

*/ GATTCharacteristicRef findCharacterisicsByValueHandle(const uint16_t charValueHandle); /** * Find and return the GATTCharacterisicsDecl within given list of primary services * via given characteristic value handle. *

* Returns nullptr if not found. *

*/ GATTCharacteristicRef findCharacterisicsByValueHandle(const uint16_t charValueHandle, std::vector &services); /** * Find and return the GATTCharacterisicsDecl within given primary service * via given characteristic value handle. *

* Returns nullptr if not found. *

*/ GATTCharacteristicRef findCharacterisicsByValueHandle(const uint16_t charValueHandle, GATTServiceRef service); /** * Discover all primary services _and_ all its characteristics declarations * including their client config. *

* BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.1 Discover All Primary Services *

* Method returns reference to GATTHandler internal data. */ std::vector & discoverCompletePrimaryServices(); /** * Returns a reference of the internal kept GATTService list. *

* The internal list will be populated via {@link #discoverCompletePrimaryServices()}. *

*/ std::vector & getServices() { return services; } /** * Discover all primary services _only_. *

* BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.1 Discover All Primary Services *

*/ bool discoverPrimaryServices(std::vector & result); /** * Discover all characteristics of a service and declaration attributes _only_. *

* BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service *

*

* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1 Characterisic Declaration Attribute Value *

*/ bool discoverCharacteristics(GATTServiceRef & service); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.7.1 Discover All Characteristic Descriptors */ bool discoverDescriptors(GATTServiceRef & service); /** * Generic read GATT value and long value *

* If expectedLength = 0, then only one ATT_READ_REQ/RSP will be used. *

*

* If expectedLength < 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used until * the response returns zero. This is the default parameter. *

*

* If expectedLength > 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used * if required until the response returns zero. *

*/ bool readValue(const uint16_t handle, POctets & res, int expectedLength=-1); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value *

* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value *

*

* If expectedLength = 0, then only one ATT_READ_REQ/RSP will be used. *

*

* If expectedLength < 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used until * the response returns zero. This is the default parameter. *

*

* If expectedLength > 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used * if required until the response returns zero. *

*/ bool readCharacteristicValue(const GATTCharacteristic & c, POctets & res, int expectedLength=-1); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.1 Read Characteristic Descriptor *

* BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.2 Read Long Characteristic Descriptor *

*

* If expectedLength = 0, then only one ATT_READ_REQ/RSP will be used. *

*

* If expectedLength < 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used until * the response returns zero. This is the default parameter. *

*

* If expectedLength > 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used * if required until the response returns zero. *

*/ bool readDescriptorValue(GATTDescriptor & cd, int expectedLength=-1); /** * Generic write GATT value and long value */ bool writeValue(const uint16_t handle, const TROOctets & value, const bool expResponse); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.3 Write Characteristic Descriptors *

* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3 Characteristic Descriptor *

*

* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration *

*/ bool writeDescriptorValue(const GATTDescriptor & cd); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value */ bool writeCharacteristicValue(const GATTCharacteristic & c, const TROOctets & value); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response */ bool writeCharacteristicValueNoResp(const GATTCharacteristic & c, const TROOctets & value); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration *

* Throws an IllegalArgumentException if the given GATTDescriptor is not a ClientCharacteristicConfiguration. *

*/ bool configIndicationNotification(GATTDescriptor & cd, const bool enableNotification, const bool enableIndication); /** * Add the given listener to the list if not already present. *

* Returns true if the given listener is not element of the list and has been newly added, * otherwise false. *

*/ bool addCharacteristicListener(std::shared_ptr l); /** * Remove the given listener from the list. *

* Returns true if the given listener is an element of the list and has been removed, * otherwise false. *

*/ bool removeCharacteristicListener(std::shared_ptr l); /** * Remove the given listener from the list. *

* Returns true if the given listener is an element of the list and has been removed, * otherwise false. *

*/ bool removeCharacteristicListener(const GATTCharacteristicListener * l); /** * Remove all event listener from the list. *

* Returns the number of removed event listener. *

*/ int removeAllCharacteristicListener(); /** * Enable or disable sending an immediate confirmation for received indication events from the device. *

* Default value is true. *

*

* This setting is per GATTHandler and hence per DBTDevice. *

*/ void setSendIndicationConfirmation(const bool v); /** * Returns whether sending an immediate confirmation for received indication events from the device is enabled. *

* Default value is true. *

*

* This setting is per GATTHandler and hence per DBTDevice. *

*/ bool getSendIndicationConfirmation(); /*****************************************************/ /** Higher level semantic functionality **/ /*****************************************************/ std::shared_ptr getGenericAccess(std::vector & primServices); std::shared_ptr getGenericAccess(std::vector & genericAccessCharDeclList); std::shared_ptr getDeviceInformation(std::vector & primServices); std::shared_ptr getDeviceInformation(std::vector & deviceInfoCharDeclList); /** * Issues a ping to the device, validating whether it is still reachable. *

* This method could be periodically utilized to shorten the underlying OS disconnect period * after turning the device off, which lies within 7-13s. *

*

* In case the device is no more reachable, disconnect will be initiated due to the occurring IO error. *

* @return {@code true} if successful, otherwise false in case no GATT services exists etc. */ bool ping(); }; } // namespace direct_bt #endif /* GATT_HANDLER_HPP_ */