From b1615fbfbfe79bebaabaf4c07319a6caf065adca Mon Sep 17 00:00:00 2001 From: PSONALl <77670766+PSONALl@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:46:10 +0530 Subject: [PATCH] Esp32/ble controller (#23921) * ESP32 as a controller * Add bluedroid support for esp32/ble-commissioner * Addressed review comments --- config/esp32/components/chip/CMakeLists.txt | 12 + config/esp32/components/chip/Kconfig | 15 + src/platform/ESP32/BLEManagerImpl.h | 125 ++- src/platform/ESP32/BUILD.gn | 31 +- src/platform/ESP32/CHIPDevicePlatformConfig.h | 1 + src/platform/ESP32/CHIPDevicePlatformEvent.h | 39 + src/platform/ESP32/ChipDeviceScanner.h | 114 +++ .../ESP32/bluedroid/BLEManagerImpl.cpp | 843 +++++++++++++++++- .../ESP32/bluedroid/ChipDeviceScanner.cpp | 127 +++ src/platform/ESP32/nimble/BLEManagerImpl.cpp | 525 ++++++++++- .../ESP32/nimble/ChipDeviceScanner.cpp | 156 ++++ src/platform/ESP32/nimble/blecent.h | 110 +++ src/platform/ESP32/nimble/misc.c | 219 +++++ src/platform/ESP32/nimble/peer.c | 828 +++++++++++++++++ 14 files changed, 3099 insertions(+), 46 deletions(-) create mode 100644 src/platform/ESP32/ChipDeviceScanner.h create mode 100644 src/platform/ESP32/bluedroid/ChipDeviceScanner.cpp create mode 100644 src/platform/ESP32/nimble/ChipDeviceScanner.cpp create mode 100644 src/platform/ESP32/nimble/blecent.h create mode 100644 src/platform/ESP32/nimble/misc.c create mode 100644 src/platform/ESP32/nimble/peer.c diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index 00696281ad64c0..e31baa2e0a6be2 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -143,6 +143,18 @@ else() chip_gn_arg_append("chip_enable_wifi" "false") endif() +if (CONFIG_ENABLE_CHIPOBLE) + chip_gn_arg_append("chip_enable_chipoble" "true") +endif() + +if ((CONFIG_BT_ENABLED) AND (CONFIG_ENABLE_CHIPOBLE)) + if (CONFIG_BT_NIMBLE_ENABLED) + chip_gn_arg_append("chip_bt_nimble_enabled" "true") + else() + chip_gn_arg_append("chip_bt_bluedroid_enabled" "true") + endif() +endif() + if (CONFIG_OPENTHREAD_ENABLED) chip_gn_arg_append("chip_enable_openthread" "true") endif() diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index c5b645bb2db069..2b2175f3e834c3 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -886,6 +886,21 @@ menu "CHIP Device Layer" default 900 help The amount of time (in seconds) after which the CHIP platform will close the Commissioning Window + endmenu + + menu "Enable ESP32 as a BLE Commissioner" + config ENABLE_ESP32_BLE_CONTROLLER + bool "Enable ESP32 as a BLE Commissioner" + default n + help + Enable esp32 as a BLE Commissioner. + + config ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + bool "Enable Commissionee and Commissioner mode" + default n + depends on ENABLE_ESP32_BLE_Controller + help + Enable including commissioner code (CHIPDeviceController.cpp) in the commissionee (Server.cpp) code. endmenu diff --git a/src/platform/ESP32/BLEManagerImpl.h b/src/platform/ESP32/BLEManagerImpl.h index 9c3020f9b504bb..71766eaf0c7e8a 100644 --- a/src/platform/ESP32/BLEManagerImpl.h +++ b/src/platform/ESP32/BLEManagerImpl.h @@ -33,6 +33,9 @@ #include "esp_bt.h" #include "esp_gap_ble_api.h" +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include "esp_gattc_api.h" +#endif #include "esp_gatts_api.h" #include #elif CONFIG_BT_NIMBLE_ENABLED @@ -56,22 +59,85 @@ struct ble_gatt_char_context #endif +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER && CONFIG_BT_NIMBLE_ENABLED +#include "nimble/blecent.h" +#endif + #include "ble/Ble.h" +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#include +#include +#endif namespace chip { namespace DeviceLayer { namespace Internal { +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void HandleIncomingBleConnection(Ble::BLEEndPoint * bleEP); + +enum class BleScanState : uint8_t +{ + kNotScanning, + kScanForDiscriminator, + kScanForAddress, + kConnecting, +}; + +struct BLEAdvConfig +{ + char * mpBleName; + uint32_t mAdapterId; + uint8_t mMajor; + uint8_t mMinor; + uint16_t mVendorId; + uint16_t mProductId; + uint64_t mDeviceId; + uint8_t mPairingStatus; + uint8_t mType; + uint16_t mDuration; + const char * mpAdvertisingUUID; +}; + +struct BLEScanConfig +{ + // If an active scan for connection is being performed + BleScanState mBleScanState = BleScanState::kNotScanning; + + // If scanning by discriminator, what are we scanning for + SetupDiscriminator mDiscriminator; + + // If scanning by address, what address are we searching for + std::string mAddress; + + // Optional argument to be passed to callback functions provided by the BLE scan/connect requestor + void * mAppState = nullptr; +}; + +#endif /** * Concrete implementation of the BLEManager singleton object for the ESP32 platform. */ class BLEManagerImpl final : public BLEManager, private Ble::BleLayer, private Ble::BlePlatformDelegate, +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + private Ble::BleApplicationDelegate, + private Ble::BleConnectionDelegate, + private ChipDeviceScannerDelegate +#else private Ble::BleApplicationDelegate +#endif { public: BLEManagerImpl() {} +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + CHIP_ERROR ConfigureBle(uint32_t aAdapterId, bool aIsCentral); +#if CONFIG_BT_BLUEDROID_ENABLED + static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t * param); +#endif +#endif private: // Allow the BLEManager interface class to delegate method calls to @@ -90,6 +156,10 @@ class BLEManagerImpl final : public BLEManager, CHIP_ERROR _SetDeviceName(const char * deviceName); uint16_t _NumConnections(void); void _OnPlatformEvent(const ChipDeviceEvent * event); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + void HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * event); + CHIP_ERROR _SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val); +#endif ::chip::Ble::BleLayer * _GetBleLayer(void); // ===== Members that implement virtual methods on BlePlatformDelegate. @@ -112,7 +182,23 @@ class BLEManagerImpl final : public BLEManager, // ===== Members that implement virtual methods on BleApplicationDelegate. void NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) override; + // ===== Members that implement virtual methods on BleConnectionDelegate. +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + + void NewConnection(chip::Ble::BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator) override; + CHIP_ERROR CancelConnection() override; + + // ===== Members that implement virtual methods on ChipDeviceScannerDelegate +#if CONFIG_BT_NIMBLE_ENABLED + virtual void OnDeviceScanned(const struct ble_hs_adv_fields & fields, const ble_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) override; +#elif CONFIG_BT_BLUEDROID_ENABLED + virtual void OnDeviceScanned(esp_ble_addr_type_t & addr_type, esp_bd_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) override; +#endif + void OnScanComplete() override; +#endif // ===== Members for internal use by the following friends. friend BLEManager & BLEMgr(void); @@ -144,6 +230,9 @@ class BLEManagerImpl final : public BLEManager, kMaxDeviceNameLength = 16 }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + BLEAdvConfig mBLEAdvConfig; +#endif #if CONFIG_BT_NIMBLE_ENABLED uint16_t mSubscribedConIds[kMaxConnections]; #endif @@ -218,7 +307,15 @@ class BLEManagerImpl final : public BLEManager, void HandleDisconnect(esp_ble_gatts_cb_param_t * param); CHIPoBLEConState * GetConnectionState(uint16_t conId, bool allocate = false); bool ReleaseConnectionState(uint16_t conId); - +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + CHIP_ERROR HandleGAPConnect(esp_ble_gattc_cb_param_t p_data); + CHIP_ERROR HandleGAPCentralConnect(esp_ble_gattc_cb_param_t p_data); + + static void HandleConnectFailed(CHIP_ERROR error); + static void ConnectDevice(esp_bd_addr_t & addr, esp_ble_addr_type_t addr_type, uint16_t timeout); + void HandleGAPConnectionFailed(); + CHIP_ERROR HandleRXNotify(esp_ble_gattc_cb_param_t p_data); +#endif static void HandleGATTEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t * param); static void HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param); @@ -231,10 +328,14 @@ class BLEManagerImpl final : public BLEManager, void HandleTXCharCCCDWrite(struct ble_gap_event * gapEvent); CHIP_ERROR HandleTXComplete(struct ble_gap_event * gapEvent); CHIP_ERROR HandleGAPConnect(struct ble_gap_event * gapEvent); + CHIP_ERROR HandleGAPPeripheralConnect(struct ble_gap_event * gapEvent); CHIP_ERROR HandleGAPDisconnect(struct ble_gap_event * gapEvent); CHIP_ERROR SetSubscribed(uint16_t conId); bool UnsetSubscribed(uint16_t conId); bool IsSubscribed(uint16_t conId); + static void ConnectDevice(const ble_addr_t & addr, uint16_t timeout); + CHIP_ERROR HandleGAPCentralConnect(struct ble_gap_event * gapEvent); + void HandleGAPConnectionFailed(struct ble_gap_event * gapEvent, CHIP_ERROR error); static CHIP_ERROR bleprph_set_random_addr(void); static void bleprph_host_task(void * param); @@ -249,6 +350,28 @@ class BLEManagerImpl final : public BLEManager, void * arg); void HandleC3CharRead(struct ble_gatt_char_context * param); #endif /* CHIP_ENABLE_ADDITIONAL_DATA_ADVERTISING */ + +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + static int btshell_on_mtu(uint16_t conn_handle, const struct ble_gatt_error * error, uint16_t mtu, void * arg); + + bool SubOrUnsubChar(BLE_CONNECTION_OBJECT conId, const Ble::ChipBleUUID * svcId, const Ble::ChipBleUUID * charId, + bool subscribe); + + static void OnGattDiscComplete(const struct peer * peer, int status, void * arg); + static void HandleConnectFailed(CHIP_ERROR error); + CHIP_ERROR HandleRXNotify(struct ble_gap_event * event); +#endif +#endif +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + static void CancelConnect(void); + static void HandleConnectTimeout(chip::System::Layer *, void * context); + void InitiateScan(BleScanState scanType); + static void InitiateScan(intptr_t arg); + void HandleAdvertisementTimer(System::Layer * systemLayer, void * context); + void HandleAdvertisementTimer(); + void CleanScanConfig(); + BLEScanConfig mBLEScanConfig; + bool mIsCentral; #endif static void DriveBLEState(intptr_t arg); diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index 29d3464f6fb824..fce0f93971ee29 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -24,6 +24,9 @@ declare_args() { chip_use_factory_data_provider = false chip_use_device_info_provider = false chip_config_software_version_number = 0 + chip_enable_chipoble = true + chip_bt_nimble_enabled = false + chip_bt_bluedroid_enabled = false } defines = [ @@ -33,7 +36,6 @@ defines = [ static_library("ESP32") { sources = [ "../SingletonConfigurationManager.cpp", - "BLEManagerImpl.h", "CHIPDevicePlatformConfig.h", "CHIPDevicePlatformEvent.h", "ConfigurationManagerImpl.cpp", @@ -55,8 +57,6 @@ static_library("ESP32") { "PlatformManagerImpl.h", "SystemTimeSupport.cpp", "SystemTimeSupport.h", - "bluedroid/BLEManagerImpl.cpp", - "nimble/BLEManagerImpl.cpp", ] deps = [ @@ -70,6 +70,7 @@ static_library("ESP32") { "${chip_root}/src/crypto", "${chip_root}/src/platform:platform_base", ] + if (chip_enable_ota_requestor) { sources += [ "OTAImageProcessorImpl.cpp", @@ -77,6 +78,30 @@ static_library("ESP32") { ] } + if (chip_enable_chipoble) { + sources += [ + "BLEManagerImpl.h", + "ChipDeviceScanner.h", + ] + } + + if (chip_bt_nimble_enabled) { + sources += [ + "nimble/BLEManagerImpl.cpp", + "nimble/ChipDeviceScanner.cpp", + "nimble/blecent.h", + "nimble/misc.c", + "nimble/peer.c", + ] + } + + if (chip_bt_bluedroid_enabled) { + sources += [ + "bluedroid/BLEManagerImpl.cpp", + "bluedroid/ChipDeviceScanner.cpp", + ] + } + if (chip_enable_wifi) { sources += [ "ConnectivityManagerImpl_WiFi.cpp", diff --git a/src/platform/ESP32/CHIPDevicePlatformConfig.h b/src/platform/ESP32/CHIPDevicePlatformConfig.h index 7f2e4a9dd11d09..13360bf91cda89 100644 --- a/src/platform/ESP32/CHIPDevicePlatformConfig.h +++ b/src/platform/ESP32/CHIPDevicePlatformConfig.h @@ -97,3 +97,4 @@ #define CHIP_DEVICE_CONFIG_CHIP_KVS_NAMESPACE_PARTITION CONFIG_CHIP_KVS_NAMESPACE_PARTITION_LABEL #define CHIP_DEVICE_CONFIG_ENABLE_DEVICE_INSTANCE_INFO_PROVIDER CONFIG_ENABLE_ESP32_DEVICE_INSTANCE_INFO_PROVIDER #define CHIP_DEVICE_CONFIG_DISCOVERY_TIMEOUT_SECS CONFIG_CHIP_DISCOVERY_TIMEOUT_SECS +#define CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE diff --git a/src/platform/ESP32/CHIPDevicePlatformEvent.h b/src/platform/ESP32/CHIPDevicePlatformEvent.h index c4e0df18f4accf..b7664ef8c69145 100644 --- a/src/platform/ESP32/CHIPDevicePlatformEvent.h +++ b/src/platform/ESP32/CHIPDevicePlatformEvent.h @@ -44,6 +44,21 @@ enum kESPSystemEvent = kRange_PublicPlatformSpecific, }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +/** + * Enumerates ESP32 platform-specific event types that are internal to the Chip Device Layer. + */ +enum InternalPlatformSpecificEventTypes +{ + kPlatformESP32Event = kRange_InternalPlatformSpecific, + kPlatformESP32BLECentralConnected, + kPlatformESP32BLECentralConnectFailed, + kPlatformESP32BLEWriteComplete, + kPlatformESP32BLESubscribeOpComplete, + kPlatformESP32BLEIndicationReceived, +}; + +#endif } // namespace DeviceEventType /** @@ -74,6 +89,30 @@ struct ChipDevicePlatformEvent final wifi_event_ap_probe_req_rx_t WiFiApProbeReqRecved; } Data; } ESPSystemEvent; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + struct + { + BLE_CONNECTION_OBJECT mConnection; + } BLECentralConnected; + struct + { + CHIP_ERROR mError; + } BLECentralConnectFailed; + struct + { + BLE_CONNECTION_OBJECT mConnection; + } BLEWriteComplete; + struct + { + BLE_CONNECTION_OBJECT mConnection; + bool mIsSubscribed; + } BLESubscribeOpComplete; + struct + { + BLE_CONNECTION_OBJECT mConnection; + chip::System::PacketBuffer * mData; + } BLEIndicationReceived; +#endif }; }; diff --git a/src/platform/ESP32/ChipDeviceScanner.h b/src/platform/ESP32/ChipDeviceScanner.h new file mode 100644 index 00000000000000..90e866b52a2c41 --- /dev/null +++ b/src/platform/ESP32/ChipDeviceScanner.h @@ -0,0 +1,114 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + +#include +#include +#include + +#if CONFIG_BT_NIMBLE_ENABLED +#include "host/ble_hs.h" +#else +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_ble_api.h" +#include "esp_gatt_common_api.h" +#include "esp_gatt_defs.h" +#include "esp_gattc_api.h" +#include "esp_gatts_api.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include +#include +#include +#endif + +namespace chip { +namespace DeviceLayer { +namespace Internal { + +/// Receives callbacks when chip devices are being scanned +class ChipDeviceScannerDelegate +{ +public: + virtual ~ChipDeviceScannerDelegate() {} + + // Called when a CHIP device was found +#if CONFIG_BT_NIMBLE_ENABLED + virtual void OnDeviceScanned(const struct ble_hs_adv_fields & fields, const ble_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) = 0; +#else + virtual void OnDeviceScanned(esp_ble_addr_type_t & addr_type, esp_bd_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) = 0; +#endif + // Called when a scan was completed (stopped or timed out) + virtual void OnScanComplete() = 0; +}; + +/// Allows scanning for CHIP devices +/// Will perform scan operations and call back whenever a device is discovered. +class ChipDeviceScanner +{ +public: + ChipDeviceScanner(ChipDeviceScanner &&) = delete; + ChipDeviceScanner(const ChipDeviceScanner &) = delete; + ChipDeviceScanner & operator=(const ChipDeviceScanner &) = delete; + + ~ChipDeviceScanner() = default; + + static ChipDeviceScanner & GetInstance() + { + static ChipDeviceScanner instance; + return instance; + } + + /// Initializes the scanner + CHIP_ERROR Init(ChipDeviceScannerDelegate * delegate) + { + VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mDelegate = delegate; + return CHIP_NO_ERROR; + } + + /// Initiate a scan for devices, with the given timeout + CHIP_ERROR StartScan(uint16_t timeout); + + /// Stop any currently running scan + CHIP_ERROR StopScan(); + + bool mIsScanning = false; +#if CONFIG_BT_NIMBLE_ENABLED + void ReportDevice(const struct ble_hs_adv_fields & fields, const ble_addr_t & addr); +#else + void ReportDevice(esp_ble_gap_cb_param_t & fields, esp_bd_addr_t & addr); +#endif + +private: + ChipDeviceScanner() = default; + + /// Check if a given device is a CHIP device and if yes, report it as discovered + static int OnBleCentralEvent(struct ble_gap_event * event, void * arg); + void RemoveDevice(); + ChipDeviceScannerDelegate * mDelegate = nullptr; +}; + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER diff --git a/src/platform/ESP32/bluedroid/BLEManagerImpl.cpp b/src/platform/ESP32/bluedroid/BLEManagerImpl.cpp index 1ffbe16d2ca0eb..46fca5f6982f2d 100644 --- a/src/platform/ESP32/bluedroid/BLEManagerImpl.cpp +++ b/src/platform/ESP32/bluedroid/BLEManagerImpl.cpp @@ -28,14 +28,29 @@ #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE #include "sdkconfig.h" +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#endif #if CONFIG_BT_BLUEDROID_ENABLED +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#endif #include #include #include #include +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#include +#include +#endif #include +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#include +#endif #include "esp_bt.h" #include "esp_bt_main.h" @@ -48,6 +63,7 @@ #define CHIP_ADV_DATA_TYPE_FLAGS 0x01 #define CHIP_ADV_DATA_FLAGS 0x06 #define CHIP_ADV_DATA_TYPE_SERVICE_DATA 0x16 +#define CHIP_MAX_MTU_SIZE 256 using namespace ::chip; using namespace ::chip::Ble; @@ -64,18 +80,26 @@ struct ESP32ChipServiceData ChipBLEDeviceIdentificationInfo DeviceIdInfo; }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +static constexpr uint16_t kNewConnectionScanTimeout = 60; +static constexpr uint16_t kConnectTimeout = 20; +#endif + const uint16_t CHIPoBLEAppId = 0x235A; -const uint8_t UUID_PrimaryService[] = { 0x00, 0x28 }; -const uint8_t UUID_CharDecl[] = { 0x03, 0x28 }; -const uint8_t UUID_ClientCharConfigDesc[] = { 0x02, 0x29 }; -const uint8_t UUID_CHIPoBLEService[] = { 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, +const uint8_t UUID_PrimaryService[] = { 0x00, 0x28 }; +const uint8_t UUID_CharDecl[] = { 0x03, 0x28 }; +const uint8_t UUID_ClientCharConfigDesc[] = { 0x02, 0x29 }; +const uint8_t UUID_CHIPoBLEService[] = { 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x00 }; -const uint8_t ShortUUID_CHIPoBLEService[] = { 0xF6, 0xFF }; -const uint8_t UUID_CHIPoBLEChar_RX[] = { 0x11, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, +const uint8_t ShortUUID_CHIPoBLEService[] = { 0xF6, 0xFF }; +const uint8_t UUID_CHIPoBLEChar_RX[] = { 0x11, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18 }; -const uint8_t UUID_CHIPoBLEChar_TX[] = { 0x12, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, +const uint8_t UUID_CHIPoBLEChar_TX[] = { 0x12, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18 }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +const uint8_t ShortUUID_CHIPoBLE_CharTx_Desc[] = { 0x02, 0x29 }; +#endif const ChipBleUUID ChipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, 0x9D, 0x11 } }; const ChipBleUUID ChipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F, @@ -124,15 +148,66 @@ const uint16_t CHIPoBLEGATTAttrCount = sizeof(CHIPoBLEGATTAttrs) / sizeof(CHIPoB } // unnamed namespace +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +ChipDeviceScanner & mDeviceScanner = Internal::ChipDeviceScanner::GetInstance(); +#endif BLEManagerImpl BLEManagerImpl::sInstance; constexpr System::Clock::Timeout BLEManagerImpl::kFastAdvertiseTimeout; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +static esp_gattc_char_elem_t * char_elem_result = NULL; +static esp_gattc_descr_elem_t * descr_elem_result = NULL; + +/// Declare static functions +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t * param); + +static esp_bt_uuid_t remote_filter_service_uuid = { + .len = ESP_UUID_LEN_16, + .uuid = {.uuid16 = 0xFFF6,}, +}; +static esp_bt_uuid_t notify_descr_uuid = { + .len = ESP_UUID_LEN_16, +}; + +static bool connect = false; +static bool get_server = false; +static uint16_t connId; + +#define PROFILE_NUM 1 +#define PROFILE_A_APP_ID 0 +#define INVALID_HANDLE 0 + +struct gattc_profile_inst +{ + esp_gattc_cb_t gattc_cb; + uint16_t gattc_if; + uint16_t app_id; + uint16_t conn_id; + uint16_t service_start_handle; + uint16_t service_end_handle; + uint16_t notify_char_handle; + uint16_t write_char_handle; + esp_bd_addr_t remote_bda; +}; + +/* One gatt-based profile one app_id and one , this array will store the gattc_if returned by ESP_GATTS_REG_EVT */ +static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { + [PROFILE_A_APP_ID] = { + .gattc_cb = BLEManagerImpl::gattc_profile_event_handler, + .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ + }, +}; +#endif CHIP_ERROR BLEManagerImpl::_Init() { CHIP_ERROR err; // Initialize the Chip BleLayer. +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); +#else err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); +#endif SuccessOrExit(err); memset(mCons, 0, sizeof(mCons)); @@ -142,8 +217,15 @@ CHIP_ERROR BLEManagerImpl::_Init() mRXCharAttrHandle = 0; mTXCharAttrHandle = 0; mTXCharCCCDAttrHandle = 0; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART && !mIsCentral); + mFlags.Set(Flags::kFastAdvertisingEnabled, !mIsCentral); + OnChipBleConnectReceived = HandleIncomingBleConnection; + +#else mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART); mFlags.Set(Flags::kFastAdvertisingEnabled, true); +#endif memset(mDeviceName, 0, sizeof(mDeviceName)); PlatformMgr().ScheduleWork(DriveBLEState, 0); @@ -277,22 +359,445 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) mFlags.Set(Flags::kAdvertisingRefreshNeeded); DriveBLEState(); + break; default: +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + HandlePlatformSpecificBLEEvent(event); +#endif + break; + } +} + +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLogProgress(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type); + + switch (apEvent->Type) + { + case DeviceEventType::kPlatformESP32BLECentralConnected: + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionComplete(mBLEScanConfig.mAppState, + apEvent->Platform.BLECentralConnected.mConnection); + CleanScanConfig(); + } + break; + + case DeviceEventType::kPlatformESP32BLECentralConnectFailed: + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnectFailed.mError); + CleanScanConfig(); + } + break; + + case DeviceEventType::kPlatformESP32BLEWriteComplete: + HandleWriteConfirmation(apEvent->Platform.BLEWriteComplete.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_RX); break; + + case DeviceEventType::kPlatformESP32BLESubscribeOpComplete: + if (apEvent->Platform.BLESubscribeOpComplete.mIsSubscribed) + HandleSubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, + &ChipUUID_CHIPoBLEChar_TX); + else + HandleUnsubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, + &ChipUUID_CHIPoBLEChar_TX); + break; + + case DeviceEventType::kPlatformESP32BLEIndicationReceived: + HandleIndicationReceived(apEvent->Platform.BLEIndicationReceived.mConnection, &CHIP_BLE_SVC_ID, &ChipUUID_CHIPoBLEChar_TX, + PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData)); + break; + + default: + break; + } + + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + } +} + +void BLEManagerImpl::gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t * param) +{ + esp_ble_gattc_cb_param_t * p_data = (esp_ble_gattc_cb_param_t *) param; + CHIP_ERROR err = CHIP_NO_ERROR; + + switch (event) + { + case ESP_GATTC_REG_EVT: + break; + case ESP_GATTC_CONNECT_EVT: + err = sInstance.HandleGAPConnect(*p_data); + SuccessOrExit(err); + break; + case ESP_GATTC_OPEN_EVT: + if (param->open.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "open failed, status %d", p_data->open.status); + break; + } + ChipLogProgress(Ble, "open success"); + break; + case ESP_GATTC_DIS_SRVC_CMPL_EVT: + if (param->dis_srvc_cmpl.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "discover service failed, status %d", param->dis_srvc_cmpl.status); + break; + } + ChipLogProgress(Ble, "discover service complete conn_id %d", param->dis_srvc_cmpl.conn_id); + esp_ble_gattc_search_service(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, param->cfg_mtu.conn_id, + &remote_filter_service_uuid); + break; + case ESP_GATTC_CFG_MTU_EVT: + if (param->cfg_mtu.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "config mtu failed, error status = %x", param->cfg_mtu.status); + } + ChipLogProgress(Ble, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, + param->cfg_mtu.conn_id); + break; + case ESP_GATTC_SEARCH_RES_EVT: { + ChipLogProgress(Ble, "SEARCH RES: conn_id = %x is primary service %d", p_data->search_res.conn_id, + p_data->search_res.is_primary); + ChipLogProgress(Ble, "start handle %d end handle %d current handle value %d", p_data->search_res.start_handle, + p_data->search_res.end_handle, p_data->search_res.srvc_id.inst_id); + if (p_data->search_res.srvc_id.uuid.len == ESP_UUID_LEN_16 && p_data->search_res.srvc_id.uuid.uuid.uuid16 == 0xFFF6) + { + ChipLogProgress(Ble, "service found"); + get_server = true; + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle = p_data->search_res.start_handle; + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle = p_data->search_res.end_handle; + ChipLogProgress(Ble, "UUID16: %x", p_data->search_res.srvc_id.uuid.uuid.uuid16); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: { + if (p_data->search_cmpl.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "search service failed, error status = %x", p_data->search_cmpl.status); + break; + } + if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) + { + ChipLogProgress(Ble, "Get service information from remote device"); + } + else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) + { + ChipLogProgress(Ble, "Get service information from flash"); + } + else + { + ChipLogProgress(Ble, "unknown service source"); + } + ChipLogProgress(Ble, "ESP_GATTC_SEARCH_CMPL_EVT"); + if (get_server) + { + uint16_t count = 0; + uint16_t offset = 0; + esp_gatt_status_t status = + esp_ble_gattc_get_attr_count(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, p_data->search_cmpl.conn_id, + ESP_GATT_DB_CHARACTERISTIC, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, INVALID_HANDLE, &count); + if (status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "esp_ble_gattc_get_attr_count error"); + } + ChipLogProgress(Ble, "Count : %d", count); + + if (count > 0) + { + char_elem_result = (esp_gattc_char_elem_t *) malloc(sizeof(esp_gattc_char_elem_t) * count); + // memset(char_elem_result, 0xff, sizeof(esp_gattc_char_elem_t) * count); + if (!char_elem_result) + { + ChipLogProgress(Ble, "gattc no mem"); + break; + } + else + { + status = esp_ble_gattc_get_all_char(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, p_data->search_cmpl.conn_id, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, + gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, char_elem_result, + &count, offset); + if (status != 0) + { + ChipLogProgress(Ble, "esp_ble_gattc_get_char_by_uuid error"); + } + + /* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */ + } + } + if (count > 0) + { + for (int i = 0; i < count; i++) + { + if (char_elem_result[i].uuid.len == ESP_UUID_LEN_128) + { + if (char_elem_result[i].properties & CharProps_Write) + { + gl_profile_tab[PROFILE_A_APP_ID].write_char_handle = char_elem_result[i].char_handle; + } + else if (char_elem_result[i].properties & CharProps_ReadNotify) + { + gl_profile_tab[PROFILE_A_APP_ID].notify_char_handle = char_elem_result[i].char_handle; + esp_ble_gattc_register_for_notify(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, + gl_profile_tab[PROFILE_A_APP_ID].remote_bda, + char_elem_result[i].char_handle); + } + } + } + } + free(char_elem_result); + } + else + { + ChipLogProgress(Ble, "no char found"); + } + } + break; + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + ChipLogProgress(Ble, "ESP_GATTC_REG_FOR_NOTIFY_EVT"); + if (p_data->reg_for_notify.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "REG FOR NOTIFY failed: error status = %d", p_data->reg_for_notify.status); + } + else + { + uint16_t count = 0; + esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( + gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, ESP_GATT_DB_DESCRIPTOR, + gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, + gl_profile_tab[PROFILE_A_APP_ID].notify_char_handle, &count); + if (ret_status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "esp_ble_gattc_get_attr_count error"); + } + if (count > 0) + { + descr_elem_result = (esp_gattc_descr_elem_t *) malloc(sizeof(esp_gattc_descr_elem_t) * count); + if (!descr_elem_result) + { + ChipLogProgress(Ble, "malloc error, gattc no mem"); + } + else + { + memcpy(¬ify_descr_uuid.uuid.uuid16, ShortUUID_CHIPoBLE_CharTx_Desc, 2); + ret_status = esp_ble_gattc_get_descr_by_char_handle( + gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, + p_data->reg_for_notify.handle, notify_descr_uuid, descr_elem_result, &count); + ChipLogProgress(Ble, "discoverd all chars and discr.........\n\n"); + + ChipDeviceEvent chipEvent; + chipEvent.Type = DeviceEventType::kPlatformESP32BLECentralConnected; + chipEvent.Platform.BLECentralConnected.mConnection = connId; + PlatformMgr().PostEventOrDie(&chipEvent); + if (ret_status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "esp_ble_gattc_get_descr_by_char_handle error"); + } + /* Every char has only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used first 'descr_elem_result' */ + if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && + descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) + { + } + + if (ret_status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "esp_ble_gattc_write_char_descr error"); + } + free(descr_elem_result); + } + } + } + ChipLogProgress(Ble, "decsr not found"); + } + break; + case ESP_GATTC_NOTIFY_EVT: + if (p_data->notify.is_notify) + { + ChipLogProgress(Ble, "ESP_GATTC_NOTIFY_EVT, receive notify value:"); + } + else + { + ChipLogProgress(Ble, "ESP_GATTC_NOTIFY_EVT, receive indicate value:"); + } + err = sInstance.HandleRXNotify(*p_data); + SuccessOrExit(err); + + break; + case ESP_GATTC_WRITE_DESCR_EVT: + if (p_data->write.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "write descr failed, error status = %x", p_data->write.status); + break; + } + ChipLogProgress(Ble, "write descr success "); + break; + case ESP_GATTC_SRVC_CHG_EVT: { + esp_bd_addr_t bda; + memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t)); + ChipLogProgress(Ble, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:"); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + if (p_data->write.status != ESP_GATT_OK) + { + ChipLogProgress(Ble, "write char failed, error status = %x", p_data->write.status); + break; + } + ChipLogProgress(Ble, "write char success "); + break; + case ESP_GATTC_DISCONNECT_EVT: + connect = false; + get_server = false; + ChipLogProgress(Ble, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason); + break; + default: + break; + } +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); + sInstance.mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + } + + // Schedule DriveBLEState() to run. + PlatformMgr().ScheduleWork(DriveBLEState, 0); +} + +static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t * param) +{ + /* If event is register event, store the for each profile */ + if (event == ESP_GATTC_REG_EVT) + { + if (param->reg.status == ESP_GATT_OK) + { + gl_profile_tab[param->reg.app_id].gattc_if = gattc_if; + } + else + { + ChipLogProgress(Ble, "reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status); + return; + } + } + + /* If the equal to profile A, call profile A cb handler, + * so here call each profile's callback */ + do + { + int idx; + for (idx = 0; idx < PROFILE_NUM; idx++) + { + if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb + function */ + gattc_if == gl_profile_tab[idx].gattc_if) + { + if (gl_profile_tab[idx].gattc_cb) + { + gl_profile_tab[idx].gattc_cb(event, gattc_if, param); + } + } + } + } while (0); +} + +void BLEManagerImpl::HandleConnectFailed(CHIP_ERROR error) +{ + if (sInstance.mIsCentral) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLECentralConnectFailed; + event.Platform.BLECentralConnectFailed.mError = error; + PlatformMgr().PostEventOrDie(&event); + } +} + +void BLEManagerImpl::CancelConnect(void) +{ + int rc = esp_ble_gattc_close(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, connId); + VerifyOrReturn(rc == 0, ChipLogError(Ble, "Failed to cancel connection rc=%d", rc)); +} + +void BLEManagerImpl::ConnectDevice(esp_bd_addr_t & addr, esp_ble_addr_type_t addr_type, uint16_t timeout) +{ + int rc; + rc = esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, addr, addr_type, true); + if (rc != 0) + { + ChipLogError(Ble, "Failed to connect to rc=%d", rc); } } +void HandleIncomingBleConnection(BLEEndPoint * bleEP) +{ + ChipLogProgress(DeviceLayer, "CHIPoBLE connection received"); +} +#endif + bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { - ChipLogProgress(DeviceLayer, "BLEManagerImpl::SubscribeCharacteristic() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + uint8_t value[2]; + int rc; + + value[0] = 0x02; + value[1] = 0x00; + + rc = esp_ble_gattc_write_char_descr(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, + descr_elem_result[0].handle, sizeof(value), value, ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + if (rc != 0) + { + ChipLogError(Ble, "esp_ble_gattc_get_descr_by_char_handle failed: %d", rc); + esp_ble_gattc_close(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, conId); + return false; + } + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLESubscribeOpComplete; + event.Platform.BLESubscribeOpComplete.mConnection = conId; + event.Platform.BLESubscribeOpComplete.mIsSubscribed = true; + PlatformMgr().PostEventOrDie(&event); + return true; +#else return false; +#endif } bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { - ChipLogProgress(DeviceLayer, "BLEManagerImpl::UnsubscribeCharacteristic() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + uint8_t value[2]; + int rc; + + value[0] = 0x00; + value[1] = 0x00; + + rc = esp_ble_gattc_write_char_descr(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, + descr_elem_result[0].handle, sizeof(value), value, ESP_GATT_WRITE_TYPE_RSP, + ESP_GATT_AUTH_REQ_NONE); + if (rc != 0) + { + ChipLogError(Ble, "ble_gattc_write_flat failed: %d", rc); + esp_ble_gattc_close(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, conId); + return false; + } + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLESubscribeOpComplete; + event.Platform.BLESubscribeOpComplete.mConnection = conId; + event.Platform.BLESubscribeOpComplete.mIsSubscribed = false; + PlatformMgr().PostEventOrDie(&event); + return true; +#else return false; +#endif } bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) @@ -311,10 +816,12 @@ bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) // Release the associated connection state record. ReleaseConnectionState(conId); +#if !CONFIG_ENABLE_ESP32_BLE_CONTROLLER // Force a refresh of the advertising state. mFlags.Set(Flags::kAdvertisingRefreshNeeded); mFlags.Clear(Flags::kAdvertisingConfigured); PlatformMgr().ScheduleWork(DriveBLEState, 0); +#endif return (err == CHIP_NO_ERROR); } @@ -336,6 +843,9 @@ bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUU VerifyOrExit(conState != NULL, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(conState->PendingIndBuf.IsNull(), err = CHIP_ERROR_INCORRECT_STATE); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + ChipLogDetail(Ble, "Sending indication for CHIPoBLE TX characteristic (con %u, len %u)", conId, data->DataLength()); +#endif err = MapBLEError(esp_ble_gatts_send_indicate(mAppIf, conId, mTXCharAttrHandle, data->DataLength(), data->Start(), false)); if (err != CHIP_NO_ERROR) @@ -360,8 +870,26 @@ bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUU bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, PacketBufferHandle pBuf) { - ChipLogError(DeviceLayer, "BLEManagerImpl::SendWriteRequest() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + ChipLogProgress(Ble, "In send write request\n"); + int rc; + + rc = esp_ble_gattc_write_char(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, + gl_profile_tab[PROFILE_A_APP_ID].write_char_handle, pBuf->DataLength(), pBuf->Start(), + ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + if (rc != 0) + { + ChipLogError(Ble, "ble_gattc_write_flat failed: %d", rc); + return false; + } + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLEWriteComplete; + event.Platform.BLEWriteComplete.mConnection = conId; + PlatformMgr().PostEventOrDie(&event); + return true; +#else return false; +#endif } bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, @@ -378,7 +906,10 @@ bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQU return false; } -void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) {} +void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) +{ + ChipLogProgress(Ble, "Got notification regarding chip connection closure"); +} CHIP_ERROR BLEManagerImpl::MapBLEError(int bleErr) { @@ -610,7 +1141,7 @@ CHIP_ERROR BLEManagerImpl::InitESPBleLayer(void) } // Set the maximum supported MTU size. - err = MapBLEError(esp_ble_gatt_set_local_mtu(ESP_GATT_MAX_MTU_SIZE)); + err = MapBLEError(esp_ble_gatt_set_local_mtu(CHIP_MAX_MTU_SIZE)); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "esp_ble_gatt_set_local_mtu() failed: %s", ErrorStr(err)); @@ -636,7 +1167,7 @@ CHIP_ERROR BLEManagerImpl::ConfigureAdvertisingData(void) if (!mFlags.Has(Flags::kUseCustomDeviceName)) { - snprintf(mDeviceName, sizeof(mDeviceName), "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); + ChipLogProgress(Ble, mDeviceName, sizeof(mDeviceName), "%s%04u", CHIP_DEVICE_CONFIG_BLE_DEVICE_NAME_PREFIX, discriminator); mDeviceName[kMaxDeviceNameLength] = 0; } @@ -816,7 +1347,7 @@ void BLEManagerImpl::HandleGATTControlEvent(esp_gatts_cb_event_t event, esp_gatt break; case ESP_GATTS_RESPONSE_EVT: - ESP_LOGD(TAG, "ESP_GATTS_RESPONSE_EVT (handle %u, status %d)", param->rsp.handle, (int) param->rsp.status); + ChipLogDetail(Ble, "ESP_GATTS_RESPONSE_EVT (handle %u, status %d)", param->rsp.handle, (int) param->rsp.status); break; default: @@ -898,7 +1429,7 @@ void BLEManagerImpl::HandleGATTCommEvent(esp_gatts_cb_event_t event, esp_gatt_if break; case ESP_GATTS_MTU_EVT: { - ESP_LOGD(TAG, "MTU for con %u: %u", param->mtu.conn_id, param->mtu.mtu); + ChipLogDetail(Ble, "MTU for con %u: %u", param->mtu.conn_id, param->mtu.mtu); CHIPoBLEConState * conState = GetConnectionState(param->mtu.conn_id); if (conState != NULL) { @@ -928,7 +1459,8 @@ void BLEManagerImpl::HandleRXCharWrite(esp_ble_gatts_cb_param_t * param) bool needResp = param->write.need_rsp; PacketBufferHandle buf; - ESP_LOGD(TAG, "Write request received for CHIPoBLE RX characteristic (con %u, len %u)", param->write.conn_id, param->write.len); + ChipLogDetail(Ble, "Write request received for CHIPoBLE RX characteristic (con %u, len %u)", param->write.conn_id, + param->write.len); // Disallow long writes. VerifyOrExit(param->write.is_prep == false, err = CHIP_ERROR_INVALID_ARGUMENT); @@ -970,7 +1502,7 @@ void BLEManagerImpl::HandleTXCharRead(esp_ble_gatts_cb_param_t * param) CHIP_ERROR err; esp_gatt_rsp_t rsp; - ESP_LOGD(TAG, "Read request received for CHIPoBLE TX characteristic (con %u)", param->read.conn_id); + ChipLogDetail(Ble, "Read request received for CHIPoBLE TX characteristic (con %u)", param->read.conn_id); // Send a zero-length response. memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); @@ -988,7 +1520,7 @@ void BLEManagerImpl::HandleTXCharCCCDRead(esp_ble_gatts_cb_param_t * param) CHIPoBLEConState * conState; esp_gatt_rsp_t rsp; - ESP_LOGD(TAG, "Read request received for CHIPoBLE TX characteristic CCCD (con %u)", param->read.conn_id); + ChipLogDetail(Ble, "Read request received for CHIPoBLE TX characteristic CCCD (con %u)", param->read.conn_id); // Find the connection state record. conState = GetConnectionState(param->read.conn_id); @@ -1016,8 +1548,8 @@ void BLEManagerImpl::HandleTXCharCCCDWrite(esp_ble_gatts_cb_param_t * param) bool needResp = param->write.need_rsp; bool indicationsEnabled; - ESP_LOGD(TAG, "Write request received for CHIPoBLE TX characteristic CCCD (con %u, len %u)", param->write.conn_id, - param->write.len); + ChipLogDetail(Ble, "Write request received for CHIPoBLE TX characteristic CCCD (con %u, len %u)", param->write.conn_id, + param->write.len); // Find the connection state record. conState = GetConnectionState(param->read.conn_id); @@ -1061,8 +1593,8 @@ void BLEManagerImpl::HandleTXCharCCCDWrite(esp_ble_gatts_cb_param_t * param) void BLEManagerImpl::HandleTXCharConfirm(CHIPoBLEConState * conState, esp_ble_gatts_cb_param_t * param) { - ESP_LOGD(TAG, "Confirm received for CHIPoBLE TX characteristic indication (con %u, status %u)", param->conf.conn_id, - param->conf.status); + ChipLogDetail(Ble, "Confirm received for CHIPoBLE TX characteristic indication (con %u, status %u)", param->conf.conn_id, + param->conf.status); // If there is a pending indication buffer for the connection, release it now. conState->PendingIndBuf = nullptr; @@ -1124,6 +1656,178 @@ void BLEManagerImpl::HandleDisconnect(esp_ble_gatts_cb_param_t * param) } } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +CHIP_ERROR BLEManagerImpl::HandleRXNotify(esp_ble_gattc_cb_param_t param) +{ + System::PacketBufferHandle buf = System::PacketBufferHandle::NewWithData(param.notify.value, param.notify.value_len); + VerifyOrReturnError(!buf.IsNull(), CHIP_ERROR_NO_MEMORY); + + ChipLogDetail(DeviceLayer, "Indication received, conn = %d", param.notify.conn_id); + + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLEIndicationReceived; + event.Platform.BLEIndicationReceived.mConnection = param.notify.conn_id; + event.Platform.BLEIndicationReceived.mData = std::move(buf).UnsafeRelease(); + PlatformMgr().PostEventOrDie(&event); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + mBLEAdvConfig.mpBleName = mDeviceName; + mBLEAdvConfig.mAdapterId = aAdapterId; + mBLEAdvConfig.mMajor = 1; + mBLEAdvConfig.mMinor = 1; + mBLEAdvConfig.mVendorId = 1; + mBLEAdvConfig.mProductId = 1; + mBLEAdvConfig.mDeviceId = 1; + mBLEAdvConfig.mDuration = 2; + mBLEAdvConfig.mPairingStatus = 0; + mBLEAdvConfig.mType = 1; + mBLEAdvConfig.mpAdvertisingUUID = "0xFFF6"; + + mIsCentral = aIsCentral; + if (mIsCentral) + { + err = MapBLEError(esp_ble_gattc_register_callback(esp_gattc_cb)); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "esp_ble_gattc_register_callback() failed: %s", ErrorStr(err)); + ExitNow(); + } + ChipLogProgress(Ble, "Before initialising (PROFILE\n"); + + int rc = esp_ble_gattc_app_register(PROFILE_A_APP_ID); + if (rc != 0) + { + ChipLogError(DeviceLayer, "esp_ble_gattc_app_register() failed %s", ErrorStr(err)); + ExitNow(); + } + } + + mFlags.Set(Flags::kESPBLELayerInitialized); + +exit: + if (err != CHIP_NO_ERROR) + return err; + + return CHIP_NO_ERROR; +} + +void BLEManagerImpl::OnDeviceScanned(esp_ble_addr_type_t & addr_type, esp_bd_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) +{ + ChipLogProgress(Ble, "In OnDeviceScanned\n"); + if (mBLEScanConfig.mBleScanState == BleScanState::kScanForDiscriminator) + { + if (!mBLEScanConfig.mDiscriminator.MatchesLongDiscriminator(info.GetDeviceDiscriminator())) + { + return; + } + ChipLogProgress(Ble, "Device Discriminator match. Attempting to connect"); + } + else if (mBLEScanConfig.mBleScanState == BleScanState::kScanForAddress) + { + ChipLogProgress(Ble, "Device Address match. Attempting to connect"); + } + else + { + ChipLogProgress(Ble, "Unknown discovery type. Ignoring"); + } + + connect = true; + mBLEScanConfig.mBleScanState = BleScanState::kConnecting; + DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(kConnectTimeout), HandleConnectTimeout, nullptr); + mDeviceScanner.StopScan(); + ChipLogProgress(Ble, "Scanned all devices\n"); + + ConnectDevice(addr, addr_type, kConnectTimeout); +} + +void BLEManagerImpl::OnScanComplete() +{ + ChipLogProgress(Ble, "Stop scan\n"); + if (mBLEScanConfig.mBleScanState != BleScanState::kScanForDiscriminator && + mBLEScanConfig.mBleScanState != BleScanState::kScanForAddress) + { + ChipLogProgress(Ble, "Scan complete notification without an active scan"); + return; + } + + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_TIMEOUT); + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + +void BLEManagerImpl::InitiateScan(BleScanState scanType) +{ + DriveBLEState(); + + // Check for a valid scan type + if (scanType == BleScanState::kNotScanning) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE); + ChipLogError(Ble, "Invalid scan type requested"); + return; + } + + // Initialize the device scanner + CHIP_ERROR err = mDeviceScanner.Init(this); + if (err != CHIP_NO_ERROR) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); + ChipLogError(Ble, "Failed to initialize device scanner: %s", ErrorStr(err)); + return; + } + + // Start scanning + mBLEScanConfig.mBleScanState = scanType; + err = mDeviceScanner.StartScan(kNewConnectionScanTimeout); + if (err != CHIP_NO_ERROR) + { + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; + ChipLogError(Ble, "Failed to start a BLE scan: %s", chip::ErrorStr(err)); + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); + return; + } +} + +void BLEManagerImpl::HandleConnectTimeout(chip::System::Layer *, void * context) +{ + CancelConnect(); + BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_TIMEOUT); +} + +void BLEManagerImpl::CleanScanConfig() +{ + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimeout, nullptr); + } + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + +void BLEManagerImpl::InitiateScan(intptr_t arg) +{ + sInstance.InitiateScan(static_cast(arg)); +} + +void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator) +{ + mBLEScanConfig.mDiscriminator = connDiscriminator; + mBLEScanConfig.mAppState = appState; + + // Initiate async scan + PlatformMgr().ScheduleWork(InitiateScan, static_cast(BleScanState::kScanForDiscriminator)); +} + +CHIP_ERROR BLEManagerImpl::CancelConnection() +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} +#endif + BLEManagerImpl::CHIPoBLEConState * BLEManagerImpl::GetConnectionState(uint16_t conId, bool allocate) { uint16_t freeIndex = kMaxConnections; @@ -1186,9 +1890,68 @@ uint16_t BLEManagerImpl::_NumConnections(void) return numCons; } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void BLEManagerImpl::HandleGAPConnectionFailed() +{ + if (sInstance.mIsCentral) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLECentralConnectFailed; + event.Platform.BLECentralConnectFailed.mError = CHIP_ERROR_INTERNAL; + PlatformMgr().PostEventOrDie(&event); + } +} + +CHIP_ERROR BLEManagerImpl::HandleGAPCentralConnect(esp_ble_gattc_cb_param_t p_data) +{ + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + { + ChipLogProgress(DeviceLayer, "BLE GAP connection established (con %u)", p_data.connect.conn_id); + + // remember the peer + connId = p_data.connect.conn_id; + gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data.connect.conn_id; + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data.connect.remote_bda, sizeof(esp_bd_addr_t)); + + // Start the GATT discovery process + int rc = esp_ble_gattc_search_service(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, connId, &remote_filter_service_uuid); + if (rc != 0) + { + HandleGAPConnectionFailed(); + ChipLogError(DeviceLayer, "peer_disc_al failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + } + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEManagerImpl::HandleGAPConnect(esp_ble_gattc_cb_param_t p_data) +{ + if (mIsCentral) + { + int rc; + gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data.connect.conn_id; + connId = p_data.connect.conn_id; + memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data.connect.remote_bda, sizeof(esp_bd_addr_t)); + rc = esp_ble_gattc_send_mtu_req(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, p_data.connect.conn_id); + + if (rc != 0) + { + ChipLogProgress(Ble, "MTU error\n"); + return CHIP_ERROR_INTERNAL; + } + + return HandleGAPCentralConnect(p_data); + } + return CHIP_NO_ERROR; +} +#endif + void BLEManagerImpl::HandleGATTEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t * param) { - ESP_LOGV(TAG, "GATT Event: %d (if %d)", (int) event, (int) gatts_if); + ChipLogProgress(Ble, "GATT Event: %d (if %d)", (int) event, (int) gatts_if); // This method is invoked on the ESP BLE thread. Therefore we must hold a lock // on the Chip stack while processing the event. @@ -1203,8 +1966,11 @@ void BLEManagerImpl::HandleGATTEvent(esp_gatts_cb_event_t event, esp_gatt_if_t g void BLEManagerImpl::HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t * param) { CHIP_ERROR err = CHIP_NO_ERROR; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + esp_ble_gap_cb_param_t * scan_result = (esp_ble_gap_cb_param_t *) param; +#endif - ESP_LOGV(TAG, "GAP Event: %d", (int) event); + ChipLogProgress(Ble, "GAP Event: %d", (int) event); // This method is invoked on the ESP BLE thread. Therefore we must hold a lock // on the Chip stack while processing the event. @@ -1212,8 +1978,7 @@ void BLEManagerImpl::HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb switch (event) { - case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { if (param->adv_data_cmpl.status != ESP_BT_STATUS_SUCCESS) { ChipLogError(DeviceLayer, "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT error: %d", (int) param->adv_data_cmpl.status); @@ -1222,11 +1987,10 @@ void BLEManagerImpl::HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb sInstance.mFlags.Set(Flags::kAdvertisingConfigured); sInstance.mFlags.Clear(Flags::kControlOpInProgress); + } + break; - break; - - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: - + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { ChipLogError(DeviceLayer, "ESP_GAP_BLE_ADV_START_COMPLETE_EVT error: %d", (int) param->adv_start_cmpl.status); @@ -1251,11 +2015,10 @@ void BLEManagerImpl::HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb err = PlatformMgr().PostEvent(&advChange); } } + } + break; - break; - - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: - + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { ChipLogError(DeviceLayer, "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT error: %d", (int) param->adv_stop_cmpl.status); @@ -1286,9 +2049,19 @@ void BLEManagerImpl::HandleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb err = PlatformMgr().PostEvent(&advChange); } } + } + break; - break; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + case ESP_GAP_BLE_SCAN_RESULT_EVT: { + mDeviceScanner.ReportDevice(*scan_result, scan_result->scan_rst.bda); + } + break; + case ESP_GAP_SEARCH_INQ_CMPL_EVT: + mDeviceScanner.mIsScanning = false; + break; +#endif default: break; } diff --git a/src/platform/ESP32/bluedroid/ChipDeviceScanner.cpp b/src/platform/ESP32/bluedroid/ChipDeviceScanner.cpp new file mode 100644 index 00000000000000..cea4b9f8d42f14 --- /dev/null +++ b/src/platform/ESP32/bluedroid/ChipDeviceScanner.cpp @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + +#include + +#include "esp_bt.h" +#include "esp_bt_main.h" +#include "esp_gap_ble_api.h" +#include "esp_gatt_common_api.h" +#include "esp_gatt_defs.h" +#include "esp_gattc_api.h" +#include "esp_gatts_api.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include +#include + +#define CHIPoBLE_SERVICE_UUID 0xFFF6 + +namespace chip { +namespace DeviceLayer { +namespace Internal { +namespace { + +/// Retrieve CHIP device identification info from the device advertising data +bool BluedroidGetChipDeviceInfo(esp_ble_gap_cb_param_t & scan_result, chip::Ble::ChipBLEDeviceIdentificationInfo & deviceInfo) +{ + // Check for CHIP Service UUID + if (scan_result.scan_rst.ble_adv != NULL) + { + if (scan_result.scan_rst.adv_data_len > 13 && scan_result.scan_rst.ble_adv[5] == 0xf6 && + scan_result.scan_rst.ble_adv[6] == 0xff) + { + deviceInfo.OpCode = scan_result.scan_rst.ble_adv[7]; + deviceInfo.DeviceDiscriminatorAndAdvVersion[0] = scan_result.scan_rst.ble_adv[8]; + deviceInfo.DeviceDiscriminatorAndAdvVersion[1] = scan_result.scan_rst.ble_adv[9]; + // vendor and product Id from adv + deviceInfo.DeviceVendorId[0] = scan_result.scan_rst.ble_adv[10]; + deviceInfo.DeviceVendorId[1] = scan_result.scan_rst.ble_adv[11]; + deviceInfo.DeviceProductId[0] = scan_result.scan_rst.ble_adv[12]; + deviceInfo.DeviceProductId[1] = scan_result.scan_rst.ble_adv[13]; + deviceInfo.AdditionalDataFlag = scan_result.scan_rst.ble_adv[14]; + return true; + } + } + return false; +} + +} // namespace + +void ChipDeviceScanner::ReportDevice(esp_ble_gap_cb_param_t & scan_result, esp_bd_addr_t & addr) +{ + chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo; + if (BluedroidGetChipDeviceInfo(scan_result, deviceInfo) == false) + { + return; + } + mDelegate->OnDeviceScanned(scan_result.scan_rst.ble_addr_type, addr, deviceInfo); +} + +void ChipDeviceScanner::RemoveDevice() +{ + // TODO +} + +CHIP_ERROR ChipDeviceScanner::StartScan(uint16_t timeout) +{ + ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE); + + static esp_ble_scan_params_t ble_scan_params = { .scan_type = BLE_SCAN_TYPE_PASSIVE, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, + .scan_interval = 0x00, + .scan_window = 0x00, + .scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE }; + + int rc = esp_ble_gap_set_scan_params(&ble_scan_params); + if (rc != 0) + { + ChipLogError(DeviceLayer, "esp_ble_gap_set_scan_params failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + /* Start the discovery process. */ + rc = esp_ble_gap_start_scanning(timeout); + if (rc != 0) + { + ChipLogError(DeviceLayer, "esp_ble_gap_start_scanning failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + mIsScanning = true; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ChipDeviceScanner::StopScan() +{ + ReturnErrorCodeIf(!mIsScanning, CHIP_NO_ERROR); + + int rc = esp_ble_gap_stop_scanning(); + if (rc != 0) + { + ChipLogError(DeviceLayer, "ble_gap_disc_cancel failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + mIsScanning = false; + mDelegate->OnScanComplete(); + return CHIP_NO_ERROR; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER diff --git a/src/platform/ESP32/nimble/BLEManagerImpl.cpp b/src/platform/ESP32/nimble/BLEManagerImpl.cpp index 3f5318ab9fec4e..048509db786b17 100644 --- a/src/platform/ESP32/nimble/BLEManagerImpl.cpp +++ b/src/platform/ESP32/nimble/BLEManagerImpl.cpp @@ -30,11 +30,17 @@ #if CONFIG_BT_NIMBLE_ENABLED +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#endif #include #include #include #include #include +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include +#endif #include #include #include @@ -44,6 +50,9 @@ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #include "esp_nimble_hci.h" #endif +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include "blecent.h" +#endif #include "host/ble_hs.h" #include "host/ble_hs_pvcy.h" #include "host/ble_uuid.h" @@ -67,6 +76,11 @@ namespace Internal { namespace { +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +static constexpr uint16_t kNewConnectionScanTimeout = 60; +static constexpr uint16_t kConnectTimeout = 20; +#endif + struct ESP32ChipServiceData { uint8_t ServiceUUID[2]; @@ -75,6 +89,10 @@ struct ESP32ChipServiceData const ble_uuid16_t ShortUUID_CHIPoBLEService = { BLE_UUID_TYPE_16, 0xFFF6 }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +const ble_uuid16_t ShortUUID_CHIPoBLE_CharTx_Desc = { BLE_UUID_TYPE_16, 0x2902 }; +#endif + const ble_uuid128_t UUID128_CHIPoBLEChar_RX = { BLE_UUID_TYPE_128, { 0x11, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18 } }; @@ -101,6 +119,9 @@ uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM; } // unnamed namespace +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +ChipDeviceScanner & mDeviceScanner = Internal::ChipDeviceScanner::GetInstance(); +#endif BLEManagerImpl BLEManagerImpl::sInstance; constexpr System::Clock::Timeout BLEManagerImpl::kFastAdvertiseTimeout; @@ -139,6 +160,55 @@ const struct ble_gatt_svc_def BLEManagerImpl::CHIPoBLEGATTAttrs[] = { }, }; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void BLEManagerImpl::HandleConnectFailed(CHIP_ERROR error) +{ + if (sInstance.mIsCentral) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLECentralConnectFailed; + event.Platform.BLECentralConnectFailed.mError = error; + PlatformMgr().PostEventOrDie(&event); + } +} + +void BLEManagerImpl::CancelConnect(void) +{ + int rc = ble_gap_conn_cancel(); + VerifyOrReturn(rc == 0, ChipLogError(Ble, "Failed to cancel connection rc=%d", rc)); +} + +void BLEManagerImpl::HandleConnectTimeout(chip::System::Layer *, void * context) +{ + CancelConnect(); + BLEManagerImpl::HandleConnectFailed(CHIP_ERROR_TIMEOUT); +} + +void BLEManagerImpl::ConnectDevice(const ble_addr_t & addr, uint16_t timeout) +{ + int rc; + uint8_t ownAddrType; + + rc = ble_hs_id_infer_auto(0, &ownAddrType); + if (rc != 0) + { + ChipLogError(Ble, "Failed to infer own address type rc=%d", rc); + return; + } + + rc = ble_gap_connect(ownAddrType, &addr, (timeout * 1000), NULL, ble_svr_gap_event, NULL); + if (rc != 0) + { + ChipLogError(Ble, "Failed to connect to rc=%d", rc); + } +} + +void HandleIncomingBleConnection(BLEEndPoint * bleEP) +{ + ChipLogProgress(DeviceLayer, "CHIPoBLE connection received"); +} +#endif + CHIP_ERROR BLEManagerImpl::_Init() { #if CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING @@ -159,7 +229,11 @@ CHIP_ERROR BLEManagerImpl::_Init() CHIP_ERROR err; // Initialize the Chip BleLayer. +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + err = BleLayer::Init(this, this, this, &DeviceLayer::SystemLayer()); +#else err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer()); +#endif SuccessOrExit(err); mRXCharAttrHandle = 0; @@ -167,8 +241,15 @@ CHIP_ERROR BLEManagerImpl::_Init() mC3CharAttrHandle = 0; #endif mTXCharCCCDAttrHandle = 0; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART && !mIsCentral); + mFlags.Set(Flags::kFastAdvertisingEnabled, !mIsCentral); + OnChipBleConnectReceived = HandleIncomingBleConnection; +#else mFlags.ClearAll().Set(Flags::kAdvertisingEnabled, CHIP_DEVICE_CONFIG_CHIPOBLE_ENABLE_ADVERTISING_AUTOSTART); mFlags.Set(Flags::kFastAdvertisingEnabled, true); + +#endif mNumGAPCons = 0; memset(reinterpret_cast(mCons), 0, sizeof(mCons)); mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled; @@ -311,20 +392,164 @@ void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) break; default: +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + HandlePlatformSpecificBLEEvent(event); +#endif break; } } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void BLEManagerImpl::HandlePlatformSpecificBLEEvent(const ChipDeviceEvent * apEvent) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLogProgress(DeviceLayer, "HandlePlatformSpecificBLEEvent %d", apEvent->Type); + + switch (apEvent->Type) + { + case DeviceEventType::kPlatformESP32BLECentralConnected: + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionComplete(mBLEScanConfig.mAppState, + apEvent->Platform.BLECentralConnected.mConnection); + CleanScanConfig(); + } + break; + + case DeviceEventType::kPlatformESP32BLECentralConnectFailed: + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, apEvent->Platform.BLECentralConnectFailed.mError); + CleanScanConfig(); + } + break; + + case DeviceEventType::kPlatformESP32BLEWriteComplete: + HandleWriteConfirmation(apEvent->Platform.BLEWriteComplete.mConnection, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_RX); + break; + + case DeviceEventType::kPlatformESP32BLESubscribeOpComplete: + if (apEvent->Platform.BLESubscribeOpComplete.mIsSubscribed) + HandleSubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, + &chipUUID_CHIPoBLEChar_TX); + else + HandleUnsubscribeComplete(apEvent->Platform.BLESubscribeOpComplete.mConnection, &CHIP_BLE_SVC_ID, + &chipUUID_CHIPoBLEChar_TX); + break; + + case DeviceEventType::kPlatformESP32BLEIndicationReceived: + HandleIndicationReceived(apEvent->Platform.BLEIndicationReceived.mConnection, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX, + PacketBufferHandle::Adopt(apEvent->Platform.BLEIndicationReceived.mData)); + break; + + default: + break; + } + + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Disabling CHIPoBLE service due to error: %s", ErrorStr(err)); + mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Disabled; + } +} + +static int OnUnsubscribeCharComplete(uint16_t conn_handle, const struct ble_gatt_error * error, struct ble_gatt_attr * attr, + void * arg) +{ + ChipLogProgress(DeviceLayer, "Subscribe complete: conn_handle=%d, error=%d, attr_handle=%d", conn_handle, error->status, + attr->handle); + + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLESubscribeOpComplete; + event.Platform.BLESubscribeOpComplete.mConnection = conn_handle; + event.Platform.BLESubscribeOpComplete.mIsSubscribed = false; + PlatformMgr().PostEventOrDie(&event); + + return 0; +} + +static int OnSubscribeCharComplete(uint16_t conn_handle, const struct ble_gatt_error * error, struct ble_gatt_attr * attr, + void * arg) +{ + ChipLogProgress(DeviceLayer, "Subscribe complete: conn_handle=%d, error=%d, attr_handle=%d", conn_handle, error->status, + attr->handle); + + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLESubscribeOpComplete; + event.Platform.BLESubscribeOpComplete.mConnection = conn_handle; + event.Platform.BLESubscribeOpComplete.mIsSubscribed = true; + PlatformMgr().PostEventOrDie(&event); + + return 0; +} +#endif + bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { - ChipLogProgress(DeviceLayer, "BLEManagerImpl::SubscribeCharacteristic() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + const struct peer_dsc * dsc; + uint8_t value[2]; + int rc; + struct peer * peer = peer_find(conId); + + dsc = peer_dsc_find_uuid(peer, (ble_uuid_t *) (&ShortUUID_CHIPoBLEService), (ble_uuid_t *) (&UUID_CHIPoBLEChar_TX), + (ble_uuid_t *) (&ShortUUID_CHIPoBLE_CharTx_Desc)); + + if (dsc == nullptr) + { + ChipLogError(Ble, "Peer does not have CCCD"); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + + value[0] = 0x02; + value[1] = 0x00; + + rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, value, sizeof(value), OnSubscribeCharComplete, NULL); + if (rc != 0) + { + ChipLogError(Ble, "ble_gattc_write_flat failed: %d", rc); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + return true; +#else return false; +#endif } bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId) { - ChipLogProgress(DeviceLayer, "BLEManagerImpl::UnsubscribeCharacteristic() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + const struct peer_dsc * dsc; + uint8_t value[2]; + int rc; + struct peer * peer = peer_find(conId); + + dsc = peer_dsc_find_uuid(peer, (ble_uuid_t *) (&ShortUUID_CHIPoBLEService), (ble_uuid_t *) (&UUID_CHIPoBLEChar_TX), + (ble_uuid_t *) (&ShortUUID_CHIPoBLE_CharTx_Desc)); + + if (dsc == nullptr) + { + ChipLogError(Ble, "Peer does not have CCCD"); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + + value[0] = 0x00; + value[1] = 0x00; + + rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle, value, sizeof(value), OnUnsubscribeCharComplete, NULL); + if (rc != 0) + { + ChipLogError(Ble, "ble_gattc_write_flat failed: %d", rc); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + return true; +#else return false; +#endif } bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) @@ -340,10 +565,12 @@ bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId) ChipLogError(DeviceLayer, "ble_gap_terminate() failed: %s", ErrorStr(err)); } +#if !CONFIG_ENABLE_ESP32_BLE_CONTROLLER // Force a refresh of the advertising state. mFlags.Set(Flags::kAdvertisingRefreshNeeded); mFlags.Clear(Flags::kAdvertisingConfigured); PlatformMgr().ScheduleWork(DriveBLEState, 0); +#endif return (err == CHIP_NO_ERROR); } @@ -354,7 +581,7 @@ uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const } bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, - PacketBufferHandle data) + chip::System::PacketBufferHandle data) { CHIP_ERROR err = CHIP_NO_ERROR; struct os_mbuf * om; @@ -387,15 +614,54 @@ bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUU return true; } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +static int OnWriteComplete(uint16_t conn_handle, const struct ble_gatt_error * error, struct ble_gatt_attr * attr, void * arg) +{ + ChipLogDetail(Ble, "Write complete; status:%d conn_handle:%d attr_handle:%d", error->status, conn_handle, attr->handle); + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLEWriteComplete; + event.Platform.BLEWriteComplete.mConnection = conn_handle; + CHIP_ERROR err = PlatformMgr().PostEvent(&event); + if (err != CHIP_NO_ERROR) + { + return 1; + } + + return 0; +} +#endif + bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, - PacketBufferHandle pBuf) + chip::System::PacketBufferHandle pBuf) { - ChipLogError(DeviceLayer, "BLEManagerImpl::SendWriteRequest() not supported"); +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + const struct peer_chr * chr; + int rc; + const struct peer * peer = peer_find(conId); + + chr = peer_chr_find_uuid(peer, (ble_uuid_t *) (&ShortUUID_CHIPoBLEService), (ble_uuid_t *) (&UUID128_CHIPoBLEChar_RX)); + if (chr == nullptr) + { + ChipLogError(Ble, "Peer does not have RX characteristic"); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + + rc = ble_gattc_write_flat(conId, chr->chr.val_handle, pBuf->Start(), pBuf->DataLength(), OnWriteComplete, this); + if (rc != 0) + { + ChipLogError(Ble, "ble_gattc_write_flat failed: %d", rc); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return false; + } + return true; +#else return false; +#endif } bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId, - PacketBufferHandle pBuf) + chip::System::PacketBufferHandle pBuf) { ChipLogError(DeviceLayer, "BLEManagerImpl::SendReadRequest() not supported"); return false; @@ -884,7 +1150,76 @@ uint16_t BLEManagerImpl::_NumConnections(void) return numCons; } -CHIP_ERROR BLEManagerImpl::HandleGAPConnect(struct ble_gap_event * gapEvent) +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +void BLEManagerImpl::HandleGAPConnectionFailed(struct ble_gap_event * gapEvent, CHIP_ERROR error) +{ + ChipLogError(Ble, "BLE GAP connection failed; status:%d", gapEvent->connect.status); + if (sInstance.mIsCentral) + { + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLECentralConnectFailed; + event.Platform.BLECentralConnectFailed.mError = error; + PlatformMgr().PostEventOrDie(&event); + } +} + +void BLEManagerImpl::OnGattDiscComplete(const struct peer * peer, int status, void * arg) +{ + if (status != 0) + { + ChipLogError(Ble, "GATT discovery failed; status:%d", status); + ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return; + } + + ChipLogProgress(Ble, "GATT discovery complete status:%d conn_handle:%d", status, peer->conn_handle); + + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLECentralConnected; + event.Platform.BLECentralConnected.mConnection = peer->conn_handle; + PlatformMgr().PostEventOrDie(&event); +} + +CHIP_ERROR BLEManagerImpl::HandleGAPCentralConnect(struct ble_gap_event * gapEvent) +{ + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + if (gapEvent->connect.status == 0) + { + // Track the number of active GAP connections. + mNumGAPCons++; + + ChipLogProgress(DeviceLayer, "BLE GAP connection established (con %u)", gapEvent->connect.conn_handle); + + // remember the peer + int rc = peer_add(gapEvent->connect.conn_handle); + if (rc != 0) + { + HandleGAPConnectionFailed(gapEvent, CHIP_ERROR_INTERNAL); + ChipLogError(DeviceLayer, "peer_add failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + + // Start the GATT discovery process + rc = peer_disc_all(gapEvent->connect.conn_handle, OnGattDiscComplete, NULL); + if (rc != 0) + { + HandleGAPConnectionFailed(gapEvent, CHIP_ERROR_INTERNAL); + ChipLogError(DeviceLayer, "peer_disc_al failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + } + else + { + HandleGAPConnectionFailed(gapEvent, CHIP_ERROR_INTERNAL); + return CHIP_ERROR_INTERNAL; + } + } + return CHIP_NO_ERROR; +} +#endif + +CHIP_ERROR BLEManagerImpl::HandleGAPPeripheralConnect(struct ble_gap_event * gapEvent) { CHIP_ERROR err = CHIP_NO_ERROR; ChipLogProgress(DeviceLayer, "BLE GAP connection established (con %u)", gapEvent->connect.conn_handle); @@ -902,6 +1237,23 @@ CHIP_ERROR BLEManagerImpl::HandleGAPConnect(struct ble_gap_event * gapEvent) return err; } +CHIP_ERROR BLEManagerImpl::HandleGAPConnect(struct ble_gap_event * gapEvent) +{ +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + int rc; + + rc = ble_gattc_exchange_mtu(gapEvent->connect.conn_handle, NULL, NULL); + if (rc != 0) + { + return CHIP_ERROR_INTERNAL; + } + + return HandleGAPCentralConnect(gapEvent); +#else + return HandleGAPPeripheralConnect(gapEvent); +#endif +} + CHIP_ERROR BLEManagerImpl::HandleGAPDisconnect(struct ble_gap_event * gapEvent) { ChipLogProgress(DeviceLayer, "BLE GAP connection terminated (con %u reason 0x%02x)", gapEvent->disconnect.conn.conn_handle, @@ -913,6 +1265,10 @@ CHIP_ERROR BLEManagerImpl::HandleGAPDisconnect(struct ble_gap_event * gapEvent) mNumGAPCons--; } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + peer_delete(gapEvent->disconnect.conn.conn_handle); +#endif + if (UnsetSubscribed(gapEvent->disconnect.conn.conn_handle)) { CHIP_ERROR disconReason; @@ -1039,6 +1395,15 @@ int BLEManagerImpl::ble_svr_gap_event(struct ble_gap_event * event, void * arg) ESP_LOGD(TAG, "BLE_GAP_EVENT_MTU = %d channel id = %d", event->mtu.value, event->mtu.channel_id); break; +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + case BLE_GAP_EVENT_NOTIFY_RX: + ESP_LOGD(TAG, "BLE_GAP_EVENT_NOTIFY_RX received %s conn_handle:%d attr_handle:%d attr_len:%d", + event->notify_rx.indication ? "indication" : "notification", event->notify_rx.conn_handle, + event->notify_rx.attr_handle, OS_MBUF_PKTLEN(event->notify_rx.om)); + err = sInstance.HandleRXNotify(event); + SuccessOrExit(err); + break; +#endif default: break; } @@ -1237,6 +1602,152 @@ void BLEManagerImpl::DriveBLEState(intptr_t arg) sInstance.DriveBLEState(); } +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +CHIP_ERROR BLEManagerImpl::HandleRXNotify(struct ble_gap_event * ble_event) +{ + uint8_t * data = OS_MBUF_DATA(ble_event->notify_rx.om, uint8_t *); + size_t dataLen = OS_MBUF_PKTLEN(ble_event->notify_rx.om); + System::PacketBufferHandle buf = System::PacketBufferHandle::NewWithData(data, dataLen); + VerifyOrReturnError(!buf.IsNull(), CHIP_ERROR_NO_MEMORY); + + ChipLogDetail(DeviceLayer, "Indication received, conn = %d", ble_event->notify_rx.conn_handle); + + ChipDeviceEvent event; + event.Type = DeviceEventType::kPlatformESP32BLEIndicationReceived; + event.Platform.BLEIndicationReceived.mConnection = ble_event->notify_rx.conn_handle; + event.Platform.BLEIndicationReceived.mData = std::move(buf).UnsafeRelease(); + PlatformMgr().PostEventOrDie(&event); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEManagerImpl::ConfigureBle(uint32_t aAdapterId, bool aIsCentral) +{ + mBLEAdvConfig.mpBleName = mDeviceName; + mBLEAdvConfig.mAdapterId = aAdapterId; + mBLEAdvConfig.mMajor = 1; + mBLEAdvConfig.mMinor = 1; + mBLEAdvConfig.mVendorId = 1; + mBLEAdvConfig.mProductId = 1; + mBLEAdvConfig.mDeviceId = 1; + mBLEAdvConfig.mDuration = 2; + mBLEAdvConfig.mPairingStatus = 0; + mBLEAdvConfig.mType = 1; + mBLEAdvConfig.mpAdvertisingUUID = "0xFFF6"; + + mIsCentral = aIsCentral; + if (mIsCentral) + { + int rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); + assert(rc == 0); + } + + return CHIP_NO_ERROR; +} + +void BLEManagerImpl::OnDeviceScanned(const struct ble_hs_adv_fields & fields, const ble_addr_t & addr, + const chip::Ble::ChipBLEDeviceIdentificationInfo & info) +{ + + if (mBLEScanConfig.mBleScanState == BleScanState::kScanForDiscriminator) + { + if (!mBLEScanConfig.mDiscriminator.MatchesLongDiscriminator(info.GetDeviceDiscriminator())) + { + return; + } + ChipLogProgress(Ble, "Device Discriminator match. Attempting to connect"); + } + else if (mBLEScanConfig.mBleScanState == BleScanState::kScanForAddress) + { + ChipLogProgress(Ble, "Device Address match. Attempting to connect"); + } + else + { + ChipLogProgress(Ble, "Unknown discovery type. Ignoring"); + } + + mBLEScanConfig.mBleScanState = BleScanState::kConnecting; + DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(kConnectTimeout), HandleConnectTimeout, nullptr); + mDeviceScanner.StopScan(); + + ConnectDevice(addr, kConnectTimeout); +} + +void BLEManagerImpl::OnScanComplete() +{ + if (mBLEScanConfig.mBleScanState != BleScanState::kScanForDiscriminator && + mBLEScanConfig.mBleScanState != BleScanState::kScanForAddress) + { + ChipLogProgress(Ble, "Scan complete notification without an active scan"); + return; + } + + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_TIMEOUT); + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + +void BLEManagerImpl::InitiateScan(BleScanState scanType) +{ + DriveBLEState(); + + // Check for a valid scan type + if (scanType == BleScanState::kNotScanning) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, CHIP_ERROR_INCORRECT_STATE); + ChipLogError(Ble, "Invalid scan type requested"); + return; + } + + // Initialize the device scanner + CHIP_ERROR err = mDeviceScanner.Init(this); + if (err != CHIP_NO_ERROR) + { + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); + ChipLogError(Ble, "Failed to initialize device scanner: %s", ErrorStr(err)); + return; + } + + // Start scanning + mBLEScanConfig.mBleScanState = scanType; + err = mDeviceScanner.StartScan(kNewConnectionScanTimeout); + if (err != CHIP_NO_ERROR) + { + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; + ChipLogError(Ble, "Failed to start a BLE scan: %s", chip::ErrorStr(err)); + BleConnectionDelegate::OnConnectionError(mBLEScanConfig.mAppState, err); + return; + } +} + +void BLEManagerImpl::CleanScanConfig() +{ + if (BLEManagerImpl::mBLEScanConfig.mBleScanState == BleScanState::kConnecting) + { + DeviceLayer::SystemLayer().CancelTimer(HandleConnectTimeout, nullptr); + } + mBLEScanConfig.mBleScanState = BleScanState::kNotScanning; +} + +void BLEManagerImpl::InitiateScan(intptr_t arg) +{ + sInstance.InitiateScan(static_cast(arg)); +} + +void BLEManagerImpl::NewConnection(BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator) +{ + mBLEScanConfig.mDiscriminator = connDiscriminator; + mBLEScanConfig.mAppState = appState; + + // Initiate async scan + PlatformMgr().ScheduleWork(InitiateScan, static_cast(BleScanState::kScanForDiscriminator)); +} + +CHIP_ERROR BLEManagerImpl::CancelConnection() +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} +#endif + } // namespace Internal } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/ESP32/nimble/ChipDeviceScanner.cpp b/src/platform/ESP32/nimble/ChipDeviceScanner.cpp new file mode 100644 index 00000000000000..5bf90bae136f60 --- /dev/null +++ b/src/platform/ESP32/nimble/ChipDeviceScanner.cpp @@ -0,0 +1,156 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + +#include "blecent.h" +#include +#include +#include + +#define CHIPoBLE_SERVICE_UUID 0xFFF6 + +namespace chip { +namespace DeviceLayer { +namespace Internal { +namespace { + +/// Retrieve CHIP device identification info from the device advertising data +bool NimbleGetChipDeviceInfo(const ble_hs_adv_fields & fields, chip::Ble::ChipBLEDeviceIdentificationInfo & deviceInfo) +{ + // Check for CHIP Service UUID + + if (fields.svc_data_uuid16 != NULL) + { + if (fields.svc_data_uuid16_len > 8 && fields.svc_data_uuid16[0] == 0xf6 && fields.svc_data_uuid16[1] == 0xff) + { + deviceInfo.OpCode = fields.svc_data_uuid16[2]; + deviceInfo.DeviceDiscriminatorAndAdvVersion[0] = fields.svc_data_uuid16[3]; + deviceInfo.DeviceDiscriminatorAndAdvVersion[1] = fields.svc_data_uuid16[4]; + // vendor and product Id from adv + deviceInfo.DeviceVendorId[0] = fields.svc_data_uuid16[5]; + deviceInfo.DeviceVendorId[1] = fields.svc_data_uuid16[6]; + deviceInfo.DeviceProductId[0] = fields.svc_data_uuid16[7]; + deviceInfo.DeviceProductId[1] = fields.svc_data_uuid16[8]; + deviceInfo.AdditionalDataFlag = fields.svc_data_uuid16[9]; + return true; + } + } + return false; +} + +} // namespace + +void ChipDeviceScanner::ReportDevice(const struct ble_hs_adv_fields & fields, const ble_addr_t & addr) +{ + chip::Ble::ChipBLEDeviceIdentificationInfo deviceInfo; + if (NimbleGetChipDeviceInfo(fields, deviceInfo) == false) + { + ChipLogDetail(Ble, "Device %s does not look like a CHIP device", addr_str(addr.val)); + return; + } + mDelegate->OnDeviceScanned(fields, addr, deviceInfo); +} + +void ChipDeviceScanner::RemoveDevice() +{ + // TODO +} + +int ChipDeviceScanner::OnBleCentralEvent(struct ble_gap_event * event, void * arg) +{ + ChipDeviceScanner * scanner = (ChipDeviceScanner *) arg; + + switch (event->type) + { + case BLE_GAP_EVENT_DISC_COMPLETE: { + scanner->mIsScanning = false; + return 0; + } + + case BLE_GAP_EVENT_DISC: { + + /* Try to connect to the advertiser if it looks interesting. */ + struct ble_hs_adv_fields fields; + ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data); + scanner->ReportDevice(fields, event->disc.addr); + return 0; + } + } + + return 0; +} + +CHIP_ERROR ChipDeviceScanner::StartScan(uint16_t timeout) +{ + ReturnErrorCodeIf(mIsScanning, CHIP_ERROR_INCORRECT_STATE); + + uint8_t ownAddrType; + struct ble_gap_disc_params discParams; + int rc; + + /* Figure out address to use while advertising. */ + rc = ble_hs_id_infer_auto(0, &ownAddrType); + if (rc != 0) + { + ChipLogError(DeviceLayer, "ble_hs_id_infer_auto failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + + /* Set up discovery parameters. */ + memset(&discParams, 0, sizeof(discParams)); + + /* Tell the controller to filter the duplicates. */ + discParams.filter_duplicates = 1; + /* Perform passive scanning. */ + discParams.passive = 1; + /* Use defaults for the rest of the parameters. */ + discParams.itvl = 0; + discParams.window = 0; + discParams.filter_policy = 0; + discParams.limited = 0; + + /* Start the discovery process. */ + rc = ble_gap_disc(ownAddrType, (timeout * 1000), &discParams, OnBleCentralEvent, this); + if (rc != 0) + { + ChipLogError(DeviceLayer, "ble_gap_disc failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + mIsScanning = true; + return CHIP_NO_ERROR; +} + +CHIP_ERROR ChipDeviceScanner::StopScan() +{ + ReturnErrorCodeIf(!mIsScanning, CHIP_NO_ERROR); + + int rc = ble_gap_disc_cancel(); + if (rc != 0) + { + ChipLogError(DeviceLayer, "ble_gap_disc_cancel failed: %d", rc); + return CHIP_ERROR_INTERNAL; + } + mIsScanning = false; + mDelegate->OnScanComplete(); + return CHIP_NO_ERROR; +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace chip +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER diff --git a/src/platform/ESP32/nimble/blecent.h b/src/platform/ESP32/nimble/blecent.h new file mode 100644 index 00000000000000..d61eb50bc0fc9d --- /dev/null +++ b/src/platform/ESP32/nimble/blecent.h @@ -0,0 +1,110 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#pragma once +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Misc. */ +void print_bytes(const uint8_t * bytes, int len); +void print_mbuf(const struct os_mbuf * om); +char * addr_str(const void * addr); +void print_uuid(const ble_uuid_t * uuid); + +/** Peer. */ +struct peer_dsc +{ + SLIST_ENTRY(peer_dsc) next; + struct ble_gatt_dsc dsc; +}; +SLIST_HEAD(peer_dsc_list, peer_dsc); + +struct peer_chr +{ + SLIST_ENTRY(peer_chr) next; + struct ble_gatt_chr chr; + + struct peer_dsc_list dscs; +}; +SLIST_HEAD(peer_chr_list, peer_chr); + +struct peer_svc +{ + SLIST_ENTRY(peer_svc) next; + struct ble_gatt_svc svc; + + struct peer_chr_list chrs; +}; +SLIST_HEAD(peer_svc_list, peer_svc); + +struct peer; +typedef void peer_disc_fn(const struct peer * peer, int status, void * arg); + +struct peer +{ + SLIST_ENTRY(peer) next; + + uint16_t conn_handle; + + /** List of discovered GATT services. */ + struct peer_svc_list svcs; + + /** Keeps track of where we are in the service discovery process. */ + uint16_t disc_prev_chr_val; + struct peer_svc * cur_svc; + + /** Callback that gets executed when service discovery completes. */ + peer_disc_fn * disc_cb; + void * disc_cb_arg; +}; + +int peer_disc_all(uint16_t conn_handle, peer_disc_fn * disc_cb, void * disc_cb_arg); +const struct peer_dsc * peer_dsc_find_uuid(const struct peer * peer, const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid, + const ble_uuid_t * dsc_uuid); +const struct peer_chr * peer_chr_find_uuid(const struct peer * peer, const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid); +const struct peer_svc * peer_svc_find_uuid(const struct peer * peer, const ble_uuid_t * uuid); +int peer_delete(uint16_t conn_handle); +int peer_add(uint16_t conn_handle); +int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs); +struct peer * peer_find(uint16_t conn_handle); + +#ifdef __cplusplus +} +#endif +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER diff --git a/src/platform/ESP32/nimble/misc.c b/src/platform/ESP32/nimble/misc.c new file mode 100644 index 00000000000000..2c887dc6918756 --- /dev/null +++ b/src/platform/ESP32/nimble/misc.c @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER +#include "blecent.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" +#include +#include +#include + +/** + * Utility function to log an array of bytes. + */ +void print_bytes(const uint8_t * bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); + } +} + +void print_mbuf(const struct os_mbuf * om) +{ + int colon, i; + + colon = 0; + while (om != NULL) + { + if (colon) + { + MODLOG_DFLT(DEBUG, ":"); + } + else + { + colon = 1; + } + for (i = 0; i < om->om_len; i++) + { + MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", om->om_data[i]); + } + om = SLIST_NEXT(om, om_next); + } +} + +char * addr_str(const void * addr) +{ + static char buf[6 * 2 + 5 + 1]; + const uint8_t * u8p; + + u8p = addr; + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); + + return buf; +} + +void print_uuid(const ble_uuid_t * uuid) +{ + char buf[BLE_UUID_STR_LEN]; + + MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf)); +} + +/** + * Logs information about a connection to the console. + */ +void print_conn_desc(const struct ble_gap_conn_desc * desc) +{ + MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ", desc->conn_handle, desc->our_ota_addr.type, + addr_str(desc->our_ota_addr.val)); + MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ", desc->our_id_addr.type, addr_str(desc->our_id_addr.val)); + MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ", desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val)); + MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ", desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val)); + MODLOG_DFLT(DEBUG, + "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, desc->supervision_timeout, desc->sec_state.encrypted, + desc->sec_state.authenticated, desc->sec_state.bonded); +} + +void print_adv_fields(const struct ble_hs_adv_fields * fields) +{ + char s[BLE_HS_ADV_MAX_SZ]; + const uint8_t * u8p; + int i; + + if (fields->flags != 0) + { + MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags); + } + + if (fields->uuids16 != NULL) + { + MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=", fields->uuids16_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids16; i++) + { + print_uuid(&fields->uuids16[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids32 != NULL) + { + MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=", fields->uuids32_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids32; i++) + { + print_uuid(&fields->uuids32[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uuids128 != NULL) + { + MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=", fields->uuids128_is_complete ? "" : "in"); + for (i = 0; i < fields->num_uuids128; i++) + { + print_uuid(&fields->uuids128[i].u); + MODLOG_DFLT(DEBUG, " "); + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->name != NULL) + { + assert(fields->name_len < sizeof s - 1); + memcpy(s, fields->name, fields->name_len); + s[fields->name_len] = '\0'; + MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n", fields->name_is_complete ? "" : "in", s); + } + + if (fields->tx_pwr_lvl_is_present) + { + MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl); + } + + if (fields->slave_itvl_range != NULL) + { + MODLOG_DFLT(DEBUG, " slave_itvl_range="); + print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid16 != NULL) + { + MODLOG_DFLT(DEBUG, " svc_data_uuid16="); + print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->public_tgt_addr != NULL) + { + MODLOG_DFLT(DEBUG, " public_tgt_addr="); + u8p = fields->public_tgt_addr; + for (i = 0; i < fields->num_public_tgt_addrs; i++) + { + MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p)); + u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; + } + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->appearance_is_present) + { + MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance); + } + + if (fields->adv_itvl_is_present) + { + MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl); + } + + if (fields->svc_data_uuid32 != NULL) + { + MODLOG_DFLT(DEBUG, " svc_data_uuid32="); + print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->svc_data_uuid128 != NULL) + { + MODLOG_DFLT(DEBUG, " svc_data_uuid128="); + print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->uri != NULL) + { + MODLOG_DFLT(DEBUG, " uri="); + print_bytes(fields->uri, fields->uri_len); + MODLOG_DFLT(DEBUG, "\n"); + } + + if (fields->mfg_data != NULL) + { + MODLOG_DFLT(DEBUG, " mfg_data="); + print_bytes(fields->mfg_data, fields->mfg_data_len); + MODLOG_DFLT(DEBUG, "\n"); + } +} +#endif // CONFIG_ENABLE_ESP32_BLE_CONTROLLER diff --git a/src/platform/ESP32/nimble/peer.c b/src/platform/ESP32/nimble/peer.c new file mode 100644 index 00000000000000..0dbf54475019c6 --- /dev/null +++ b/src/platform/ESP32/nimble/peer.c @@ -0,0 +1,828 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER + +#include "blecent.h" +#include "host/ble_hs.h" +#include +#include + +static void * peer_svc_mem; +static struct os_mempool peer_svc_pool; + +static void * peer_chr_mem; +static struct os_mempool peer_chr_pool; + +static void * peer_dsc_mem; +static struct os_mempool peer_dsc_pool; + +static void * peer_mem; +static struct os_mempool peer_pool; +static SLIST_HEAD(, peer) peers; + +static struct peer_svc * peer_svc_find_range(struct peer * peer, uint16_t attr_handle); +static struct peer_svc * peer_svc_find(struct peer * peer, uint16_t svc_start_handle, struct peer_svc ** out_prev); +int peer_svc_is_empty(const struct peer_svc * svc); + +uint16_t chr_end_handle(const struct peer_svc * svc, const struct peer_chr * chr); +int chr_is_empty(const struct peer_svc * svc, const struct peer_chr * chr); +static struct peer_chr * peer_chr_find(const struct peer_svc * svc, uint16_t chr_def_handle, struct peer_chr ** out_prev); +static void peer_disc_chrs(struct peer * peer); + +static int peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error * error, uint16_t chr_val_handle, + const struct ble_gatt_dsc * dsc, void * arg); + +struct peer * peer_find(uint16_t conn_handle) +{ + struct peer * peer; + + SLIST_FOREACH(peer, &peers, next) + { + if (peer->conn_handle == conn_handle) + { + return peer; + } + } + + return NULL; +} + +static void peer_disc_complete(struct peer * peer, int rc) +{ + peer->disc_prev_chr_val = 0; + + /* Notify caller that discovery has completed. */ + if (peer->disc_cb != NULL) + { + peer->disc_cb(peer, rc, peer->disc_cb_arg); + } +} + +static struct peer_dsc * peer_dsc_find_prev(const struct peer_chr * chr, uint16_t dsc_handle) +{ + struct peer_dsc * prev; + struct peer_dsc * dsc; + + prev = NULL; + SLIST_FOREACH(dsc, &chr->dscs, next) + { + if (dsc->dsc.handle >= dsc_handle) + { + break; + } + + prev = dsc; + } + + return prev; +} + +static struct peer_dsc * peer_dsc_find(const struct peer_chr * chr, uint16_t dsc_handle, struct peer_dsc ** out_prev) +{ + struct peer_dsc * prev; + struct peer_dsc * dsc; + + prev = peer_dsc_find_prev(chr, dsc_handle); + if (prev == NULL) + { + dsc = SLIST_FIRST(&chr->dscs); + } + else + { + dsc = SLIST_NEXT(prev, next); + } + + if (dsc != NULL && dsc->dsc.handle != dsc_handle) + { + dsc = NULL; + } + + if (out_prev != NULL) + { + *out_prev = prev; + } + return dsc; +} + +static int peer_dsc_add(struct peer * peer, uint16_t chr_val_handle, const struct ble_gatt_dsc * gatt_dsc) +{ + struct peer_dsc * prev; + struct peer_dsc * dsc; + struct peer_svc * svc; + struct peer_chr * chr; + + svc = peer_svc_find_range(peer, chr_val_handle); + if (svc == NULL) + { + /* Can't find service for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, chr_val_handle, NULL); + if (chr == NULL) + { + /* Can't find characteristic for discovered descriptor; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev); + if (dsc != NULL) + { + /* Descriptor already discovered. */ + return 0; + } + + dsc = os_memblock_get(&peer_dsc_pool); + if (dsc == NULL) + { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(dsc, 0, sizeof *dsc); + + dsc->dsc = *gatt_dsc; + + if (prev == NULL) + { + SLIST_INSERT_HEAD(&chr->dscs, dsc, next); + } + else + { + SLIST_NEXT(prev, next) = dsc; + } + + return 0; +} + +static void peer_disc_dscs(struct peer * peer) +{ + struct peer_chr * chr; + struct peer_svc * svc; + int rc; + + /* Search through the list of discovered characteristics for the first + * characteristic that contains undiscovered descriptors. Then, discover + * all descriptors belonging to that characteristic. + */ + SLIST_FOREACH(svc, &peer->svcs, next) + { + SLIST_FOREACH(chr, &svc->chrs, next) + { + if (!chr_is_empty(svc, chr) && SLIST_EMPTY(&chr->dscs) && peer->disc_prev_chr_val <= chr->chr.def_handle) + { + + rc = ble_gattc_disc_all_dscs(peer->conn_handle, chr->chr.val_handle, chr_end_handle(svc, chr), peer_dsc_disced, + peer); + if (rc != 0) + { + peer_disc_complete(peer, rc); + } + + peer->disc_prev_chr_val = chr->chr.val_handle; + return; + } + } + } + + /* All descriptors discovered. */ + peer_disc_complete(peer, 0); +} + +static int peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error * error, uint16_t chr_val_handle, + const struct ble_gatt_dsc * dsc, void * arg) +{ + struct peer * peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) + { + case 0: + rc = peer_dsc_add(peer, chr_val_handle, dsc); + break; + + case BLE_HS_EDONE: + /* All descriptors in this characteristic discovered; start discovering + * descriptors in the next characteristic. + */ + if (peer->disc_prev_chr_val > 0) + { + peer_disc_dscs(peer); + } + rc = 0; + break; + + default: + /* Error; abort discovery. */ + rc = error->status; + break; + } + + if (rc != 0) + { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +uint16_t chr_end_handle(const struct peer_svc * svc, const struct peer_chr * chr) +{ + const struct peer_chr * next_chr; + + next_chr = SLIST_NEXT(chr, next); + if (next_chr != NULL) + { + return next_chr->chr.def_handle - 1; + } + else + { + return svc->svc.end_handle; + } +} + +int chr_is_empty(const struct peer_svc * svc, const struct peer_chr * chr) +{ + return chr_end_handle(svc, chr) <= chr->chr.val_handle; +} + +static struct peer_chr * peer_chr_find_prev(const struct peer_svc * svc, uint16_t chr_val_handle) +{ + struct peer_chr * prev; + struct peer_chr * chr; + + prev = NULL; + SLIST_FOREACH(chr, &svc->chrs, next) + { + if (chr->chr.val_handle >= chr_val_handle) + { + break; + } + + prev = chr; + } + + return prev; +} + +static struct peer_chr * peer_chr_find(const struct peer_svc * svc, uint16_t chr_val_handle, struct peer_chr ** out_prev) +{ + struct peer_chr * prev; + struct peer_chr * chr; + + prev = peer_chr_find_prev(svc, chr_val_handle); + if (prev == NULL) + { + chr = SLIST_FIRST(&svc->chrs); + } + else + { + chr = SLIST_NEXT(prev, next); + } + + if (chr != NULL && chr->chr.val_handle != chr_val_handle) + { + chr = NULL; + } + + if (out_prev != NULL) + { + *out_prev = prev; + } + return chr; +} + +static void peer_chr_delete(struct peer_chr * chr) +{ + struct peer_dsc * dsc; + + while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) + { + SLIST_REMOVE_HEAD(&chr->dscs, next); + os_memblock_put(&peer_dsc_pool, dsc); + } + + os_memblock_put(&peer_chr_pool, chr); +} + +static int peer_chr_add(struct peer * peer, uint16_t svc_start_handle, const struct ble_gatt_chr * gatt_chr) +{ + struct peer_chr * prev; + struct peer_chr * chr; + struct peer_svc * svc; + + svc = peer_svc_find(peer, svc_start_handle, NULL); + if (svc == NULL) + { + /* Can't find service for discovered characteristic; this shouldn't + * happen. + */ + assert(0); + return BLE_HS_EUNKNOWN; + } + + chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); + if (chr != NULL) + { + /* Characteristic already discovered. */ + return 0; + } + + chr = os_memblock_get(&peer_chr_pool); + if (chr == NULL) + { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(chr, 0, sizeof *chr); + + chr->chr = *gatt_chr; + + if (prev == NULL) + { + SLIST_INSERT_HEAD(&svc->chrs, chr, next); + } + else + { + SLIST_NEXT(prev, next) = chr; + } + + return 0; +} + +static int peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error * error, const struct ble_gatt_chr * chr, void * arg) +{ + struct peer * peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + + switch (error->status) + { + case 0: + rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr); + break; + + case BLE_HS_EDONE: + /* All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ + if (peer->disc_prev_chr_val > 0) + { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) + { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +static void peer_disc_chrs(struct peer * peer) +{ + struct peer_svc * svc; + int rc; + + /* Search through the list of discovered service for the first service that + * contains undiscovered characteristics. Then, discover all + * characteristics belonging to that service. + */ + SLIST_FOREACH(svc, &peer->svcs, next) + { + if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) + { + peer->cur_svc = svc; + rc = ble_gattc_disc_all_chrs(peer->conn_handle, svc->svc.start_handle, svc->svc.end_handle, peer_chr_disced, peer); + if (rc != 0) + { + peer_disc_complete(peer, rc); + } + return; + } + } + + /* All characteristics discovered. */ + peer_disc_dscs(peer); +} + +int peer_svc_is_empty(const struct peer_svc * svc) +{ + return svc->svc.end_handle <= svc->svc.start_handle; +} + +static struct peer_svc * peer_svc_find_prev(struct peer * peer, uint16_t svc_start_handle) +{ + struct peer_svc * prev; + struct peer_svc * svc; + + prev = NULL; + SLIST_FOREACH(svc, &peer->svcs, next) + { + if (svc->svc.start_handle >= svc_start_handle) + { + break; + } + + prev = svc; + } + + return prev; +} + +static struct peer_svc * peer_svc_find(struct peer * peer, uint16_t svc_start_handle, struct peer_svc ** out_prev) +{ + struct peer_svc * prev; + struct peer_svc * svc; + + prev = peer_svc_find_prev(peer, svc_start_handle); + if (prev == NULL) + { + svc = SLIST_FIRST(&peer->svcs); + } + else + { + svc = SLIST_NEXT(prev, next); + } + + if (svc != NULL && svc->svc.start_handle != svc_start_handle) + { + svc = NULL; + } + + if (out_prev != NULL) + { + *out_prev = prev; + } + return svc; +} + +static struct peer_svc * peer_svc_find_range(struct peer * peer, uint16_t attr_handle) +{ + struct peer_svc * svc; + + SLIST_FOREACH(svc, &peer->svcs, next) + { + if (svc->svc.start_handle <= attr_handle && svc->svc.end_handle >= attr_handle) + { + + return svc; + } + } + + return NULL; +} + +const struct peer_svc * peer_svc_find_uuid(const struct peer * peer, const ble_uuid_t * uuid) +{ + const struct peer_svc * svc; + + SLIST_FOREACH(svc, &peer->svcs, next) + { + if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) + { + return svc; + } + } + + return NULL; +} + +const struct peer_chr * peer_chr_find_uuid(const struct peer * peer, const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid) +{ + const struct peer_svc * svc; + const struct peer_chr * chr; + + svc = peer_svc_find_uuid(peer, svc_uuid); + if (svc == NULL) + { + return NULL; + } + + SLIST_FOREACH(chr, &svc->chrs, next) + { + if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) + { + return chr; + } + } + + return NULL; +} + +const struct peer_dsc * peer_dsc_find_uuid(const struct peer * peer, const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid, + const ble_uuid_t * dsc_uuid) +{ + const struct peer_chr * chr; + const struct peer_dsc * dsc; + + chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); + if (chr == NULL) + { + return NULL; + } + + SLIST_FOREACH(dsc, &chr->dscs, next) + { + if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) + { + return dsc; + } + } + + return NULL; +} + +static int peer_svc_add(struct peer * peer, const struct ble_gatt_svc * gatt_svc) +{ + struct peer_svc * prev; + struct peer_svc * svc; + + svc = peer_svc_find(peer, gatt_svc->start_handle, &prev); + if (svc != NULL) + { + /* Service already discovered. */ + return 0; + } + + svc = os_memblock_get(&peer_svc_pool); + if (svc == NULL) + { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof *svc); + + svc->svc = *gatt_svc; + SLIST_INIT(&svc->chrs); + + if (prev == NULL) + { + SLIST_INSERT_HEAD(&peer->svcs, svc, next); + } + else + { + SLIST_INSERT_AFTER(prev, svc, next); + } + + return 0; +} + +static void peer_svc_delete(struct peer_svc * svc) +{ + struct peer_chr * chr; + + while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) + { + SLIST_REMOVE_HEAD(&svc->chrs, next); + peer_chr_delete(chr); + } + + os_memblock_put(&peer_svc_pool, svc); +} + +static int peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error * error, const struct ble_gatt_svc * service, + void * arg) +{ + struct peer * peer; + int rc; + + peer = arg; + assert(peer->conn_handle == conn_handle); + switch (error->status) + { + + case 0: + rc = peer_svc_add(peer, service); + break; + + case BLE_HS_EDONE: + /* All services discovered; start discovering characteristics. */ + if (peer->disc_prev_chr_val > 0) + { + peer_disc_chrs(peer); + } + rc = 0; + break; + + default: + rc = error->status; + break; + } + + if (rc != 0) + { + /* Error; abort discovery. */ + peer_disc_complete(peer, rc); + } + + return rc; +} + +int peer_disc_all(uint16_t conn_handle, peer_disc_fn * disc_cb, void * disc_cb_arg) +{ + struct peer_svc * svc; + struct peer * peer; + int rc; + peer = peer_find(conn_handle); + if (peer == NULL) + { + return BLE_HS_ENOTCONN; + } + + /* Undiscover everything first. */ + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) + { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + peer->disc_prev_chr_val = 1; + peer->disc_cb = disc_cb; + peer->disc_cb_arg = disc_cb_arg; + + rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer); + if (rc != 0) + { + return rc; + } + + return 0; +} + +int peer_delete(uint16_t conn_handle) +{ + struct peer_svc * svc; + struct peer * peer; + int rc; + + peer = peer_find(conn_handle); + if (peer == NULL) + { + return BLE_HS_ENOTCONN; + } + + SLIST_REMOVE(&peers, peer, peer, next); + + while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) + { + SLIST_REMOVE_HEAD(&peer->svcs, next); + peer_svc_delete(svc); + } + + rc = os_memblock_put(&peer_pool, peer); + if (rc != 0) + { + return BLE_HS_EOS; + } + + return 0; +} + +int peer_add(uint16_t conn_handle) +{ + struct peer * peer; + + /* Make sure the connection handle is unique. */ + peer = peer_find(conn_handle); + if (peer != NULL) + { + return BLE_HS_EALREADY; + } + + peer = os_memblock_get(&peer_pool); + if (peer == NULL) + { + /* Out of memory. */ + return BLE_HS_ENOMEM; + } + + memset(peer, 0, sizeof *peer); + peer->conn_handle = conn_handle; + + SLIST_INSERT_HEAD(&peers, peer, next); + + return 0; +} + +static void peer_free_mem(void) +{ + free(peer_mem); + peer_mem = NULL; + + free(peer_svc_mem); + peer_svc_mem = NULL; + + free(peer_chr_mem); + peer_chr_mem = NULL; + + free(peer_dsc_mem); + peer_dsc_mem = NULL; +} + +int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs) +{ + int rc; + + /* Free memory first in case this function gets called more than once. */ + peer_free_mem(); + + peer_mem = malloc(OS_MEMPOOL_BYTES(max_peers, sizeof(struct peer))); + if (peer_mem == NULL) + { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_pool, max_peers, sizeof(struct peer), peer_mem, "peer_pool"); + if (rc != 0) + { + rc = BLE_HS_EOS; + goto err; + } + + peer_svc_mem = malloc(OS_MEMPOOL_BYTES(max_svcs, sizeof(struct peer_svc))); + if (peer_svc_mem == NULL) + { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_svc_pool, max_svcs, sizeof(struct peer_svc), peer_svc_mem, "peer_svc_pool"); + if (rc != 0) + { + rc = BLE_HS_EOS; + goto err; + } + + peer_chr_mem = malloc(OS_MEMPOOL_BYTES(max_chrs, sizeof(struct peer_chr))); + if (peer_chr_mem == NULL) + { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_chr_pool, max_chrs, sizeof(struct peer_chr), peer_chr_mem, "peer_chr_pool"); + if (rc != 0) + { + rc = BLE_HS_EOS; + goto err; + } + + peer_dsc_mem = malloc(OS_MEMPOOL_BYTES(max_dscs, sizeof(struct peer_dsc))); + if (peer_dsc_mem == NULL) + { + rc = BLE_HS_ENOMEM; + goto err; + } + + rc = os_mempool_init(&peer_dsc_pool, max_dscs, sizeof(struct peer_dsc), peer_dsc_mem, "peer_dsc_pool"); + if (rc != 0) + { + rc = BLE_HS_EOS; + goto err; + } + + return 0; + +err: + peer_free_mem(); + return rc; +} +#endif