diff options
-rw-r--r-- | api/direct_bt/DBGattServer.hpp | 304 | ||||
-rw-r--r-- | examples/dbt_peripheral00.cpp | 90 |
2 files changed, 394 insertions, 0 deletions
diff --git a/api/direct_bt/DBGattServer.hpp b/api/direct_bt/DBGattServer.hpp new file mode 100644 index 00000000..c397925b --- /dev/null +++ b/api/direct_bt/DBGattServer.hpp @@ -0,0 +1,304 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2021 Gothel Software e.K. + * Copyright (c) 2021 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 DB_GATT_SERVER_HPP_ +#define DB_GATT_SERVER_HPP_ + +#include <cstring> +#include <string> +#include <memory> +#include <cstdint> + +#include <mutex> +#include <atomic> + +#include <jau/java_uplink.hpp> +#include <jau/octets.hpp> +#include <jau/uuid.hpp> + +#include "BTTypes0.hpp" +#include "ATTPDUTypes.hpp" + +#include "BTTypes1.hpp" + +// #include "BTGattDesc.hpp" +#include "BTGattChar.hpp" +// #include "BTGattService.hpp" + + +/** + * - - - - - - - - - - - - - - - + * + * Module DBGattServer covering the GattServer elements: + * + * - 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 + */ +namespace direct_bt { + + /** + * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3 Characteristic Descriptor + */ + class DBGattDesc { + public: + /** + * Characteristic Descriptor Handle + * <p> + * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). + * </p> + */ + uint16_t handle; + + /** Type of descriptor */ + std::shared_ptr<const jau::uuid_t> type; + + /* Characteristics Descriptor's Value */ + jau::POctets value; + + DBGattDesc(const std::shared_ptr<const jau::uuid_t>& type_, + const jau::TROOctets& value_) noexcept + : handle(0), type(type_), value(value_) {} + + std::string toString() const noexcept { + return "Desc[type 0x"+type->toString()+", handle "+jau::to_hexstring(handle)+", value["+value.toString()+"]]"; + } + + /** Value is uint16_t bitfield */ + bool isExtendedProperties() const noexcept { return BTGattDesc::TYPE_EXT_PROP == *type; } + + /* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration (Characteristic Descriptor, optional, single, uint16_t bitfield) */ + bool isClientCharConfig() const noexcept{ return BTGattDesc::TYPE_CCC_DESC == *type; } + }; + inline bool operator==(const DBGattDesc& lhs, const DBGattDesc& rhs) noexcept + { return lhs.handle == rhs.handle; /** unique attribute handles */ } + + inline bool operator!=(const DBGattDesc& lhs, const DBGattDesc& rhs) noexcept + { return !(lhs == rhs); } + + /** + * <p> + * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1 Characteristic Declaration Attribute Value + * </p> + * handle -> CDAV value + * <p> + * BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service + * + * Here the handle is a service's characteristics-declaration + * and the value the Characteristics Property, Characteristics Value Handle _and_ Characteristics UUID. + * </p> + */ + class DBGattChar { + private: + bool enabledNotifyState = false; + bool enabledIndicateState = false; + + public: + class Listener { + public: + virtual bool readValue(jau::POctets & res) const noexcept = 0; + + virtual bool writeValue(const jau::TROOctets & value) const noexcept = 0; + virtual bool writeValueNoResp(const jau::TROOctets & value) const noexcept = 0; + + virtual ~Listener() noexcept {} + }; + + /** + * Characteristic Handle of this instance. + * <p> + * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). + * </p> + */ + uint16_t handle; + + /** + * Characteristics Value Handle. + * <p> + * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). + * </p> + */ + uint16_t value_handle; + + /* Characteristics Value Type UUID */ + std::shared_ptr<const jau::uuid_t> value_type; + + /* Characteristics Property */ + BTGattChar::PropertyBitVal properties; + + /** List of Characteristic Descriptions as shared reference */ + jau::darray<DBGattDesc> descriptors; + + /* Characteristics Descriptor's Value */ + jau::POctets value; + + /* Optional Client Characteristic Configuration index within descriptorList */ + int clientCharConfigIndex; + + DBGattChar(const std::shared_ptr<const jau::uuid_t>& value_type_, + const BTGattChar::PropertyBitVal properties_, + const jau::darray<DBGattDesc>& descriptors_, + const jau::TROOctets & value_) noexcept + : handle(0), value_handle(0), + value_type(value_type_), + properties(properties_), + descriptors(descriptors_), + value(value_), + clientCharConfigIndex(-1) + { + int i=0; + // C++11: Range-based for loop: [begin, end[ + for(DBGattDesc& d : descriptors) { + if( d.isClientCharConfig() ) { + clientCharConfigIndex=i; + break; + } + ++i; + } + } + + bool hasProperties(const BTGattChar::PropertyBitVal v) const noexcept { return v == ( properties & v ); } + + std::string toString() const noexcept { + std::string notify_str; + if( hasProperties(BTGattChar::PropertyBitVal::Notify) || hasProperties(BTGattChar::PropertyBitVal::Indicate) ) { + notify_str = ", enabled[notify "+std::to_string(enabledNotifyState)+", indicate "+std::to_string(enabledIndicateState)+"]"; + } + return "Char[handle "+jau::to_hexstring(handle)+", props "+jau::to_hexstring(properties)+" "+BTGattChar::getPropertiesString(properties)+ + ", value[handle "+jau::to_hexstring(value_handle)+ + "], ccd-idx "+std::to_string(clientCharConfigIndex)+notify_str+"]"; + } + + DBGattDesc* getClientCharConfig() noexcept { + if( 0 > clientCharConfigIndex ) { + return nullptr; + } + return &descriptors.at(static_cast<size_t>(clientCharConfigIndex)); // abort if out of bounds + } + }; + inline bool operator==(const DBGattChar& lhs, const DBGattChar& rhs) noexcept + { return lhs.handle == rhs.handle; /** unique attribute handles */ } + + inline bool operator!=(const DBGattChar& lhs, const DBGattChar& rhs) noexcept + { return !(lhs == rhs); } + + /** + * Representing a complete [Primary] Service Declaration + * including its list of Characteristic Declarations, + * which also may include its client config if available. + */ + class DBGattService { + public: + bool primary; + + /** + * Service start handle + * <p> + * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). + * </p> + */ + uint16_t handle; + + /** + * Service end handle, inclusive. + * <p> + * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). + * </p> + */ + uint16_t end_handle; + + /** Service type UUID */ + std::shared_ptr<const jau::uuid_t> type; + + /** List of Characteristic Declarations as shared reference */ + jau::darray<DBGattChar> characteristics; + + DBGattService(const bool primary_, + const std::shared_ptr<const jau::uuid_t>& type_, + const jau::darray<DBGattChar>& characteristics_) noexcept + : primary(primary_), handle(0), end_handle(0), + type(type_), + characteristics(characteristics_) + { } + + /** + * Sets all handles of this service instance and all its owned childs, + * i.e. DBGattChars elements and its DBGattDesc elements. + * + * @param start_handle a valid and unique start handle number > 0 + * @return number of set handles, i.e. `( end_handle - handle ) + 1` + */ + int setHandles(const uint16_t start_handle) { + if( 0 == start_handle ) { + handle = 0; + end_handle = 0; + return 0; + } + uint16_t h = start_handle; + handle = h++; + for(DBGattChar& c : characteristics) { + c.handle = h++; + c.value_handle = h++; + for(DBGattDesc& d : c.descriptors) { + d.handle = h++; + } + } + end_handle = h; + return ( end_handle - handle ) + 1; + } + std::string toString() const noexcept { + return "Srvc[type 0x"+type->toString()+", handle ["+jau::to_hexstring(handle)+".."+jau::to_hexstring(end_handle)+"], "+ + std::to_string(characteristics.size())+" chars]"; + + } + }; + inline bool operator==(const DBGattService& lhs, const DBGattService& rhs) noexcept + { return lhs.handle == rhs.handle && lhs.end_handle == rhs.end_handle; /** unique attribute handles */ } + + inline bool operator!=(const DBGattService& lhs, const DBGattService& rhs) noexcept + { return !(lhs == rhs); } + + typedef jau::darray<DBGattService> Services; + + /** + * Sets all handles of all service instances and all its owned childs, + * i.e. DBGattChars elements and its DBGattDesc elements. + * + * @param start_handle a valid and unique start handle number > 0 + * @return number of set handles, i.e. `( end_handle - handle ) + 1` + */ + inline int setServicesHandles(const uint16_t start_handle, Services srvcs) { + int c = 0; + uint16_t h = start_handle; + for(DBGattService s : srvcs) { + int l = s.setHandles(h); + c += l; + h += l; // end + 1 for next service + } + return c; + } + +} // namespace direct_bt + +#endif /* DB_GATT_SERVER_HPP_ */ diff --git a/examples/dbt_peripheral00.cpp b/examples/dbt_peripheral00.cpp index 5d58c138..e7dc6210 100644 --- a/examples/dbt_peripheral00.cpp +++ b/examples/dbt_peripheral00.cpp @@ -41,6 +41,8 @@ #include <jau/darray.hpp> #include <direct_bt/DirectBT.hpp> +#include <direct_bt/GattNumbers.hpp> +#include <direct_bt/DBGattServer.hpp> extern "C" { #include <unistd.h> @@ -67,6 +69,82 @@ static bool SHOW_UPDATE_EVENTS = false; static bool startAdvertising(BTAdapter *a, std::string msg); +static jau::POctets make_poctets(const char* name) { + return jau::POctets( (const uint8_t*)name, (nsize_t)strlen(name), endian::little ); +} +static jau::POctets make_poctets(const uint16_t v) { + jau::POctets p( 2, endian::little); + p.put_uint16_nc(0, v); + return p; +} + +const DBGattService GenericAccess ( + true /* primary */, + std::make_unique<const jau::uuid16_t>(GattServiceType::GENERIC_ACCESS) /* type_ */, + /* characteristics: */ { + DBGattChar( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */, + BTGattChar::PropertyBitVal::Read, + /* descriptors: */ { }, + make_poctets("Synthethic Sensor 01") /* value */ + ), + DBGattChar( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::APPEARANCE) /* value_type_ */, + BTGattChar::PropertyBitVal::Read, + /* descriptors: */ { }, + make_poctets((uint16_t)0) /* value */ + ) + } + ); + +void lala() { + const std::shared_ptr<BTGattHandler> handler; + + /** + * - H 1: Service [type 0x1800, handle [0x0001..0x0007] - GENERIC_ACCESS, 3 characteristics] + * + * - H 2,3: Characteristic [handle 0x0002, props 0x02 [read], value[type 0x2a00, handle 0x0003, DEVICE_NAME] + * - H 4: Descriptor [type 0x2803, handle 0x0004, value[...]] + * - H 5: Descriptor [type 0x2801, handle 0x0005, value[...]] + * + * - H 4,5: Characteristic [handle 0x0004, props 0x02 [read], value[type 0x2a01, handle 0x0005, SOMETHING] + * - H 6: Descriptor [type 0x2803, handle 0x0006, value[...]] + * - H 7: Descriptor [type 0x2804, handle 0x0007, value[...]] + * + * - Characteristic [handle 0x0006, props 0x02 [read], value[type 0x2a04, handle 0x0007, SOMETHING] + * + * + * Char.S1C1 : handles[c1hs..c1he] + * Descr.S1C1D1: handles[d1hs..d1he] + */ + { + uint16_t handle=1; + BTGattServiceRef s1( std::make_shared<BTGattService>( + handler, true /* isPrimary_ */, + handle /* startHandle_ */, handle+6 /* endHandle_ */, + std::make_unique<const jau::uuid16_t>(GattServiceType::GENERIC_ACCESS) /* type_ */) ); + + BTGattCharRef s1c1( std::make_shared<BTGattChar>( + s1, handle+1 /* const uint16_t handle_ */, + BTGattChar::PropertyBitVal::Read, + handle+2 /* value_handle_*/, + std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */) ); + handle += 2; + BTGattCharRef s1c2( std::make_shared<BTGattChar>( + s1, handle+1 /* const uint16_t handle_ */, + BTGattChar::PropertyBitVal::Read, + handle+2 /* value_handle_*/, + std::make_unique<const jau::uuid16_t>(GattCharacteristicType::APPEARANCE) /* value_type_ */) ); + handle += 2; + } + { + DBGattChar ga_c1( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */, + BTGattChar::PropertyBitVal::Read, + /* descriptors: */ { }, + make_poctets("Synthethic Sensor 01") /* value */ ); + + } + +} + class MyAdapterStatusListener : public AdapterStatusListener { void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, @@ -242,6 +320,18 @@ static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { return false; } // adapter is powered-on + fprintf_td(stderr, "initAdapter: %s\n", adapter->toString().c_str()); + { + const LE_Features le_feats = adapter->getLEFeatures(); + fprintf_td(stderr, "initAdapter: LE_Features %s\n", to_string(le_feats).c_str()); + } + { + LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M }; + HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx); + fprintf_td(stderr, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", + to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); + } + std::shared_ptr<AdapterStatusListener> asl(new MyAdapterStatusListener()); adapter->addStatusListener( asl ); // Flush discovered devices after registering our status listener. |