Esp32/ble controller (#23921)
* ESP32 as a controller
* Add bluedroid support for esp32/ble-commissioner
* Addressed review comments
diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt
index 0069628..e31baa2 100644
--- a/config/esp32/components/chip/CMakeLists.txt
+++ b/config/esp32/components/chip/CMakeLists.txt
@@ -143,6 +143,18 @@
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 c5b645b..2b2175f 100644
--- a/config/esp32/components/chip/Kconfig
+++ b/config/esp32/components/chip/Kconfig
@@ -886,6 +886,21 @@
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 9c3020f..71766ea 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 <lib/core/CHIPCallback.h>
#elif CONFIG_BT_NIMBLE_ENABLED
@@ -56,22 +59,85 @@
#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 <ble/BleLayer.h>
+#include <ble/BleUUID.h>
+#include <platform/ESP32/ChipDeviceScanner.h>
+#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 @@
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 @@
// ===== 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 @@
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 @@
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 @@
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 @@
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 29d3464..fce0f93 100644
--- a/src/platform/ESP32/BUILD.gn
+++ b/src/platform/ESP32/BUILD.gn
@@ -24,6 +24,9 @@
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 @@
static_library("ESP32") {
sources = [
"../SingletonConfigurationManager.cpp",
- "BLEManagerImpl.h",
"CHIPDevicePlatformConfig.h",
"CHIPDevicePlatformEvent.h",
"ConfigurationManagerImpl.cpp",
@@ -55,8 +57,6 @@
"PlatformManagerImpl.h",
"SystemTimeSupport.cpp",
"SystemTimeSupport.h",
- "bluedroid/BLEManagerImpl.cpp",
- "nimble/BLEManagerImpl.cpp",
]
deps = [
@@ -70,6 +70,7 @@
"${chip_root}/src/crypto",
"${chip_root}/src/platform:platform_base",
]
+
if (chip_enable_ota_requestor) {
sources += [
"OTAImageProcessorImpl.cpp",
@@ -77,6 +78,30 @@
]
}
+ 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 7f2e4a9..13360bf 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 c4e0df1..b7664ef 100644
--- a/src/platform/ESP32/CHIPDevicePlatformEvent.h
+++ b/src/platform/ESP32/CHIPDevicePlatformEvent.h
@@ -44,6 +44,21 @@
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 @@
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 0000000..90e866b
--- /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 <ble/CHIPBleServiceData.h>
+#include <lib/core/CHIPError.h>
+#include <system/SystemLayer.h>
+
+#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 <ble/CHIPBleServiceData.h>
+#include <lib/core/CHIPError.h>
+#include <system/SystemLayer.h>
+#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 1ffbe16..46fca5f 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 <lib/support/CodeUtils.h>
+#endif
#if CONFIG_BT_BLUEDROID_ENABLED
+#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER
+#include <ble/BleLayer.h>
+#endif
#include <ble/CHIPBleServiceData.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CommissionableDataProvider.h>
+#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER
+#include <platform/DeviceInstanceInfoProvider.h>
+#include <platform/ESP32/BLEManagerImpl.h>
+#include <platform/ESP32/ChipDeviceScanner.h>
+#endif
#include <platform/internal/BLEManager.h>
+#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER
+#include <setup_payload/AdditionalDataPayloadGenerator.h>
+#include <system/SystemTimer.h>
+#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 @@
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 @@
} // 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 @@
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 @@
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 @@
// 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 @@
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::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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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::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 @@
}
}
+#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<BleScanState>(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<intptr_t>(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 @@
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::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 @@
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 @@
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 @@
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 @@
err = PlatformMgr().PostEvent(&advChange);
}
}
+ }
+ 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 0000000..cea4b9f
--- /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 <platform/ESP32/ChipDeviceScanner.h>
+
+#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 <lib/support/CodeUtils.h>
+#include <lib/support/logging/CHIPLogging.h>
+
+#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 3f5318a..048509d 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 <ble/BleLayer.h>
+#endif
#include <ble/CHIPBleServiceData.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CommissionableDataProvider.h>
#include <platform/DeviceInstanceInfoProvider.h>
+#if CONFIG_ENABLE_ESP32_BLE_CONTROLLER
+#include <platform/ESP32/BLEManagerImpl.h>
+#endif
#include <platform/internal/BLEManager.h>
#include <setup_payload/AdditionalDataPayloadGenerator.h>
#include <system/SystemTimer.h>
@@ -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 {
+#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 @@
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 @@
} // 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 @@
},
};
+#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 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 @@
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<void *>(mCons), 0, sizeof(mCons));
mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled;
@@ -311,20 +392,164 @@
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 @@
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 @@
}
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 @@
return true;
}
-bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
- PacketBufferHandle pBuf)
+#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)
{
- ChipLogError(DeviceLayer, "BLEManagerImpl::SendWriteRequest() not supported");
+ 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,
+ chip::System::PacketBufferHandle pBuf)
+{
+#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 @@
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 @@
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 @@
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 @@
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 @@
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<BleScanState>(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<intptr_t>(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 0000000..5bf90ba
--- /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 <lib/support/CodeUtils.h>
+#include <lib/support/logging/CHIPLogging.h>
+#include <platform/ESP32/ChipDeviceScanner.h>
+
+#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 0000000..d61eb50
--- /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 <host/ble_gatt.h>
+
+#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 0000000..2c887dc
--- /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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * 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 0000000..0dbf544
--- /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 <assert.h>
+#include <string.h>
+
+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