From 121c35153e4e131fdab3cce5dd89723defe566e1 Mon Sep 17 00:00:00 2001 From: Richard Peters <40753452+richardapeters@users.noreply.github.com> Date: Tue, 9 May 2023 10:55:20 +0200 Subject: [PATCH] feat: add GATT Client (discovery attributes) (#131) Merged PR 11381: [ECR] GATT Client (discovery attributes) GATT Client (discovery attributes). (cherry picked from commit f128b77ef3378c861caaf9d237c5113bbe9b6440) Co-authored-by: Santos, Gabriel --- .../middlewares/ble_middleware/CMakeLists.txt | 4 + .../ble_middleware/GattClientSt.cpp | 235 ++++++++++++++++++ .../ble_middleware/GattClientSt.hpp | 66 +++++ .../ble_middleware/TracingGattClientSt.cpp | 43 ++++ .../ble_middleware/TracingGattClientSt.hpp | 25 ++ .../ble_middleware/TracingGattServerSt.cpp | 8 +- 6 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 hal_st/middlewares/ble_middleware/GattClientSt.cpp create mode 100644 hal_st/middlewares/ble_middleware/GattClientSt.hpp create mode 100644 hal_st/middlewares/ble_middleware/TracingGattClientSt.cpp create mode 100644 hal_st/middlewares/ble_middleware/TracingGattClientSt.hpp diff --git a/hal_st/middlewares/ble_middleware/CMakeLists.txt b/hal_st/middlewares/ble_middleware/CMakeLists.txt index 3cf72a52..c44f9205 100644 --- a/hal_st/middlewares/ble_middleware/CMakeLists.txt +++ b/hal_st/middlewares/ble_middleware/CMakeLists.txt @@ -23,6 +23,8 @@ target_sources(hal_st.ble_middleware PRIVATE GapPeripheralSt.hpp GapSt.cpp GapSt.hpp + GattClientSt.cpp + GattClientSt.hpp GattServerSt.cpp GattServerSt.hpp HciEventObserver.hpp @@ -32,6 +34,8 @@ target_sources(hal_st.ble_middleware PRIVATE TracingGapCentralSt.hpp TracingGapPeripheralSt.cpp TracingGapPeripheralSt.hpp + TracingGattClientSt.cpp + TracingGattClientSt.hpp TracingGattServerSt.cpp TracingGattServerSt.hpp TracingSystemTransportLayer.cpp diff --git a/hal_st/middlewares/ble_middleware/GattClientSt.cpp b/hal_st/middlewares/ble_middleware/GattClientSt.cpp new file mode 100644 index 00000000..3d45ce7d --- /dev/null +++ b/hal_st/middlewares/ble_middleware/GattClientSt.cpp @@ -0,0 +1,235 @@ +#include "hal_st/middlewares/ble_middleware/GattClientSt.hpp" +#include "infra/event/EventDispatcherWithWeakPtr.hpp" +#include "infra/stream/InputStream.hpp" +#include "infra/util/Endian.hpp" + +extern "C" +{ +#include "auto/ble_gatt_aci.h" +#include "ble/ble.h" +#include "ble/core/ble_core.h" +} + +namespace hal +{ + GattClientSt::GattClientSt(hal::HciEventSource& hciEventSource) + : hal::HciEventSink(hciEventSource) + , connectionHandle(invalidConnection) + {} + + void GattClientSt::StartServiceDiscovery() + { + onDiscoveryCompletion = [](services::GattClientDiscoveryObserver& observer) { observer.ServiceDiscoveryComplete(); }; + + aci_gatt_disc_all_primary_services(connectionHandle); + } + + void GattClientSt::StartCharacteristicDiscovery(const services::GattService& service) + { + onDiscoveryCompletion = [](services::GattClientDiscoveryObserver& observer) { observer.CharacteristicDiscoveryComplete(); }; + + aci_gatt_disc_all_char_of_service(connectionHandle, service.Handle(), service.EndHandle()); + } + + void GattClientSt::StartDescriptorDiscovery(const services::GattService& service) + { + onDiscoveryCompletion = [](services::GattClientDiscoveryObserver& observer) { observer.DescriptorDiscoveryComplete(); }; + + aci_gatt_disc_all_char_desc(connectionHandle, service.Handle(), service.EndHandle()); + } + + void GattClientSt::HciEvent(hci_event_pckt& event) + { + switch (event.evt) + { + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: + HandleHciDisconnectEvent(event); + break; + case HCI_LE_META_EVT_CODE: + HandleHciLeMetaEvent(event); + break; + case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: + HandleHciVendorSpecificDebugEvent(event); + break; + default: + break; + } + } + + void GattClientSt::HandleHciLeMetaEvent(hci_event_pckt& eventPacket) + { + auto metaEvent = reinterpret_cast(eventPacket.data); + + switch (metaEvent->subevent) + { + case HCI_LE_ENHANCED_CONNECTION_COMPLETE_SUBEVT_CODE: + HandleHciLeEnhancedConnectionCompleteEvent(metaEvent); + break; + case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: + HandleHciLeConnectionCompleteEvent(metaEvent); + break; + default: + break; + } + } + + void GattClientSt::HandleHciVendorSpecificDebugEvent(hci_event_pckt& eventPacket) + { + auto vendorEvent = reinterpret_cast(eventPacket.data); + + switch (vendorEvent->ecode) + { + case ACI_ATT_READ_BY_GROUP_TYPE_RESP_VSEVT_CODE: + HandleAttReadByGroupTypeResponse(vendorEvent); + break; + case ACI_ATT_READ_BY_TYPE_RESP_VSEVT_CODE: + HandleAttReadByTypeResponse(vendorEvent); + break; + case ACI_ATT_FIND_INFO_RESP_VSEVT_CODE: + HandleAttFindInfoResponse(vendorEvent); + break; + case ACI_GATT_PROC_COMPLETE_VSEVT_CODE: + HandleGattCompleteResponse(vendorEvent); + break; + default: + break; + } + } + + void GattClientSt::HandleAttReadByGroupTypeResponse(evt_blecore_aci* vendorEvent) + { + const uint8_t uuid16 = 6; + auto attReadByGroupResponse = *reinterpret_cast(vendorEvent->data); + + infra::ByteRange data(&attReadByGroupResponse.Attribute_Data_List[0], &attReadByGroupResponse.Attribute_Data_List[0] + attReadByGroupResponse.Data_Length); + infra::ByteInputStream stream(data, infra::softFail); + + really_assert(attReadByGroupResponse.Connection_Handle == connectionHandle); + + HandleServiceDiscovered(stream, attReadByGroupResponse.Attribute_Data_Length == uuid16); + } + + void GattClientSt::HandleAttReadByTypeResponse(evt_blecore_aci* vendorEvent) + { + const uint8_t uuid16 = 7; + auto attReadByTypeResponse = *reinterpret_cast(vendorEvent->data); + + infra::ByteRange data(&attReadByTypeResponse.Handle_Value_Pair_Data[0], &attReadByTypeResponse.Handle_Value_Pair_Data[0] + attReadByTypeResponse.Data_Length); + infra::ByteInputStream stream(data, infra::softFail); + + really_assert(attReadByTypeResponse.Connection_Handle == connectionHandle); + + HandleCharacteristicDiscovered(stream, attReadByTypeResponse.Handle_Value_Pair_Length == uuid16); + } + + void GattClientSt::HandleAttFindInfoResponse(evt_blecore_aci* vendorEvent) + { + auto attFindInfoResponse = *reinterpret_cast(vendorEvent->data); + + infra::ByteRange data(&attFindInfoResponse.Handle_UUID_Pair[0], &attFindInfoResponse.Handle_UUID_Pair[0] + attFindInfoResponse.Event_Data_Length); + infra::ByteInputStream stream(data, infra::softFail); + + really_assert(attFindInfoResponse.Connection_Handle == connectionHandle); + + HandleDescriptorDiscovered(stream, attFindInfoResponse.Format == UUID_TYPE_16); + } + + void GattClientSt::HandleGattCompleteResponse(evt_blecore_aci* vendorEvent) + { + auto gattProcedureEvent = *reinterpret_cast(vendorEvent->data); + + really_assert(gattProcedureEvent.Connection_Handle == connectionHandle); + really_assert(gattProcedureEvent.Error_Code == BLE_STATUS_SUCCESS); + + if (onDiscoveryCompletion) + infra::Subject::NotifyObservers(std::exchange(onDiscoveryCompletion, nullptr)); + } + + void GattClientSt::HandleHciLeConnectionCompleteEvent(evt_le_meta_event* metaEvent) + { + auto connectionCompleteEvent = *reinterpret_cast(metaEvent->data); + + really_assert(connectionCompleteEvent.Status == BLE_STATUS_SUCCESS); + + connectionHandle = connectionCompleteEvent.Connection_Handle; + } + + void GattClientSt::HandleHciLeEnhancedConnectionCompleteEvent(evt_le_meta_event* metaEvent) + { + auto enhancedConnectionCompleteEvent = *reinterpret_cast(metaEvent->data); + + really_assert(enhancedConnectionCompleteEvent.Status == BLE_STATUS_SUCCESS); + + connectionHandle = enhancedConnectionCompleteEvent.Connection_Handle; + } + + void GattClientSt::HandleHciDisconnectEvent(hci_event_pckt& eventPacket) + { + auto disconnectionCompleteEvent = *reinterpret_cast(eventPacket.data); + + really_assert(disconnectionCompleteEvent.Connection_Handle == connectionHandle); + really_assert(disconnectionCompleteEvent.Status == BLE_STATUS_SUCCESS); + + connectionHandle = GattClientSt::invalidConnection; + } + + void GattClientSt::HandleServiceDiscovered(infra::DataInputStream& stream, bool isUuid16) + { + while (!stream.Empty()) + { + Atttributes attributes; + + stream >> attributes.startHandle >> attributes.endHandle; + + HandleUuidFromDiscovery(stream, isUuid16, attributes.type); + + really_assert(!stream.Failed()); + + infra::Subject::NotifyObservers([&attributes](auto& observer) { observer.ServiceDiscovered(attributes.type, attributes.startHandle, attributes.endHandle); }); + } + } + + void GattClientSt::HandleCharacteristicDiscovered(infra::DataInputStream& stream, bool isUuid16) + { + while (!stream.Empty()) + { + Atttributes attributes; + + stream >> attributes.startHandle >> attributes.properties >> attributes.endHandle; + + HandleUuidFromDiscovery(stream, isUuid16, attributes.type); + + really_assert(!stream.Failed()); + + infra::Subject::NotifyObservers([&attributes](auto& observer) { observer.CharacteristicDiscovered(attributes.type, attributes.startHandle, attributes.endHandle, attributes.properties); }); + } + } + + void GattClientSt::HandleDescriptorDiscovered(infra::DataInputStream& stream, bool isUuid16) + { + while (!stream.Empty()) + { + Atttributes attributes; + + stream >> attributes.startHandle; + + HandleUuidFromDiscovery(stream, isUuid16, attributes.type); + + really_assert(!stream.Failed()); + + infra::Subject::NotifyObservers([&attributes](auto& observer) { observer.DescriptorDiscovered(attributes.type, attributes.startHandle); }); + } + } + + void GattClientSt::HandleUuidFromDiscovery(infra::DataInputStream& stream, bool isUuid16, services::AttAttribute::Uuid& type) + { + if (isUuid16) + { + stream >> type.Emplace(); + } + else + { + stream >> type.Emplace(); + } + } +} diff --git a/hal_st/middlewares/ble_middleware/GattClientSt.hpp b/hal_st/middlewares/ble_middleware/GattClientSt.hpp new file mode 100644 index 00000000..0271b809 --- /dev/null +++ b/hal_st/middlewares/ble_middleware/GattClientSt.hpp @@ -0,0 +1,66 @@ +#ifndef HAL_ST_GATT_CLIENT_ST_HPP +#define HAL_ST_GATT_CLIENT_ST_HPP + +#include "ble/ble.h" +#include "hal_st/middlewares/ble_middleware/HciEventObserver.hpp" +#include "infra/util/AutoResetFunction.hpp" +#include "infra/util/BoundedVector.hpp" +#include "infra/stream/ByteInputStream.hpp" +#include "services/ble/GattClient.hpp" + +namespace hal +{ + class GattClientSt + : public services::GattClientDiscovery + , public hal::HciEventSink + { + public: + explicit GattClientSt(hal::HciEventSource& hciEventSource); + + // Implementation of services::GattClientDiscovery + virtual void StartServiceDiscovery() override; + virtual void StartCharacteristicDiscovery(const services::GattService& service) override; + virtual void StartDescriptorDiscovery(const services::GattService& service) override; + + // Implementation of hal::HciEventSink + virtual void HciEvent(hci_event_pckt& event) override; + + protected: + virtual void HandleHciDisconnectEvent(hci_event_pckt& eventPacket); + virtual void HandleHciLeMetaEvent(hci_event_pckt& eventPacket); + virtual void HandleHciVendorSpecificDebugEvent(hci_event_pckt& eventPacket); + + virtual void HandleHciLeConnectionCompleteEvent(evt_le_meta_event* metaEvent); + virtual void HandleHciLeEnhancedConnectionCompleteEvent(evt_le_meta_event* metaEvent); + + virtual void HandleGattCompleteResponse(evt_blecore_aci* vendorEvent); + + virtual void HandleAttReadByGroupTypeResponse(evt_blecore_aci* vendorEvent); + virtual void HandleAttReadByTypeResponse(evt_blecore_aci* vendorEvent); + virtual void HandleAttFindInfoResponse(evt_blecore_aci* vendorEvent); + + private: + void HandleServiceDiscovered(infra::DataInputStream& stream, bool isUuid16); + void HandleCharacteristicDiscovered(infra::DataInputStream& stream, bool isUuid16); + void HandleDescriptorDiscovered(infra::DataInputStream& stream, bool isUuid16); + void HandleUuidFromDiscovery(infra::DataInputStream& stream, bool isUuid16, services::AttAttribute::Uuid& type); + + private: + struct Atttributes + { + services::AttAttribute::Uuid type; + services::AttAttribute::Handle startHandle; + services::AttAttribute::Handle endHandle; + services::GattCharacteristic::PropertyFlags properties; + }; + + private: + uint16_t connectionHandle; + + static constexpr uint16_t invalidConnection = 0xffff; + + infra::Function onDiscoveryCompletion; + }; +} + +#endif diff --git a/hal_st/middlewares/ble_middleware/TracingGattClientSt.cpp b/hal_st/middlewares/ble_middleware/TracingGattClientSt.cpp new file mode 100644 index 00000000..e2d229b4 --- /dev/null +++ b/hal_st/middlewares/ble_middleware/TracingGattClientSt.cpp @@ -0,0 +1,43 @@ +#include "hal_st/middlewares/ble_middleware/TracingGattClientSt.hpp" +#include "infra/stream/StringOutputStream.hpp" + +namespace infra +{ + TextOutputStream& operator<<(TextOutputStream& stream, const services::AttAttribute::Uuid& uuid) + { + if (uuid.Is()) + stream << "0x" << hex << uuid.Get(); + else + stream << "[" << AsHex(MakeByteRange(uuid.Get())) << "]"; + + return stream; + } +} + +namespace hal +{ + TracingGattClientSt::TracingGattClientSt(hal::HciEventSource& hciEventSource, services::Tracer& tracer) + : GattClientSt(hciEventSource) + , tracer(tracer) + { + tracer.Trace() << "TracingGattClientSt::TracingGattClientSt()"; + } + + void TracingGattClientSt::StartServiceDiscovery() + { + tracer.Trace() << "TracingGattClientSt::StartServiceDiscovery"; + GattClientSt::StartServiceDiscovery(); + } + + void TracingGattClientSt::StartCharacteristicDiscovery(const services::GattService& service) + { + tracer.Trace() << "TracingGattClientSt::StartCharacteristicDiscovery"; + GattClientSt::StartCharacteristicDiscovery(service); + } + + void TracingGattClientSt::StartDescriptorDiscovery(const services::GattService& service) + { + tracer.Trace() << "TracingGattClientSt::StartDescriptorDiscovery"; + GattClientSt::StartDescriptorDiscovery(service); + } +} diff --git a/hal_st/middlewares/ble_middleware/TracingGattClientSt.hpp b/hal_st/middlewares/ble_middleware/TracingGattClientSt.hpp new file mode 100644 index 00000000..15b989ca --- /dev/null +++ b/hal_st/middlewares/ble_middleware/TracingGattClientSt.hpp @@ -0,0 +1,25 @@ +#ifndef HAL_ST_TRACING_GATT_CLIENT_ST_HPP +#define HAL_ST_TRACING_GATT_CLIENT_ST_HPP + +#include "hal_st/middlewares/ble_middleware/GattClientSt.hpp" +#include "services/tracer/Tracer.hpp" + +namespace hal +{ + class TracingGattClientSt + : public GattClientSt + { + public: + explicit TracingGattClientSt(hal::HciEventSource& hciEventSource, services::Tracer& tracer); + + // Implementation of services::GattClientDiscovery + virtual void StartServiceDiscovery() override; + virtual void StartCharacteristicDiscovery(const services::GattService& service) override; + virtual void StartDescriptorDiscovery(const services::GattService& service) override; + + private: + services::Tracer& tracer; + }; +} + +#endif diff --git a/hal_st/middlewares/ble_middleware/TracingGattServerSt.cpp b/hal_st/middlewares/ble_middleware/TracingGattServerSt.cpp index 28db6d4a..f1f003a0 100644 --- a/hal_st/middlewares/ble_middleware/TracingGattServerSt.cpp +++ b/hal_st/middlewares/ble_middleware/TracingGattServerSt.cpp @@ -1,14 +1,14 @@ #include "hal_st/middlewares/ble_middleware/TracingGattServerSt.hpp" #include "infra/stream/StringOutputStream.hpp" -namespace +namespace infra { - infra::TextOutputStream& operator<<(infra::TextOutputStream& stream, const services::AttAttribute::Uuid& uuid) + TextOutputStream& operator<<(TextOutputStream& stream, const services::AttAttribute::Uuid& uuid) { if (uuid.Is()) - stream << "0x" << infra::hex << uuid.Get(); + stream << "0x" << hex << uuid.Get(); else - stream << "[" << infra::AsHex(infra::MakeByteRange(uuid.Get())) << "]"; + stream << "[" << AsHex(MakeByteRange(uuid.Get())) << "]"; return stream; }