From 0204f749097b7b166e999e35ef7ee399f652e119 Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk Date: Wed, 16 Aug 2023 08:24:22 +0200 Subject: [PATCH] applications: Enabled using persistent storage in Matter bridge. The change includes the following commits: 1. applications: Created application bridged devices creator module. The bridged devices adding and removing was handled in the shell module, however it may be useful to trigger such actions from a different parts of an application (e.g. app task). Extracted methods to add and remove bridged devices and handle Bluetooth LE connected callback to the dedicated bridged devices creator module. It will be possible to use its API in shell and in the different locations as well. 2. samples: matter: Refactored bridge manager API for adding devices Changed bridged manager API and adding devices algorithm to allow requesting usage of specific index and endpoint id. 3. applications: Enabled using persistent storage in Matter bridge Added storing bridged devices in the persistent storage after creation, removing them from the storage after removal and loading them from the storage after boot. 4. samples: matter: Added BLE connections recover after bridge reboot Implemented recovering Bluetooth LE connections to the stored bridged devices after bridge reboot. Additionally refactored structure of BLEConnectivityManager and BLEBridgedDeviceProvider by moving the BLEBridgedDevice objects ownership from manager to providers. Signed-off-by: Kamil Kasperczyk --- applications/matter_bridge/CMakeLists.txt | 1 + applications/matter_bridge/Kconfig | 2 +- applications/matter_bridge/src/app_task.cpp | 74 +++- applications/matter_bridge/src/app_task.h | 1 + .../matter_bridge/src/bridge_shell.cpp | 152 +------- .../ble_environmental_data_provider.cpp | 22 +- .../ble_environmental_data_provider.h | 11 - .../ble_onoff_light_data_provider.cpp | 28 +- .../ble_onoff_light_data_provider.h | 12 - .../src/bridged_devices_creator.cpp | 348 ++++++++++++++++++ .../src/bridged_devices_creator.h | 61 +++ .../common/src/bridge/ble_bridged_device.h | 43 ++- .../src/bridge/ble_connectivity_manager.cpp | 248 ++++++++----- .../src/bridge/ble_connectivity_manager.h | 121 +++++- .../common/src/bridge/bridge_manager.cpp | 273 ++++++++++---- .../matter/common/src/bridge/bridge_manager.h | 131 ++++++- 16 files changed, 1157 insertions(+), 371 deletions(-) create mode 100644 applications/matter_bridge/src/bridged_devices_creator.cpp create mode 100644 applications/matter_bridge/src/bridged_devices_creator.h diff --git a/applications/matter_bridge/CMakeLists.txt b/applications/matter_bridge/CMakeLists.txt index 566207979bc..87fc46efd74 100644 --- a/applications/matter_bridge/CMakeLists.txt +++ b/applications/matter_bridge/CMakeLists.txt @@ -43,6 +43,7 @@ target_sources(app PRIVATE src/app_task.cpp src/main.cpp src/bridge_shell.cpp + src/bridged_devices_creator.cpp ${COMMON_ROOT}/src/bridge/bridge_manager.cpp ${COMMON_ROOT}/src/bridge/matter_bridged_device.cpp ${COMMON_ROOT}/src/bridge/bridge_storage_manager.cpp diff --git a/applications/matter_bridge/Kconfig b/applications/matter_bridge/Kconfig index 2e21e7379d8..1d4b65c7264 100644 --- a/applications/matter_bridge/Kconfig +++ b/applications/matter_bridge/Kconfig @@ -46,7 +46,7 @@ config BT_SCAN # Configure how many Bluetooth LE devices the Matter bridge can support (the final number is given value - 1, as 1 connection is used for the Matter Bluetooth LE service). config BT_MAX_CONN - default 2 + default 3 config BT_SCAN_FILTER_ENABLE default y diff --git a/applications/matter_bridge/src/app_task.cpp b/applications/matter_bridge/src/app_task.cpp index be90416490b..e9995eee813 100644 --- a/applications/matter_bridge/src/app_task.cpp +++ b/applications/matter_bridge/src/app_task.cpp @@ -7,6 +7,8 @@ #include "app_task.h" #include "app_config.h" #include "bridge_manager.h" +#include "bridge_storage_manager.h" +#include "bridged_devices_creator.h" #include "led_util.h" #ifdef CONFIG_BRIDGED_DEVICE_BT @@ -154,13 +156,23 @@ CHIP_ERROR AppTask::Init() ConfigurationMgr().LogDeviceConfig(); PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); - /* Initialize bridge manager */ - BridgeManager::Instance().Init(); - #ifdef CONFIG_BRIDGED_DEVICE_BT - BLEConnectivityManager::Instance().Init(sUuidServices, kUuidServicesNumber); + /* Initialize BLE Connectivity Manager before the Bridge Manager, as it must be ready to recover devices loaded + * from persistent storaged during the bridge init. */ + err = BLEConnectivityManager::Instance().Init(sUuidServices, kUuidServicesNumber); + if (err != CHIP_NO_ERROR) { + LOG_ERR("BLEConnectivityManager initialization failed"); + return err; + } #endif + /* Initialize bridge manager */ + err = BridgeManager::Instance().Init(RestoreBridgedDevices); + if (err != CHIP_NO_ERROR) { + LOG_ERR("BridgeManager initialization failed"); + return err; + } + /* * Add CHIP event handler and start CHIP thread. * Note that all the initialization code should happen prior to this point to avoid data races @@ -360,3 +372,57 @@ void AppTask::DispatchEvent(const AppEvent &event) LOG_INF("Event received with no handler. Dropping event."); } } + +CHIP_ERROR AppTask::RestoreBridgedDevices() +{ + uint8_t count; + uint8_t indexes[BridgeManager::kMaxBridgedDevices] = { 0 }; + size_t indexesCount = 0; + + if (!BridgeStorageManager::Instance().LoadBridgedDevicesCount(count)) { + LOG_INF("No bridged devices to load from the storage."); + return CHIP_NO_ERROR; + } + + if (!BridgeStorageManager::Instance().LoadBridgedDevicesIndexes(indexes, BridgeManager::kMaxBridgedDevices, + indexesCount)) { + return CHIP_NO_ERROR; + } + + /* Load all devices based on the read count number. */ + for (size_t i = 0; i < indexesCount; i++) { + uint16_t endpointId; + char label[MatterBridgedDevice::kNodeLabelSize] = { 0 }; + size_t labelSize; + uint16_t deviceType; + + if (!BridgeStorageManager::Instance().LoadBridgedDeviceEndpointId(endpointId, indexes[i])) { + return CHIP_ERROR_NOT_FOUND; + } + + /* Ignore an error, as node label is optional, so it may not be found. */ + BridgeStorageManager::Instance().LoadBridgedDeviceNodeLabel(label, sizeof(label), labelSize, + indexes[i]); + + if (!BridgeStorageManager::Instance().LoadBridgedDeviceType(deviceType, indexes[i])) { + return CHIP_ERROR_NOT_FOUND; + } + + LOG_INF("Loaded bridged device on endpoint id %d from the storage", endpointId); + +#ifdef CONFIG_BRIDGED_DEVICE_BT + bt_addr_le_t addr; + + if (!BridgeStorageManager::Instance().LoadBtAddress(addr, indexes[i])) { + return CHIP_ERROR_NOT_FOUND; + } + + BridgedDeviceCreator::CreateDevice(deviceType, label, addr, chip::Optional(indexes[i]), + chip::Optional(endpointId)); +#else + BridgedDeviceCreator::CreateDevice(deviceType, label, chip::Optional(indexes[i]), + chip::Optional(endpointId)); +#endif + } + return CHIP_NO_ERROR; +} diff --git a/applications/matter_bridge/src/app_task.h b/applications/matter_bridge/src/app_task.h index 1454aa50062..ec799f800b2 100644 --- a/applications/matter_bridge/src/app_task.h +++ b/applications/matter_bridge/src/app_task.h @@ -48,6 +48,7 @@ class AppTask { static void LEDStateUpdateHandler(LEDWidget &ledWidget); static void FunctionTimerTimeoutCallback(k_timer *timer); static void UpdateStatusLED(); + static CHIP_ERROR RestoreBridgedDevices(); FunctionEvent mFunction = FunctionEvent::NoneSelected; bool mFunctionTimerActive = false; diff --git a/applications/matter_bridge/src/bridge_shell.cpp b/applications/matter_bridge/src/bridge_shell.cpp index e73abe9454c..760e333a29c 100644 --- a/applications/matter_bridge/src/bridge_shell.cpp +++ b/applications/matter_bridge/src/bridge_shell.cpp @@ -5,116 +5,19 @@ */ #include "bridge_manager.h" -#include "bridged_device_factory.h" - -#include -#include +#include "bridged_devices_creator.h" #ifdef CONFIG_BRIDGED_DEVICE_BT -#include "ble_bridged_device.h" #include "ble_connectivity_manager.h" #endif /* CONFIG_BRIDGED_DEVICE_BT */ -LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); - -#ifdef CONFIG_BRIDGED_DEVICE_SIMULATED -static BridgedDeviceDataProvider *CreateSimulatedProvider(int deviceType) -{ - return BridgeFactory::GetSimulatedDataProviderFactory().Create( - static_cast(deviceType), BridgeManager::HandleUpdate); -} -#endif /* CONFIG_BRIDGED_DEVICE_SIMULATED */ - -#ifdef CONFIG_BRIDGED_DEVICE_BT - -static BridgedDeviceDataProvider *CreateBleProvider(int deviceType) -{ - return BridgeFactory::GetBleDataProviderFactory().Create( - static_cast(deviceType), BridgeManager::HandleUpdate); -} - -struct BluetoothConnectionContext { - const struct shell *shell; - int deviceType; - char nodeLabel[MatterBridgedDevice::kNodeLabelSize]; - BLEBridgedDeviceProvider *provider; -}; -#endif /* CONFIG_BRIDGED_DEVICE_BT */ - -static void AddDevice(const struct shell *shell, int deviceType, const char *nodeLabel, - BridgedDeviceDataProvider *provider) -{ - VerifyOrReturn(provider != nullptr, shell_fprintf(shell, SHELL_INFO, "No valid data provider!\n")); - - CHIP_ERROR err; - if (deviceType == MatterBridgedDevice::DeviceType::EnvironmentalSensor) { - /* Handle special case for environmental sensor */ - auto *temperatureBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( - MatterBridgedDevice::DeviceType::TemperatureSensor, nodeLabel); - - VerifyOrReturn(temperatureBridgedDevice != nullptr, delete provider, - shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n")); - - auto *humidityBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( - MatterBridgedDevice::DeviceType::HumiditySensor, nodeLabel); - - VerifyOrReturn(humidityBridgedDevice != nullptr, delete provider, delete temperatureBridgedDevice, - shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n")); - - MatterBridgedDevice *newBridgedDevices[] = { temperatureBridgedDevice, humidityBridgedDevice }; - err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider, - ARRAY_SIZE(newBridgedDevices)); - - } else { - auto *newBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( - static_cast(deviceType), nodeLabel); - - MatterBridgedDevice *newBridgedDevices[] = { newBridgedDevice }; - VerifyOrReturn(newBridgedDevice != nullptr, delete provider, - shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n")); - err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider, - ARRAY_SIZE(newBridgedDevices)); - } - - if (err == CHIP_NO_ERROR) { - shell_fprintf(shell, SHELL_INFO, "Done\n"); - } else if (err == CHIP_ERROR_INVALID_STRING_LENGTH) { - shell_fprintf(shell, SHELL_ERROR, "Error: too long node label (max %d)\n", - MatterBridgedDevice::kNodeLabelSize); - } else if (err == CHIP_ERROR_NO_MEMORY) { - shell_fprintf(shell, SHELL_ERROR, "Error: no memory\n"); - } else if (err == CHIP_ERROR_INVALID_ARGUMENT) { - shell_fprintf(shell, SHELL_ERROR, "Error: invalid device type\n"); - } else { - shell_fprintf(shell, SHELL_ERROR, "Error: internal\n"); - } -} - -#ifdef CONFIG_BRIDGED_DEVICE_BT -static void BluetoothDeviceConnected(BLEBridgedDevice *device, bt_gatt_dm *discoveredData, bool discoverySucceeded, - void *context) -{ - if (context && device && discoveredData && discoverySucceeded) { - BluetoothConnectionContext *ctx = reinterpret_cast(context); - chip::Platform::UniquePtr ctxPtr(ctx); - - if (ctxPtr->provider->MatchBleDevice(device)) { - return; - } - - if (ctxPtr->provider->ParseDiscoveredData(discoveredData)) { - return; - } - - AddDevice(ctx->shell, ctx->deviceType, ctx->nodeLabel, ctx->provider); - } -} -#endif /* CONFIG_BRIDGED_DEVICE_BT */ +#include static int AddBridgedDeviceHandler(const struct shell *shell, size_t argc, char **argv) { int deviceType = strtoul(argv[1], NULL, 0); char *nodeLabel = nullptr; + CHIP_ERROR result = CHIP_NO_ERROR; #if defined(CONFIG_BRIDGED_DEVICE_BT) int bleDeviceIndex = strtoul(argv[2], NULL, 0); @@ -123,36 +26,14 @@ static int AddBridgedDeviceHandler(const struct shell *shell, size_t argc, char nodeLabel = argv[3]; } - /* The device object can be created once the Bluetooth LE connection will be established. */ - BluetoothConnectionContext *context = chip::Platform::New(); - - if (!context) { - return -ENOMEM; - } - - chip::Platform::UniquePtr contextPtr(context); - contextPtr->shell = shell; - contextPtr->deviceType = deviceType; - - if (nodeLabel) { - strcpy(contextPtr->nodeLabel, nodeLabel); - } - - BridgedDeviceDataProvider *provider = CreateBleProvider(contextPtr->deviceType); - - if (!provider) { - shell_fprintf(shell, SHELL_INFO, "Cannot allocate data provider of given type\n"); - return -ENOMEM; + bt_addr_le_t address; + if (BLEConnectivityManager::Instance().GetScannedDeviceAddress(&address, bleDeviceIndex) != CHIP_NO_ERROR) { + shell_fprintf(shell, SHELL_ERROR, "Invalid Bluetooth LE device index.\n"); + } else { + shell_fprintf(shell, SHELL_ERROR, "Found device address\n"); } - contextPtr->provider = static_cast(provider); - - CHIP_ERROR err = BLEConnectivityManager::Instance().Connect( - bleDeviceIndex, BluetoothDeviceConnected, contextPtr.get(), contextPtr->provider->GetServiceUuid()); - - if (err == CHIP_NO_ERROR) { - contextPtr.release(); - } + result = BridgedDeviceCreator::CreateDevice(deviceType, nodeLabel, address); #elif defined(CONFIG_BRIDGED_DEVICE_SIMULATED) @@ -160,14 +41,19 @@ static int AddBridgedDeviceHandler(const struct shell *shell, size_t argc, char nodeLabel = argv[2]; } - /* The device is simulated, so it can be added immediately. */ - BridgedDeviceDataProvider *provider = CreateSimulatedProvider(deviceType); - AddDevice(shell, deviceType, nodeLabel, provider); + result = BridgedDeviceCreator::CreateDevice(deviceType, nodeLabel); + #else return -ENOTSUP; #endif /* CONFIG_BRIDGED_DEVICE_BT */ + if (result == CHIP_NO_ERROR) { + shell_fprintf(shell, SHELL_INFO, "Done\n"); + } else { + shell_fprintf(shell, SHELL_ERROR, "Device add failed\n"); + } + return 0; } @@ -175,10 +61,10 @@ static int RemoveBridgedDeviceHandler(const struct shell *shell, size_t argc, ch { int endpointId = strtoul(argv[1], NULL, 0); - if (CHIP_NO_ERROR == BridgeManager::Instance().RemoveBridgedDevice(endpointId)) { + if (CHIP_NO_ERROR == BridgedDeviceCreator::RemoveDevice(endpointId)) { shell_fprintf(shell, SHELL_INFO, "Done\n"); } else { - shell_fprintf(shell, SHELL_ERROR, "Error: device not found\n"); + shell_fprintf(shell, SHELL_ERROR, "Error: device remove failed\n"); } return 0; } diff --git a/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.cpp b/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.cpp index cb6faf1b23f..545fafa9ae4 100644 --- a/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.cpp +++ b/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.cpp @@ -26,9 +26,8 @@ bt_gatt_read_params BleEnvironmentalDataProvider::sHumidityReadParams{}; BleEnvironmentalDataProvider *GetProvider(bt_conn *conn) { - BLEBridgedDevice *device = BLEConnectivityManager::Instance().FindBLEBridgedDevice(conn); - VerifyOrReturnValue(device, nullptr); - return reinterpret_cast(device->mProvider); + return static_cast( + BLEConnectivityManager::Instance().FindBLEProvider(*bt_conn_get_dst(conn))); } bt_uuid *BleEnvironmentalDataProvider::GetServiceUuid() @@ -40,6 +39,7 @@ uint8_t BleEnvironmentalDataProvider::GattTemperatureNotifyCallback(bt_conn *con const void *data, uint16_t length) { BleEnvironmentalDataProvider *provider = GetProvider(conn); + VerifyOrExit(provider, ); VerifyOrExit(data, ); VerifyOrExit(length == sizeof(mTemperatureValue), ); @@ -148,10 +148,10 @@ CHIP_ERROR BleEnvironmentalDataProvider::ParseHumidityCharacteristic(bt_gatt_dm void BleEnvironmentalDataProvider::Subscribe() { - VerifyOrReturn(mDevice && mDevice->mConn, LOG_ERR("Invalid connection object")); + VerifyOrReturn(mDevice.mConn, LOG_ERR("Invalid connection object")); if (CheckSubscriptionParameters(&mGattTemperatureSubscribeParams)) { - int err = bt_gatt_subscribe(mDevice->mConn, &mGattTemperatureSubscribeParams); + int err = bt_gatt_subscribe(mDevice.mConn, &mGattTemperatureSubscribeParams); if (err) { LOG_ERR("Subscribe to temperature characteristic failed with error %d", err); } @@ -160,7 +160,7 @@ void BleEnvironmentalDataProvider::Subscribe() } if (CheckSubscriptionParameters(&mGattHumiditySubscribeParams)) { - int err = bt_gatt_subscribe(mDevice->mConn, &mGattHumiditySubscribeParams); + int err = bt_gatt_subscribe(mDevice.mConn, &mGattHumiditySubscribeParams); if (err) { LOG_ERR("Subscribe to humidity characteristic failed with error %d", err); } @@ -178,14 +178,14 @@ void BleEnvironmentalDataProvider::Subscribe() void BleEnvironmentalDataProvider::Unsubscribe() { - VerifyOrReturn(mDevice && mDevice->mConn, LOG_ERR("Invalid connection object")); + VerifyOrReturn(mDevice.mConn, LOG_ERR("Invalid connection object")); - int err = bt_gatt_unsubscribe(mDevice->mConn, &mGattTemperatureSubscribeParams); + int err = bt_gatt_unsubscribe(mDevice.mConn, &mGattTemperatureSubscribeParams); if (err) { LOG_INF("Cannot unsubscribe from temperature characteristic (error %d)", err); } - err = bt_gatt_unsubscribe(mDevice->mConn, &mGattHumiditySubscribeParams); + err = bt_gatt_unsubscribe(mDevice.mConn, &mGattHumiditySubscribeParams); if (err) { LOG_INF("Cannot unsubscribe from humidity characteristic (error %d)", err); } @@ -233,7 +233,7 @@ void BleEnvironmentalDataProvider::HumidityTimerTimeoutCallback(k_timer *timer) BleEnvironmentalDataProvider *provider = reinterpret_cast(timer->user_data); VerifyOrReturn(provider, LOG_ERR("Invalid provider object")); - VerifyOrReturn(provider->mDevice && provider->mDevice->mConn, LOG_ERR("Invalid connection object")); + VerifyOrReturn(provider->mDevice.mConn, LOG_ERR("Invalid connection object")); DeviceLayer::PlatformMgr().ScheduleWork(ReadGATTHumidity, reinterpret_cast(provider)); } @@ -242,7 +242,7 @@ void BleEnvironmentalDataProvider::ReadGATTHumidity(intptr_t context) { BleEnvironmentalDataProvider *provider = reinterpret_cast(context); - int err = bt_gatt_read(provider->mDevice->mConn, &sHumidityReadParams); + int err = bt_gatt_read(provider->mDevice.mConn, &sHumidityReadParams); if (err) { LOG_INF("GATT read failed (err %d)", err); } diff --git a/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.h b/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.h index ce4854846ee..2d16400edf5 100644 --- a/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.h +++ b/applications/matter_bridge/src/bridged_device_types/ble_environmental_data_provider.h @@ -22,17 +22,6 @@ class BleEnvironmentalDataProvider : public BLEBridgedDeviceProvider { size_t dataSize) override; CHIP_ERROR UpdateState(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer) override; bt_uuid *GetServiceUuid() override; - int MatchBleDevice(BLEBridgedDevice *device) override - { - if (!device) { - return -EINVAL; - } - - mDevice = device; - mDevice->mProvider = this; - - return 0; - } int ParseDiscoveredData(bt_gatt_dm *discoveredData) override; private: diff --git a/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.cpp b/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.cpp index a51e9268896..8f3da53c672 100644 --- a/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.cpp +++ b/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.cpp @@ -25,14 +25,11 @@ static bt_uuid *sUuidCcc = BT_UUID_GATT_CCC; uint8_t BleOnOffLightDataProvider::GattNotifyCallback(bt_conn *conn, bt_gatt_subscribe_params *params, const void *data, uint16_t length) { - BLEBridgedDevice *dev = BLEConnectivityManager::Instance().FindBLEBridgedDevice(conn); - BleOnOffLightDataProvider *provider; + BleOnOffLightDataProvider *provider = static_cast( + BLEConnectivityManager::Instance().FindBLEProvider(*bt_conn_get_dst(conn))); - VerifyOrExit(dev, ); VerifyOrExit(data, ); VerifyOrExit(length == sizeof(mOnOff), ); - - provider = reinterpret_cast(dev->mProvider); VerifyOrExit(provider, ); /* Save data received in notification. */ @@ -66,9 +63,7 @@ void BleOnOffLightDataProvider::NotifyUpdateState(chip::ClusterId clusterId, chi void BleOnOffLightDataProvider::GattWriteCallback(bt_conn *conn, uint8_t err, bt_gatt_write_params *params) { - BLEBridgedDevice *dev = BLEConnectivityManager::Instance().FindBLEBridgedDevice(conn); - - if (!dev || !params) { + if (!params) { return; } @@ -76,12 +71,13 @@ void BleOnOffLightDataProvider::GattWriteCallback(bt_conn *conn, uint8_t err, bt return; } - if (!dev->mProvider) { + BleOnOffLightDataProvider *provider = static_cast( + BLEConnectivityManager::Instance().FindBLEProvider(*bt_conn_get_dst(conn))); + + if (!provider) { return; } - BleOnOffLightDataProvider *provider = reinterpret_cast(dev->mProvider); - /* Save data received in GATT write response. */ memcpy(&provider->mOnOff, params->data, params->length); @@ -91,14 +87,14 @@ void BleOnOffLightDataProvider::GattWriteCallback(bt_conn *conn, uint8_t err, bt CHIP_ERROR BleOnOffLightDataProvider::UpdateState(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t *buffer) { - if (!mDevice) { - return CHIP_ERROR_INCORRECT_STATE; - } - if (clusterId != Clusters::OnOff::Id) { return CHIP_ERROR_INVALID_ARGUMENT; } + if (!mDevice.mConn) { + return CHIP_ERROR_INCORRECT_STATE; + } + LOG_INF("Updating state of the BleOnOffLightDataProvider, cluster ID: %u, attribute ID: %u.", clusterId, attributeId); @@ -112,7 +108,7 @@ CHIP_ERROR BleOnOffLightDataProvider::UpdateState(chip::ClusterId clusterId, chi mGattWriteParams.handle = mLedCharacteristicHandle; mGattWriteParams.func = BleOnOffLightDataProvider::GattWriteCallback; - int err = bt_gatt_write(mDevice->mConn, &mGattWriteParams); + int err = bt_gatt_write(mDevice.mConn, &mGattWriteParams); if (err) { LOG_ERR("GATT write operation failed"); return CHIP_ERROR_INTERNAL; diff --git a/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.h b/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.h index b58da862387..aa5c97724f2 100644 --- a/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.h +++ b/applications/matter_bridge/src/bridged_device_types/ble_onoff_light_data_provider.h @@ -15,7 +15,6 @@ class BleOnOffLightDataProvider : public BLEBridgedDeviceProvider { public: explicit BleOnOffLightDataProvider(UpdateAttributeCallback callback) : BLEBridgedDeviceProvider(callback) {} - ~BleOnOffLightDataProvider() = default; void Init() override; void NotifyUpdateState(chip::ClusterId clusterId, chip::AttributeId attributeId, void *data, @@ -28,17 +27,6 @@ class BleOnOffLightDataProvider : public BLEBridgedDeviceProvider { uint16_t length); bt_uuid *GetServiceUuid() override; - int MatchBleDevice(BLEBridgedDevice *device) override - { - if (!device) { - return -EINVAL; - } - - mDevice = device; - mDevice->mProvider = this; - - return 0; - } int ParseDiscoveredData(bt_gatt_dm *discoveredData) override; private: diff --git a/applications/matter_bridge/src/bridged_devices_creator.cpp b/applications/matter_bridge/src/bridged_devices_creator.cpp new file mode 100644 index 00000000000..d95427f538f --- /dev/null +++ b/applications/matter_bridge/src/bridged_devices_creator.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "bridged_devices_creator.h" +#include "bridge_manager.h" +#include "bridge_storage_manager.h" +#include "bridged_device_factory.h" + +#include + +LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); + +namespace +{ +CHIP_ERROR StoreDevice(MatterBridgedDevice *device, BridgedDeviceDataProvider *provider, uint8_t index) +{ + uint16_t endpointId; + uint8_t count = 0; + uint8_t indexes[BridgeManager::kMaxBridgedDevices] = { 0 }; + bool deviceRefresh = false; + + /* Check if a device is already present in the storage. */ + if (BridgeStorageManager::Instance().LoadBridgedDeviceEndpointId(endpointId, index)) { + deviceRefresh = true; + } + + if (!BridgeStorageManager::Instance().StoreBridgedDevice(device, index)) { + LOG_ERR("Failed to store bridged device"); + return CHIP_ERROR_INTERNAL; + } + +/* Store additional information that are not present for every generic MatterBridgedDevice. */ +#ifdef CONFIG_BRIDGED_DEVICE_BT + BLEBridgedDeviceProvider *bleProvider = static_cast(provider); + + bt_addr_le_t addr = bleProvider->GetBtAddress(); + + if (!BridgeStorageManager::Instance().StoreBtAddress(addr, index)) { + LOG_ERR("Failed to store bridged device's Bluetooth address"); + return CHIP_ERROR_INTERNAL; + } +#endif + + /* If a device was not present in the storage before, put new index on the end of list and increment the count + * number of stored devices. */ + if (!deviceRefresh) { + CHIP_ERROR err = BridgeManager::Instance().GetDevicesIndexes(indexes, sizeof(indexes), count); + if (CHIP_NO_ERROR != err) { + LOG_ERR("Failed to get bridged devices indexes"); + return err; + } + + if (!BridgeStorageManager::Instance().StoreBridgedDevicesIndexes(indexes, count)) { + LOG_ERR("Failed to store bridged devices indexes."); + return CHIP_ERROR_INTERNAL; + } + + if (!BridgeStorageManager::Instance().StoreBridgedDevicesCount(count)) { + LOG_ERR("Failed to store bridged devices count."); + return CHIP_ERROR_INTERNAL; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AddMatterDevice(int deviceType, const char *nodeLabel, BridgedDeviceDataProvider *provider, + chip::Optional index, chip::Optional endpointId) +{ + VerifyOrReturnError(provider != nullptr, CHIP_ERROR_INVALID_ARGUMENT, LOG_ERR("No valid data provider!")); + + CHIP_ERROR err; + + if (deviceType == MatterBridgedDevice::DeviceType::EnvironmentalSensor) { + /* Handle special case for environmental sensor */ + auto *temperatureBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( + MatterBridgedDevice::DeviceType::TemperatureSensor, nodeLabel); + + if (!temperatureBridgedDevice) { + LOG_ERR("Cannot allocate Matter device of given type"); + return CHIP_ERROR_NO_MEMORY; + } + + auto *humidityBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( + MatterBridgedDevice::DeviceType::HumiditySensor, nodeLabel); + + if (!humidityBridgedDevice) { + chip::Platform::Delete(temperatureBridgedDevice); + LOG_ERR("Cannot allocate Matter device of given type"); + return CHIP_ERROR_NO_MEMORY; + } + + MatterBridgedDevice *newBridgedDevices[] = { temperatureBridgedDevice, humidityBridgedDevice }; + uint8_t deviceIndexes[] = { 0, 0 }; + err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider, + ARRAY_SIZE(newBridgedDevices), deviceIndexes); + + if (err == CHIP_NO_ERROR) { + for (uint8_t i = 0; i < ARRAY_SIZE(newBridgedDevices); i++) { + err = StoreDevice(newBridgedDevices[i], provider, deviceIndexes[i]); + if (err != CHIP_NO_ERROR) { + break; + } + } + } + } else { + auto *newBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( + static_cast(deviceType), nodeLabel); + + MatterBridgedDevice *newBridgedDevices[] = { newBridgedDevice }; + + if (!newBridgedDevice) { + LOG_ERR("Cannot allocate Matter device of given type"); + return CHIP_ERROR_NO_MEMORY; + } + + uint8_t deviceIndex[] = { 0 }; + uint16_t endpointIds[] = { 0 }; + + /* The current implementation assumes that index and endpoint id values are given only in case of + * recovering a device. In such scenario the devices (endpoints) are recovered one by one, so it's not + * an option for environmental sensor. */ + if (index.HasValue() && endpointId.HasValue()) { + endpointIds[0] = endpointId.Value(); + deviceIndex[0] = index.Value(); + err = BridgeManager::Instance().AddBridgedDevices( + newBridgedDevices, provider, ARRAY_SIZE(newBridgedDevices), deviceIndex, endpointIds); + } else { + err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider, + ARRAY_SIZE(newBridgedDevices), deviceIndex); + } + + if (err == CHIP_NO_ERROR) { + err = StoreDevice(newBridgedDevice, provider, deviceIndex[0]); + } + } + + if (err != CHIP_NO_ERROR) { + LOG_ERR("Adding Matter bridged device failed: %s", ErrorStr(err)); + } + return err; +} + +#ifdef CONFIG_BRIDGED_DEVICE_SIMULATED +BridgedDeviceDataProvider *CreateSimulatedProvider(int deviceType) +{ + return BridgeFactory::GetSimulatedDataProviderFactory().Create( + static_cast(deviceType), BridgeManager::HandleUpdate); +} +#endif /* CONFIG_BRIDGED_DEVICE_SIMULATED */ + +#ifdef CONFIG_BRIDGED_DEVICE_BT + +struct BluetoothConnectionContext { + int deviceType; + char nodeLabel[MatterBridgedDevice::kNodeLabelSize] = { 0 }; + BLEBridgedDeviceProvider *provider; + bt_addr_le_t address; + chip::Optional index; + chip::Optional endpointId; +}; + +void BluetoothDeviceConnected(bt_gatt_dm *discoveredData, bool discoverySucceeded, void *context) +{ + if (!context) { + return; + } + + BluetoothConnectionContext *ctx = reinterpret_cast(context); + + VerifyOrExit(discoveredData && discoverySucceeded, ); + VerifyOrExit(ctx->provider->ParseDiscoveredData(discoveredData) == 0, ); + + /* Schedule adding device to main thread to avoid overflowing the BT stack. */ + VerifyOrExit(chip::DeviceLayer::PlatformMgr().ScheduleWork( + [](intptr_t context) { + BluetoothConnectionContext *ctx = + reinterpret_cast(context); + if (CHIP_NO_ERROR != AddMatterDevice(ctx->deviceType, ctx->nodeLabel, + ctx->provider, ctx->index, ctx->endpointId)) { + BLEConnectivityManager::Instance().RemoveBLEProvider(ctx->address); + chip::Platform::Delete(ctx->provider); + } + chip::Platform::Delete(ctx); + }, + reinterpret_cast(ctx)) == CHIP_NO_ERROR, ); + return; + +exit: + BLEConnectivityManager::Instance().RemoveBLEProvider(ctx->address); + chip::Platform::Delete(ctx); +} + +BridgedDeviceDataProvider *CreateBleProvider(int deviceType) +{ + return BridgeFactory::GetBleDataProviderFactory().Create( + static_cast(deviceType), BridgeManager::HandleUpdate); +} +#endif /* CONFIG_BRIDGED_DEVICE_BT */ +} /* namespace */ + +#ifdef CONFIG_BRIDGED_DEVICE_BT +CHIP_ERROR BridgedDeviceCreator::CreateDevice(int deviceType, const char *nodeLabel, bt_addr_le_t btAddress, + chip::Optional index, chip::Optional endpointId) +{ + CHIP_ERROR err; + + /* Check if there is already existing provider for given address. + * In case it is, the provider was either connected or recovered before. All what needs to be done is creating + * new Matter endpoint. In case there isn't, the provider has to be created. + */ + BLEBridgedDeviceProvider *provider = BLEConnectivityManager::Instance().FindBLEProvider(btAddress); + if (provider) { + return AddMatterDevice(deviceType, nodeLabel, provider, index, endpointId); + } + + /* The EnvironmentalSensor is a dummy device type, so it is not saved in the persistent storage. For now the + * only BLE service handling temperature and humidity device types is environmental sensor, so it's necessary to + * perform mapping. */ + if (deviceType == MatterBridgedDevice::DeviceType::TemperatureSensor || + deviceType == MatterBridgedDevice::DeviceType::HumiditySensor) { + provider = static_cast( + CreateBleProvider(MatterBridgedDevice::DeviceType::EnvironmentalSensor)); + } else { + provider = static_cast(CreateBleProvider(deviceType)); + } + + if (!provider) { + return CHIP_ERROR_NO_MEMORY; + } + + err = BLEConnectivityManager::Instance().AddBLEProvider(provider); + VerifyOrExit(err == CHIP_NO_ERROR, ); + + /* There are two cases for creating a new device: + * - connecting new device for the first time - the index and endpoint id are not relevant, the new Bluetooth LE + * connection has to be created and the operation confirmed by connected callback. + * - recovering a device that was connected in the past after reboot - the index and endpoint id are read from + * storage, the device has to be re-connected without confirming its existence by callback. + */ + if (index.HasValue() && endpointId.HasValue()) { + provider->InitializeBridgedDevice(btAddress, nullptr, nullptr); + BLEConnectivityManager::Instance().Recover(provider); + err = AddMatterDevice(deviceType, nodeLabel, provider, index, endpointId); + } else { + /* The device object can be created once the Bluetooth LE connection will be established. */ + BluetoothConnectionContext *context = chip::Platform::New(); + + VerifyOrExit(context, err = CHIP_ERROR_NO_MEMORY); + + chip::Platform::UniquePtr contextPtr(context); + contextPtr->deviceType = deviceType; + + if (nodeLabel) { + strcpy(contextPtr->nodeLabel, nodeLabel); + } + + contextPtr->provider = provider; + contextPtr->index = index; + contextPtr->endpointId = endpointId; + contextPtr->address = btAddress; + + provider->InitializeBridgedDevice(btAddress, BluetoothDeviceConnected, contextPtr.get()); + err = BLEConnectivityManager::Instance().Connect(provider); + + if (err == CHIP_NO_ERROR) { + contextPtr.release(); + } + } + +exit: + + if (err != CHIP_NO_ERROR) { + BLEConnectivityManager::Instance().RemoveBLEProvider(btAddress); + chip::Platform::Delete(provider); + } + + return err; +} +#else +CHIP_ERROR BridgedDeviceCreator::CreateDevice(int deviceType, const char *nodeLabel, chip::Optional index, + chip::Optional endpointId) +{ +#if defined(CONFIG_BRIDGED_DEVICE_SIMULATED) + BridgedDeviceDataProvider *provider = CreateSimulatedProvider(deviceType); + /* The device is simulated, so it can be added immediately. */ + return AddMatterDevice(deviceType, nodeLabel, provider, index, endpointId); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif /* CONFIG_BRIDGED_DEVICE_SIMULATED */ +} +#endif /* CONFIG_BRIDGED_DEVICE_BT */ + +CHIP_ERROR BridgedDeviceCreator::RemoveDevice(int endpointId) +{ + uint8_t index; + uint8_t count = 0; + uint8_t indexes[BridgeManager::kMaxBridgedDevices] = { 0 }; + + CHIP_ERROR err = BridgeManager::Instance().RemoveBridgedDevice(endpointId, index); + + if (CHIP_NO_ERROR != err) { + LOG_ERR("Failed to remove bridged device"); + return err; + } + + err = BridgeManager::Instance().GetDevicesIndexes(indexes, sizeof(indexes), count); + if (CHIP_NO_ERROR != err) { + LOG_ERR("Failed to get bridged devices indexes"); + return err; + } + + /* Update the current indexes list. */ + if (!BridgeStorageManager::Instance().StoreBridgedDevicesIndexes(indexes, count)) { + LOG_ERR("Failed to store bridged devices indexes."); + return CHIP_ERROR_INTERNAL; + } + + if (!BridgeStorageManager::Instance().StoreBridgedDevicesCount(count)) { + LOG_ERR("Failed to store bridged devices count."); + return CHIP_ERROR_INTERNAL; + } + + if (!BridgeStorageManager::Instance().RemoveBridgedDeviceEndpointId(index)) { + LOG_ERR("Failed to remove bridged device endpoint id."); + return CHIP_ERROR_INTERNAL; + } + + /* Ignore error, as node label may not be present in the storage. */ + BridgeStorageManager::Instance().RemoveBridgedDeviceNodeLabel(index); + + if (!BridgeStorageManager::Instance().RemoveBridgedDeviceType(index)) { + LOG_ERR("Failed to remove bridged device type."); + return CHIP_ERROR_INTERNAL; + } + +#ifdef CONFIG_BRIDGED_DEVICE_BT + if (!BridgeStorageManager::Instance().RemoveBtAddress(index)) { + LOG_ERR("Failed to remove bridged device Bluetooth address."); + return CHIP_ERROR_INTERNAL; + } +#endif + + return CHIP_NO_ERROR; +} diff --git a/applications/matter_bridge/src/bridged_devices_creator.h b/applications/matter_bridge/src/bridged_devices_creator.h new file mode 100644 index 00000000000..e380d9987dd --- /dev/null +++ b/applications/matter_bridge/src/bridged_devices_creator.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#pragma once + +#include +#include + +#include + +namespace BridgedDeviceCreator +{ +#ifdef CONFIG_BRIDGED_DEVICE_BT +/** + * @brief Create a bridged device. + * + * @param deviceType the Matter device type of a bridged device to be created + * @param nodeLabel node label of a Matter device to be created + * @param btAddress the Bluetooth LE address of a device to be bridged with created Matter device + * @param index optional index object that shall have a valid value set if the value is meant + * to be used to index assignment, or shall not have a value set if the default index assignment should be used. + * @param endpointId optional endpoint id object that shall have a valid value set if the value is meant + * to be used to endpoint id assignment, or shall not have a value set if the default endpoint id assignment should be + * used. + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ +CHIP_ERROR CreateDevice(int deviceType, const char *nodeLabel, bt_addr_le_t btAddress, + chip::Optional index = chip::Optional(), + chip::Optional endpointId = chip::Optional()); +#else +/** + * @brief Create a bridged device. + * + * @param deviceType the Matter device type of a bridged device to be created + * @param nodeLabel node label of a Matter device to be created + * @param index optional index object that shall have a valid value set if the value is meant + * to be used to index assignment, or shall not have a value set if the default index assignment should be used. + * @param endpointId optional endpoint id object that shall have a valid value set if the value is meant + * to be used to endpoint id assignment, or shall not have a value set if the default endpoint id assignment should be + * used. + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ +CHIP_ERROR CreateDevice(int deviceType, const char *nodeLabel, + chip::Optional index = chip::Optional(), + chip::Optional endpointId = chip::Optional()); +#endif + +/** + * @brief Remove bridged device. + * + * @param endpointId value of endpoint id specifying the bridged device to be removed + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ +CHIP_ERROR RemoveDevice(int endpointId); +} /* namespace BridgedDeviceCreator */ diff --git a/samples/matter/common/src/bridge/ble_bridged_device.h b/samples/matter/common/src/bridge/ble_bridged_device.h index d02543f8a37..1354b74bd5c 100644 --- a/samples/matter/common/src/bridge/ble_bridged_device.h +++ b/samples/matter/common/src/bridge/ble_bridged_device.h @@ -6,6 +6,7 @@ #pragma once +#include "ble_connectivity_manager.h" #include "bridged_device_data_provider.h" #include @@ -13,29 +14,41 @@ #include #include -struct BLEBridgedDevice; +struct BLEBridgedDeviceProvider; + +struct BLEBridgedDevice { + bt_addr_le_t mAddr; + BLEConnectivityManager::DeviceConnectedCallback mFirstConnectionCallback; + void *mFirstConnectionCallbackContext; + bt_conn *mConn; + BLEBridgedDeviceProvider *mProvider; +}; class BLEBridgedDeviceProvider : public BridgedDeviceDataProvider { public: explicit BLEBridgedDeviceProvider(UpdateAttributeCallback callback) : BridgedDeviceDataProvider(callback) {} - virtual ~BLEBridgedDeviceProvider() = default; + ~BLEBridgedDeviceProvider() + { + BLEConnectivityManager::Instance().RemoveBLEProvider(GetBtAddress()); + } virtual bt_uuid *GetServiceUuid() = 0; - virtual int MatchBleDevice(BLEBridgedDevice *device) = 0; virtual int ParseDiscoveredData(bt_gatt_dm *discoveredData) = 0; -protected: - BLEBridgedDevice *mDevice{ nullptr }; -}; + void InitializeBridgedDevice(bt_addr_le_t address, BLEConnectivityManager::DeviceConnectedCallback callback, + void *context) + { + mDevice.mAddr = address; + mDevice.mFirstConnectionCallback = callback; + mDevice.mFirstConnectionCallbackContext = context; + mDevice.mProvider = this; + } -struct BLEBridgedDevice { - using DeviceConnectedCallback = void (*)(BLEBridgedDevice *device, bt_gatt_dm *discoveredData, - bool discoverySucceeded, void *context); + BLEBridgedDevice &GetBLEBridgedDevice() { return mDevice; } + void SetConnectionObject(bt_conn *conn) { mDevice.mConn = conn; } - bt_addr_le_t mAddr; - DeviceConnectedCallback mFirstConnectionCallback; - void *mFirstConnectionCallbackContext; - bt_uuid *mServiceUuid; - bt_conn *mConn; - BLEBridgedDeviceProvider *mProvider; + bt_addr_le_t GetBtAddress() { return mDevice.mAddr; } + +protected: + BLEBridgedDevice mDevice = { 0 }; }; diff --git a/samples/matter/common/src/bridge/ble_connectivity_manager.cpp b/samples/matter/common/src/bridge/ble_connectivity_manager.cpp index 3a1bb95fb3e..66e61890686 100644 --- a/samples/matter/common/src/bridge/ble_connectivity_manager.cpp +++ b/samples/matter/common/src/bridge/ble_connectivity_manager.cpp @@ -5,6 +5,7 @@ */ #include "ble_connectivity_manager.h" +#include "ble_bridged_device.h" #include #include @@ -59,37 +60,42 @@ void BLEConnectivityManager::FilterMatch(bt_scan_device_info *device_info, bt_sc void BLEConnectivityManager::ConnectionHandler(bt_conn *conn, uint8_t conn_err) { - const bt_addr_le_t *addr = bt_conn_get_dst(conn); + const bt_addr_le_t *dstAddr = bt_conn_get_dst(conn); char str_addr[BT_ADDR_LE_STR_LEN]; int err; - BLEBridgedDevice *device = nullptr; + BLEBridgedDeviceProvider *provider = nullptr; - bt_addr_le_to_str(addr, str_addr, sizeof(str_addr)); + bt_addr_le_to_str(dstAddr, str_addr, sizeof(str_addr)); /* Find the created device instance based on address. */ - for (int i = 0; i < Instance().mCreatedDevicesCounter; i++) { - if (memcmp(&Instance().mCreatedDevices[i].mAddr, addr, sizeof(Instance().mCreatedDevices[i].mAddr)) == - 0) { - device = &Instance().mCreatedDevices[i]; + for (int i = 0; i < kMaxConnectedDevices; i++) { + if (Instance().mConnectedProviders[i] == nullptr) { + continue; + } + + bt_addr_le_t addr = Instance().mConnectedProviders[i]->GetBtAddress(); + if (memcmp(&addr, dstAddr, sizeof(addr)) == 0) { + provider = Instance().mConnectedProviders[i]; break; } } - if (!device) { + /* The device with given address was not found. */ + if (!provider) { return; } /* TODO: Add security validation. */ if (conn_err && Instance().mRecovery.mRecoveryInProgress) { - Instance().mRecovery.PutDevice(device); + Instance().mRecovery.PutProvider(provider); return; } LOG_INF("Connected: %s", str_addr); /* Start GATT discovery for the device's service UUID. */ - err = bt_gatt_dm_start(conn, device->mServiceUuid, &discovery_cb, device); + err = bt_gatt_dm_start(conn, provider->GetServiceUuid(), &discovery_cb, provider); if (err) { LOG_ERR("Could not start the discovery procedure, error " "code: %d", @@ -99,54 +105,48 @@ void BLEConnectivityManager::ConnectionHandler(bt_conn *conn, uint8_t conn_err) void BLEConnectivityManager::DisconnectionHandler(bt_conn *conn, uint8_t reason) { - /* Verify whether the device should be recovered */ - BLEBridgedDevice *device = Instance().FindBLEBridgedDevice(conn); + char addr[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); - if (device) { - char addr[BT_ADDR_LE_STR_LEN]; - bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + LOG_INF("Disconnected: %s (reason %u)", addr, reason); - LOG_INF("Disconnected: %s (reason %u)", addr, reason); + /* Try to find provider matching the connection */ + BLEBridgedDeviceProvider *provider = Instance().FindBLEProvider(*bt_conn_get_dst(conn)); - if (device->mConn) { - bt_conn_unref(device->mConn); - device->mConn = nullptr; - } + bt_conn_unref(conn); + if (provider) { + /* Verify whether the device should be recovered. */ if (reason == BT_HCI_ERR_CONN_TIMEOUT) { - Instance().mRecovery.NotifyLostDevice(device); - if (device->mProvider) { - VerifyOrReturn(CHIP_NO_ERROR == device->mProvider->NotifyReachableStatusChange(false), - LOG_WRN("The device has not been notified about the status change.")); - } + Instance().mRecovery.NotifyProviderToRecover(provider); + VerifyOrReturn(CHIP_NO_ERROR == provider->NotifyReachableStatusChange(false), + LOG_WRN("The device has not been notified about the status change.")); } - - /* TODO Add removing a device when disconnection has been invoked by the user */ } } void BLEConnectivityManager::DiscoveryCompletedHandler(bt_gatt_dm *dm, void *context) { LOG_INF("The GATT discovery completed"); - BLEBridgedDevice *device = reinterpret_cast(context); + BLEBridgedDeviceProvider *provider = reinterpret_cast(context); bool discoveryResult = false; const bt_gatt_dm_attr *gatt_service_attr = bt_gatt_dm_service_get(dm); const bt_gatt_service_val *gatt_service = bt_gatt_dm_attr_service_val(gatt_service_attr); - VerifyOrExit(device, ); + VerifyOrExit(provider, ); bt_gatt_dm_data_print(dm); - VerifyOrExit(bt_uuid_cmp(gatt_service->uuid, device->mServiceUuid) == 0, ); - device->mConn = bt_gatt_dm_conn_get(dm); + VerifyOrExit(bt_uuid_cmp(gatt_service->uuid, provider->GetServiceUuid()) == 0, ); discoveryResult = true; exit: if (!Instance().mRecovery.mRecoveryInProgress) { - device->mFirstConnectionCallback(device, dm, discoveryResult, device->mFirstConnectionCallbackContext); + provider->GetBLEBridgedDevice().mFirstConnectionCallback( + dm, discoveryResult, provider->GetBLEBridgedDevice().mFirstConnectionCallbackContext); } - if (device->mProvider) { - VerifyOrReturn(CHIP_NO_ERROR == device->mProvider->NotifyReachableStatusChange(true), + if (provider) { + VerifyOrReturn(CHIP_NO_ERROR == provider->NotifyReachableStatusChange(true), LOG_WRN("The device has not been notified about the status change.")); } @@ -163,18 +163,20 @@ void BLEConnectivityManager::DiscoveryNotFound(bt_conn *conn, void *context) { LOG_ERR("GATT service could not be found during the discovery"); - BLEBridgedDevice *device = reinterpret_cast(context); + BLEBridgedDeviceProvider *provider = reinterpret_cast(context); - device->mFirstConnectionCallback(device, nullptr, false, device->mFirstConnectionCallbackContext); + provider->GetBLEBridgedDevice().mFirstConnectionCallback( + nullptr, false, provider->GetBLEBridgedDevice().mFirstConnectionCallbackContext); } void BLEConnectivityManager::DiscoveryError(bt_conn *conn, int err, void *context) { LOG_ERR("The GATT discovery procedure failed with %d", err); - BLEBridgedDevice *device = reinterpret_cast(context); + BLEBridgedDeviceProvider *provider = reinterpret_cast(context); - device->mFirstConnectionCallback(device, nullptr, false, device->mFirstConnectionCallbackContext); + provider->GetBLEBridgedDevice().mFirstConnectionCallback( + nullptr, false, provider->GetBLEBridgedDevice().mFirstConnectionCallbackContext); } CHIP_ERROR BLEConnectivityManager::Init(bt_uuid **serviceUuids, uint8_t serviceUuidsCount) @@ -249,24 +251,30 @@ void BLEConnectivityManager::ReScanCallback(ScannedDevice *devices, uint8_t coun LOG_DBG("Found devices no. %d", Instance().mScannedDevicesCounter); if (Instance().mScannedDevicesCounter != 0 && !Instance().mRecovery.mRecoveryInProgress) { - BLEBridgedDevice *deviceLost = Instance().mRecovery.GetDevice(); + BLEBridgedDeviceProvider *deviceLost = Instance().mRecovery.GetProvider(); if (deviceLost) { for (uint8_t idx = 0; idx < Instance().mScannedDevicesCounter; idx++) { auto &deviceScanned = Instance().mScannedDevices[idx]; - if (memcmp(&deviceScanned.mAddr, &deviceLost->mAddr, sizeof(deviceLost->mAddr)) == 0) { + bt_addr_le_t address = deviceLost->GetBtAddress(); + + if (memcmp(&deviceScanned.mAddr, &address, sizeof(address)) == 0) { LOG_DBG("Found the lost device"); Instance().mRecovery.mIndexToRecover = idx; - Instance().mRecovery.mCurrentDevice = deviceLost; + Instance().mRecovery.mCurrentProvider = deviceLost; Instance().mRecovery.mRecoveryInProgress = true; DeviceLayer::PlatformMgr().ScheduleWork( - [](intptr_t context) { Instance().Reconnect(); }, 0); + [](intptr_t context) { + Instance().Reconnect( + reinterpret_cast(context)); + }, + reinterpret_cast(deviceLost)); break; } } if (!Instance().mRecovery.mRecoveryInProgress) { - Instance().mRecovery.NotifyLostDevice(deviceLost); + Instance().mRecovery.NotifyProviderToRecover(deviceLost); } } } else if (Instance().mRecovery.IsNeeded()) { @@ -303,8 +311,12 @@ void BLEConnectivityManager::ScanTimeoutHandle(intptr_t context) Instance().mScanDoneCallbackContext); } -CHIP_ERROR BLEConnectivityManager::Reconnect() +CHIP_ERROR BLEConnectivityManager::Reconnect(BLEBridgedDeviceProvider *provider) { + if (!provider) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + StopScan(); bt_conn *conn; @@ -315,81 +327,149 @@ CHIP_ERROR BLEConnectivityManager::Reconnect() if (err) { LOG_ERR("Creating reconnection failed (err %d)", err); return System::MapErrorZephyr(err); + } else { + provider->SetConnectionObject(conn); } return CHIP_NO_ERROR; } -CHIP_ERROR BLEConnectivityManager::Connect(uint8_t index, BLEBridgedDevice::DeviceConnectedCallback callback, - void *context, bt_uuid *serviceUuid) +CHIP_ERROR BLEConnectivityManager::Connect(BLEBridgedDeviceProvider *provider) { - /* Check if device selected to connect is present on the scanned devices list. */ - if (mScannedDevicesCounter <= index) { - return CHIP_ERROR_INVALID_ARGUMENT; - } - - /* Verify whether there is a free space for a new device. */ - if (mCreatedDevicesCounter >= kMaxCreatedDevices) { - return CHIP_ERROR_NO_MEMORY; - } - - if (!callback || !serviceUuid) { + if (!provider) { return CHIP_ERROR_INVALID_ARGUMENT; } StopScan(); bt_conn *conn; + bt_le_conn_param *connParams = GetScannedDeviceConnParams(provider->GetBLEBridgedDevice().mAddr); - int err = bt_conn_le_create(&mScannedDevices[index].mAddr, create_param, &mScannedDevices[index].mConnParam, - &conn); + if (!connParams) { + LOG_ERR("Failed to get conn params"); + return CHIP_ERROR_INTERNAL; + } + + int err = bt_conn_le_create(&provider->GetBLEBridgedDevice().mAddr, create_param, connParams, &conn); if (err) { LOG_ERR("Creating connection failed (err %d)", err); return System::MapErrorZephyr(err); + } else { + provider->SetConnectionObject(conn); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BLEConnectivityManager::AddBLEProvider(BLEBridgedDeviceProvider *provider) +{ + if (!provider) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (mConnectedProvidersCounter >= kMaxConnectedDevices) { + return CHIP_ERROR_NO_MEMORY; } - mCreatedDevices[mCreatedDevicesCounter].mAddr = mScannedDevices[index].mAddr; - mCreatedDevices[mCreatedDevicesCounter].mFirstConnectionCallback = callback; - mCreatedDevices[mCreatedDevicesCounter].mFirstConnectionCallbackContext = context; - mCreatedDevices[mCreatedDevicesCounter].mServiceUuid = serviceUuid; - mCreatedDevicesCounter++; + /* Find first free slot to store new providers' address. */ + for (auto i = 0; i < kMaxConnectedDevices; i++) { + if (mConnectedProviders[i] == nullptr) { + mConnectedProviders[i] = provider; + mConnectedProvidersCounter++; + return CHIP_NO_ERROR; + } + } + + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR BLEConnectivityManager::RemoveBLEProvider(bt_addr_le_t address) +{ + BLEBridgedDeviceProvider *provider = nullptr; + + /* Find provider's address on the list and remove it. */ + for (auto i = 0; i < kMaxConnectedDevices; i++) { + if (mConnectedProviders[i] == nullptr) { + continue; + } + + bt_addr_le_t addr = mConnectedProviders[i]->GetBtAddress(); + if (memcmp(&addr, &address, sizeof(address)) == 0) { + provider = mConnectedProviders[i]; + mConnectedProviders[i] = nullptr; + mConnectedProvidersCounter--; + break; + } + } + + if (!provider) { + return CHIP_ERROR_NOT_FOUND; + } + + if (!provider->GetBLEBridgedDevice().mConn) { + return CHIP_ERROR_INTERNAL; + } + + /* Release the connection immediately in case of disconnection failed, as callback will not be called. */ + if (bt_conn_disconnect(provider->GetBLEBridgedDevice().mConn, BT_HCI_ERR_REMOTE_USER_TERM_CONN) != 0) { + bt_conn_unref(provider->GetBLEBridgedDevice().mConn); + } return CHIP_NO_ERROR; } -BLEBridgedDevice *BLEConnectivityManager::FindBLEBridgedDevice(bt_conn *conn) +BLEBridgedDeviceProvider *BLEConnectivityManager::FindBLEProvider(bt_addr_le_t address) { - bt_conn_info info_in, info_local; + /* Find BLE provider that matches given address. */ + for (int i = 0; i < kMaxConnectedDevices; i++) { + if (!mConnectedProviders[i]) { + continue; + } - VerifyOrExit(conn, ); - VerifyOrExit(bt_conn_get_info(conn, &info_in) == 0, ); + bt_addr_le_t addr = mConnectedProviders[i]->GetBtAddress(); - /* Find BLE device that matches given connection id. */ - for (int i = 0; i < mCreatedDevicesCounter; i++) { - if (bt_conn_get_info(mCreatedDevices[i].mConn, &info_local) == 0) { - if (info_local.id == info_in.id) { - return &mCreatedDevices[i]; - } + if (memcmp(&addr, &address, sizeof(addr)) == 0) { + return mConnectedProviders[i]; } } -exit: + return nullptr; +} + +CHIP_ERROR BLEConnectivityManager::GetScannedDeviceAddress(bt_addr_le_t *address, uint8_t index) +{ + if (address == nullptr || index >= mScannedDevicesCounter) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + memcpy(address, &mScannedDevices[index].mAddr, sizeof(mScannedDevices[index].mAddr)); + + return CHIP_NO_ERROR; +} + +bt_le_conn_param *BLEConnectivityManager::GetScannedDeviceConnParams(bt_addr_le_t address) +{ + for (auto i = 0; i < mScannedDevicesCounter; i++) { + if (memcmp(&mScannedDevices[i].mAddr, &address, sizeof(address)) == 0) { + return &mScannedDevices[i].mConnParam; + } + } return nullptr; } BLEConnectivityManager::Recovery::Recovery() { - ring_buf_init(&mRingBuf, sizeof(mDevicesToRecover), reinterpret_cast(mDevicesToRecover)); + ring_buf_init(&mRingBuf, sizeof(mProvidersToRecover), reinterpret_cast(mProvidersToRecover)); k_timer_init(&mRecoveryTimer, TimerTimeoutCallback, nullptr); k_timer_user_data_set(&mRecoveryTimer, this); } -void BLEConnectivityManager::Recovery::NotifyLostDevice(BLEBridgedDevice *device) +void BLEConnectivityManager::Recovery::NotifyProviderToRecover(BLEBridgedDeviceProvider *provider) { - if (device) { - PutDevice(device); + if (provider) { + PutProvider(provider); StartTimer(); } } @@ -401,19 +481,19 @@ void BLEConnectivityManager::Recovery::TimerTimeoutCallback(k_timer *timer) [](intptr_t) { Instance().Scan(ReScanCallback, nullptr, kRecoveryScanTimeoutMs); }, 0); } -BLEBridgedDevice *BLEConnectivityManager::Recovery::GetDevice() +BLEBridgedDeviceProvider *BLEConnectivityManager::Recovery::GetProvider() { uint32_t deviceAddr; int ret = ring_buf_get(&mRingBuf, reinterpret_cast(&deviceAddr), sizeof(deviceAddr)); if (ret == sizeof(deviceAddr)) { - return reinterpret_cast(deviceAddr); + return reinterpret_cast(deviceAddr); } return nullptr; } -bool BLEConnectivityManager::Recovery::PutDevice(BLEBridgedDevice *device) +bool BLEConnectivityManager::Recovery::PutProvider(BLEBridgedDeviceProvider *provider) { - int ret = ring_buf_put(&mRingBuf, reinterpret_cast(&device), sizeof(device)); - return ret == sizeof(device); + int ret = ring_buf_put(&mRingBuf, reinterpret_cast(&provider), sizeof(provider)); + return ret == sizeof(provider); } diff --git a/samples/matter/common/src/bridge/ble_connectivity_manager.h b/samples/matter/common/src/bridge/ble_connectivity_manager.h index 4cd47a05e20..8b4aedf4f41 100644 --- a/samples/matter/common/src/bridge/ble_connectivity_manager.h +++ b/samples/matter/common/src/bridge/ble_connectivity_manager.h @@ -6,9 +6,9 @@ #pragma once -#include "ble_bridged_device.h" #include "bridged_device_data_provider.h" +#include #include #include #include @@ -16,12 +16,16 @@ #include #include +/* Forward declarations. */ +struct BLEBridgedDevice; +struct BLEBridgedDeviceProvider; + class BLEConnectivityManager { public: static constexpr uint16_t kScanTimeoutMs = 10000; static constexpr uint16_t kMaxScannedDevices = 16; /* One BT connection is reserved for the Matter service purposes. */ - static constexpr uint16_t kMaxCreatedDevices = CONFIG_BT_MAX_CONN - 1; + static constexpr uint16_t kMaxConnectedDevices = CONFIG_BT_MAX_CONN - 1; static constexpr uint8_t kMaxServiceUuids = CONFIG_BT_SCAN_UUID_CNT; private: @@ -33,20 +37,20 @@ class BLEConnectivityManager { public: Recovery(); ~Recovery() { CancelTimer(); } - void NotifyLostDevice(BLEBridgedDevice *device); + void NotifyProviderToRecover(BLEBridgedDeviceProvider *provider); private: - BLEBridgedDevice *GetDevice(); - bool PutDevice(BLEBridgedDevice *device); + BLEBridgedDeviceProvider *GetProvider(); + bool PutProvider(BLEBridgedDeviceProvider *provider); bool IsNeeded() { return !ring_buf_is_empty(&mRingBuf); } void StartTimer() { k_timer_start(&mRecoveryTimer, K_MSEC(kRecoveryIntervalMs), K_NO_WAIT); } void CancelTimer() { k_timer_stop(&mRecoveryTimer); } - size_t GetCurrentAmount() { return ring_buf_size_get(&mRingBuf) / sizeof(BLEBridgedDevice *); } + size_t GetCurrentAmount() { return ring_buf_size_get(&mRingBuf) / sizeof(BLEBridgedDeviceProvider *); } static void TimerTimeoutCallback(k_timer *timer); - BLEBridgedDevice *mDevicesToRecover[BLEConnectivityManager::kMaxCreatedDevices]; - BLEBridgedDevice *mCurrentDevice = nullptr; + BLEBridgedDeviceProvider *mProvidersToRecover[BLEConnectivityManager::kMaxConnectedDevices]; + BLEBridgedDeviceProvider *mCurrentProvider = nullptr; bool mRecoveryInProgress = false; ring_buf mRingBuf; uint8_t mIndexToRecover; @@ -54,6 +58,8 @@ class BLEConnectivityManager { }; public: + using DeviceConnectedCallback = void (*)(bt_gatt_dm *discoveredData, bool discoverySucceeded, void *context); + struct ScannedDevice { bt_addr_le_t mAddr; bt_le_conn_param mConnParam; @@ -61,13 +67,98 @@ class BLEConnectivityManager { using ScanDoneCallback = void (*)(ScannedDevice *devices, uint8_t count, void *context); + /** + * @brief Initialize BLEConnectivityManager instance. + * + * @param serviceUuids the address of array containing all Bluetooth service UUIDs to be handled by manager + * @param serviceUuidsCount the number of services on the serviceUuids list + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ CHIP_ERROR Init(bt_uuid **serviceUuids, uint8_t serviceUuidsCount); + + /** + * @brief Start scanning for Bluetooth LE peripheral devices advertising service UUIDs passed in @ref Init + * method. + * + * @param callback callback with results to be called once scan is done + * @param context context that will be passed as an argument once calling @ref callback + * @param scanTimeoutMs optional timeout on scan operation in ms (by default 10 s) + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ CHIP_ERROR Scan(ScanDoneCallback callback, void *context, uint32_t scanTimeoutMs = kScanTimeoutMs); + + /** + * @brief Stop scanning operation. + * + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ CHIP_ERROR StopScan(); - CHIP_ERROR Connect(uint8_t index, BLEBridgedDevice::DeviceConnectedCallback callback, void *context, - bt_uuid *serviceUuid); - CHIP_ERROR Reconnect(); - BLEBridgedDevice *FindBLEBridgedDevice(bt_conn *conn); + + /** + * @brief Create connection to the Bluetooth LE device, managed by BLEBridgedDeviceProvider object. It is + * necessary to first add the provider to the manager's list using @ref AddBLEProvider method. + * + * @param provider address of a valid provider object + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR Connect(BLEBridgedDeviceProvider *provider); + + /** + * @brief Create connection to the first Bluetooth LE device on the @ref mProvidersToRecover recovery list. + * + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR Reconnect(BLEBridgedDeviceProvider *provider); + + /** + * @brief Get BLE provider that uses the specified connection object. + * + * @param address Bluetooth LE address used by the provider. + * @return address of provider on success + * @return nullptr on failure + */ + BLEBridgedDeviceProvider *FindBLEProvider(bt_addr_le_t address); + + /** + * @brief Add the BLE provider's address to the manager's list. + * + * @param provider address of a valid provider object to be added + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR AddBLEProvider(BLEBridgedDeviceProvider *provider); + + /** + * @brief Remove the BLE provider from the manager's list. + * + * @param address Bluetooth LE address used by the provider. + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR RemoveBLEProvider(bt_addr_le_t address); + + /** + * @brief Gets Bluetooth LE address of a device that was scanned before. + * + * @param address Bluetooth LE address to store the obtained address + * @param index index of Bluetooth LE device on the scanned devices list + * @return CHIP_NO_ERROR on success + * @return other error code if device is not present or argument is invalid + */ + CHIP_ERROR GetScannedDeviceAddress(bt_addr_le_t *address, uint8_t index); + + /** + * @brief Recover connection with the specified BLE provider. It is + * necessary to first add the provider to the manager's list using @ref AddBLEProvider method. + * + * @param provider address of a valid provider object to be recovered. + */ + void Recover(BLEBridgedDeviceProvider *provider) { mRecovery.NotifyProviderToRecover(provider); } static void FilterMatch(bt_scan_device_info *device_info, bt_scan_filter_match *filter_match, bool connectable); static void ScanTimeoutCallback(k_timer *timer); @@ -86,12 +177,14 @@ class BLEConnectivityManager { static void ReScanCallback(ScannedDevice *devices, uint8_t count, void *context); private: + bt_le_conn_param *GetScannedDeviceConnParams(bt_addr_le_t address); + bool mScanActive; k_timer mScanTimer; uint8_t mScannedDevicesCounter; - uint8_t mCreatedDevicesCounter; + uint8_t mConnectedProvidersCounter; ScannedDevice mScannedDevices[kMaxScannedDevices]; - BLEBridgedDevice mCreatedDevices[kMaxCreatedDevices]; + BLEBridgedDeviceProvider *mConnectedProviders[kMaxConnectedDevices]; bt_uuid *mServicesUuid[kMaxServiceUuids]; uint8_t mServicesUuidCount; ScanDoneCallback mScanDoneCallback; diff --git a/samples/matter/common/src/bridge/bridge_manager.cpp b/samples/matter/common/src/bridge/bridge_manager.cpp index 31624f59edb..74ad9585de0 100644 --- a/samples/matter/common/src/bridge/bridge_manager.cpp +++ b/samples/matter/common/src/bridge/bridge_manager.cpp @@ -18,8 +18,17 @@ LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); using namespace ::chip; using namespace ::chip::app; -void BridgeManager::Init() +CHIP_ERROR BridgeManager::Init(LoadStoredBridgedDevicesCallback loadStoredBridgedDevicesCb) { + if (!loadStoredBridgedDevicesCb) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (mIsInitialized) { + LOG_INF("BridgeManager is already initialized."); + return CHIP_ERROR_INCORRECT_STATE; + } + /* The first dynamic endpoint is the last fixed endpoint + 1. */ mFirstDynamicEndpointId = static_cast( static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); @@ -29,50 +38,65 @@ void BridgeManager::Init() /* Disable the placeholder endpoint */ emberAfEndpointEnableDisable(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1)), false); + + /* Invoke the callback to load stored devices in a proper moment. */ + CHIP_ERROR err = loadStoredBridgedDevicesCb(); + + if (err == CHIP_NO_ERROR) { + mIsInitialized = true; + } + + return err; } CHIP_ERROR BridgeManager::AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, - uint8_t deviceListSize) + uint8_t deviceListSize, uint8_t devicesPairIndexes[]) { - VerifyOrReturnError(devices && dataProvider, CHIP_ERROR_INTERNAL); + chip::Optional indexes[deviceListSize]; + uint16_t endpoints[deviceListSize]; - dataProvider->Init(); + VerifyOrReturnError(devicesPairIndexes, CHIP_ERROR_INTERNAL); - /* Wrap input data into unique_ptr to avoid memory leakage in case any error occurs. */ - Platform::UniquePtr devicesPtr[deviceListSize]; - for (auto i = 0; i < deviceListSize; ++i) { - devicesPtr[i] = std::move(Platform::UniquePtr(devices[i])); + return AddBridgedDevices(devices, dataProvider, deviceListSize, devicesPairIndexes, endpoints, indexes); +} + +CHIP_ERROR BridgeManager::AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, uint8_t devicesPairIndexes[], + uint16_t endpointIds[]) +{ + chip::Optional indexes[deviceListSize]; + + VerifyOrReturnError(devicesPairIndexes, CHIP_ERROR_INTERNAL); + + for (auto i = 0; i < deviceListSize; i++) { + indexes[i].SetValue(devicesPairIndexes[i]); } - Platform::UniquePtr dataProviderPtr(dataProvider); - /* Maximum number of Matter bridged devices is controlled inside mDevicesMap, - but the data providers may be created independently, so let's ensure we do not - violate the maximum number of supported instances. */ - VerifyOrReturnError(mDevicesMap.FreeSlots() >= deviceListSize, CHIP_ERROR_NO_MEMORY, - LOG_ERR("Not enough free slots in the map")); - VerifyOrReturnError(mNumberOfProviders + 1 <= kMaxDataProviders, CHIP_ERROR_NO_MEMORY, - LOG_ERR("Maximum number of providers exceeded")); - mNumberOfProviders++; + return AddBridgedDevices(devices, dataProvider, deviceListSize, devicesPairIndexes, endpointIds, indexes); +} - bool cumulativeStatus{ true }; - bool status{ true }; - for (auto i = 0; i < deviceListSize; ++i) { - MatterBridgedDevice *device = devicesPtr[i].get(); - status = AddSingleDevice(device, dataProviderPtr.get()); - if (status) { - devicesPtr[i].release(); - } - cumulativeStatus &= status; +CHIP_ERROR BridgeManager::AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, uint8_t devicesPairIndexes[], + uint16_t endpointIds[], chip::Optional indexes[]) +{ + CHIP_ERROR err = AddDevices(devices, dataProvider, deviceListSize, indexes, endpointIds); + + if (err != CHIP_NO_ERROR) { + return err; } - if (cumulativeStatus) { - dataProviderPtr.release(); - return CHIP_NO_ERROR; + for (auto i = 0; i < deviceListSize; i++) { + if (!indexes[i].HasValue()) { + return CHIP_ERROR_INTERNAL; + } + + devicesPairIndexes[i] = indexes[i].Value(); } - return CHIP_ERROR_NO_MEMORY; + + return CHIP_NO_ERROR; } -CHIP_ERROR BridgeManager::RemoveBridgedDevice(uint16_t endpoint) +CHIP_ERROR BridgeManager::RemoveBridgedDevice(uint16_t endpoint, uint8_t &devicesPairIndex) { uint8_t index = 0; while (index < kMaxBridgedDevices) { @@ -82,6 +106,7 @@ CHIP_ERROR BridgeManager::RemoveBridgedDevice(uint16_t endpoint) LOG_INF("Removed dynamic endpoint %d (index=%d)", endpoint, index); /* Free dynamically allocated memory */ emberAfClearDynamicEndpoint(index); + devicesPairIndex = index; return SafelyRemoveDevice(index); } } @@ -119,6 +144,26 @@ CHIP_ERROR BridgeManager::SafelyRemoveDevice(uint8_t index) if (removeProvider) { mNumberOfProviders--; } + + /* Find the required index on the list, remove it and move all following indexes one position earlier. + */ + bool indexFound = false; + for (size_t i = 0; i < mDevicesIndexesCounter; i++) { + if (mDevicesIndexes[i] == index) { + indexFound = true; + } + + if (indexFound && ((i + 1) < BridgeManager::kMaxBridgedDevices)) { + mDevicesIndexes[i] = mDevicesIndexes[i + 1]; + } + } + + if (indexFound) { + mDevicesIndexesCounter--; + } else { + return CHIP_ERROR_NOT_FOUND; + } + return CHIP_NO_ERROR; } else { LOG_ERR("Cannot remove bridged devices under index=%d", index); @@ -126,48 +171,146 @@ CHIP_ERROR BridgeManager::SafelyRemoveDevice(uint8_t index) } } -bool BridgeManager::AddSingleDevice(MatterBridgedDevice *device, BridgedDeviceDataProvider *dataProvider) +CHIP_ERROR BridgeManager::AddSingleDevice(MatterBridgedDevice *device, BridgedDeviceDataProvider *dataProvider, + chip::Optional &devicesPairIndex, uint16_t endpointId) { uint8_t index{ 0 }; - while (index < kMaxBridgedDevices) { - /* Find the first empty index in the bridged devices list */ - if (!mDevicesMap.Contains(index)) { - if (mDevicesMap.Insert(index, BridgedDevicePair(device, dataProvider))) { - while (true) { - auto *storedDevice = mDevicesMap[index].mDevice; - /* Allocate endpoints for provided devices */ - EmberAfStatus ret = emberAfSetDynamicEndpoint( - index, mCurrentDynamicEndpointId, storedDevice->mEp, - Span(storedDevice->mDataVersion, - storedDevice->mDataVersionSize), - Span(storedDevice->mDeviceTypeList, - storedDevice->mDeviceTypeListSize)); - - if (ret == EMBER_ZCL_STATUS_SUCCESS) { - LOG_INF("Added device to dynamic endpoint %d (index=%d)", - mCurrentDynamicEndpointId, index); - storedDevice->Init(mCurrentDynamicEndpointId); - return true; - } else if (ret != EMBER_ZCL_STATUS_DUPLICATE_EXISTS) { - LOG_ERR("Failed to add dynamic endpoint: Internal error!"); - if (CHIP_NO_ERROR != SafelyRemoveDevice(index)) { - LOG_ERR("Cannot remove device from the map!"); + CHIP_ERROR err; + + /* The adding algorithm differs depending on the devicesPairIndex value: + * - If devicesPairIndex has value it means that index and endpoint id are specified and should be assigned + * based on the input arguments (e.g. the device was loaded from storage and has to use specific data). + * - If devicesPairIndex has no value it means the default monotonically increasing numbering should be used. + */ + if (devicesPairIndex.HasValue()) { + index = devicesPairIndex.Value(); + /* The requested index is already used. */ + if (mDevicesMap.Contains(index)) { + return CHIP_ERROR_INTERNAL; + } + + if (!mDevicesMap.Insert(index, BridgedDevicePair(device, dataProvider))) { + return CHIP_ERROR_INTERNAL; + } + + err = CreateEndpoint(index, endpointId); + + if (err == CHIP_NO_ERROR) { + devicesPairIndex.SetValue(index); + mDevicesIndexes[mDevicesIndexesCounter] = index; + mDevicesIndexesCounter++; + + /* Make sure that the following endpoint id assignments will be monotonically continued from the + * biggest assigned number. */ + mCurrentDynamicEndpointId = + mCurrentDynamicEndpointId > endpointId ? mCurrentDynamicEndpointId : endpointId + 1; + } + + return err; + } else { + while (index < kMaxBridgedDevices) { + /* Find the first empty index in the bridged devices list */ + if (!mDevicesMap.Contains(index)) { + if (mDevicesMap.Insert(index, BridgedDevicePair(device, dataProvider))) { + /* Assign the free endpoint ID. */ + do { + err = CreateEndpoint(index, mCurrentDynamicEndpointId); + + /* Handle wrap condition */ + if (++mCurrentDynamicEndpointId < mFirstDynamicEndpointId) { + mCurrentDynamicEndpointId = mFirstDynamicEndpointId; } - return false; - } + } while (err == CHIP_ERROR_SENTINEL); - /* Handle wrap condition */ - if (++mCurrentDynamicEndpointId < mFirstDynamicEndpointId) { - mCurrentDynamicEndpointId = mFirstDynamicEndpointId; + if (err == CHIP_NO_ERROR) { + devicesPairIndex.SetValue(index); + mDevicesIndexes[mDevicesIndexesCounter] = index; + mDevicesIndexesCounter++; } + + return err; + } else { + /* Failing Insert metod means the BridgedDevicePair destructor will be called + * and pointers wiped out. It's not safe to iterate further. */ + return CHIP_ERROR_INTERNAL; } } + index++; } - index++; } LOG_ERR("Failed to add dynamic endpoint: No endpoints or indexes available!"); - return false; + return CHIP_ERROR_NO_MEMORY; +} + +CHIP_ERROR BridgeManager::CreateEndpoint(uint8_t index, uint16_t endpointId) +{ + if (!mDevicesMap.Contains(index)) { + LOG_ERR("Cannot retrieve bridged device from index %d", index); + return CHIP_ERROR_INTERNAL; + } + + auto *storedDevice = mDevicesMap[index].mDevice; + EmberAfStatus ret = emberAfSetDynamicEndpoint( + index, endpointId, storedDevice->mEp, + Span(storedDevice->mDataVersion, storedDevice->mDataVersionSize), + Span(storedDevice->mDeviceTypeList, storedDevice->mDeviceTypeListSize)); + + if (ret == EMBER_ZCL_STATUS_SUCCESS) { + LOG_INF("Added device to dynamic endpoint %d (index=%d)", endpointId, index); + storedDevice->Init(endpointId); + return CHIP_NO_ERROR; + } else if (ret != EMBER_ZCL_STATUS_DUPLICATE_EXISTS) { + LOG_ERR("Failed to add dynamic endpoint: Internal error!"); + if (CHIP_NO_ERROR != SafelyRemoveDevice(index)) { + LOG_ERR("Cannot remove device from the map!"); + } + return CHIP_ERROR_INTERNAL; + } else { + return CHIP_ERROR_SENTINEL; + } +} + +CHIP_ERROR BridgeManager::AddDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, chip::Optional devicesPairIndexes[], + uint16_t endpointIds[]) +{ + VerifyOrReturnError(devices && dataProvider, CHIP_ERROR_INTERNAL); + + /* Wrap input data into unique_ptr to avoid memory leakage in case any error occurs. */ + Platform::UniquePtr devicesPtr[deviceListSize]; + for (auto i = 0; i < deviceListSize; ++i) { + devicesPtr[i] = std::move(Platform::UniquePtr(devices[i])); + } + Platform::UniquePtr dataProviderPtr(dataProvider); + + dataProviderPtr->Init(); + + /* Maximum number of Matter bridged devices is controlled inside mDevicesMap, + but the data providers may be created independently, so let's ensure we do not + violate the maximum number of supported instances. */ + VerifyOrReturnError(mDevicesMap.FreeSlots() >= deviceListSize, CHIP_ERROR_NO_MEMORY, + LOG_ERR("Not enough free slots in the map")); + VerifyOrReturnError(mNumberOfProviders + 1 <= kMaxDataProviders, CHIP_ERROR_NO_MEMORY, + LOG_ERR("Maximum number of providers exceeded")); + mNumberOfProviders++; + + CHIP_ERROR cumulativeStatus{ CHIP_NO_ERROR }; + CHIP_ERROR status{ CHIP_NO_ERROR }; + for (auto i = 0; i < deviceListSize; ++i) { + MatterBridgedDevice *device = devicesPtr[i].get(); + status = AddSingleDevice(device, dataProviderPtr.get(), devicesPairIndexes[i], endpointIds[i]); + if (status == CHIP_NO_ERROR) { + devicesPtr[i].release(); + } else { + cumulativeStatus = status; + } + } + + if (cumulativeStatus == CHIP_NO_ERROR) { + dataProviderPtr.release(); + } + return cumulativeStatus; } CHIP_ERROR BridgeManager::HandleRead(uint16_t index, ClusterId clusterId, @@ -203,10 +346,12 @@ void BridgeManager::HandleUpdate(BridgedDeviceDataProvider &dataProvider, Cluste { VerifyOrReturn(data); - /* The state update was triggered by non-Matter device, find bridged Matter device to update it as well. */ + /* The state update was triggered by non-Matter device, find bridged Matter device to update it as well. + */ for (auto &item : Instance().mDevicesMap.mMap) { if (item.value.mProvider == &dataProvider) { - /* If the Bridged Device state was updated successfully, schedule sending Matter data report. */ + /* If the Bridged Device state was updated successfully, schedule sending Matter data + * report. */ auto *device = item.value.mDevice; if (CHIP_NO_ERROR == device->HandleAttributeChange(clusterId, attributeId, data, dataSize)) { MatterReportingAttributeChangeCallback(device->GetEndpointId(), clusterId, attributeId); diff --git a/samples/matter/common/src/bridge/bridge_manager.h b/samples/matter/common/src/bridge/bridge_manager.h index e6a8e6fd8aa..dc0da0da61b 100644 --- a/samples/matter/common/src/bridge/bridge_manager.h +++ b/samples/matter/common/src/bridge/bridge_manager.h @@ -12,7 +12,20 @@ class BridgeManager { public: - void Init(); + static constexpr uint8_t kMaxBridgedDevices = CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; + + using LoadStoredBridgedDevicesCallback = CHIP_ERROR (*)(); + + /** + * @brief Initialize BridgeManager instance. + * + * @param loadStoredBridgedDevicesCb callback to method capable of loading and adding bridged devices stored in + * persistent storage + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR Init(LoadStoredBridgedDevicesCallback loadStoredBridgedDevicesCb); + /** * @brief Add devices which are supposed to be bridged to the Bridge Manager * @@ -20,11 +33,66 @@ class BridgeManager { * application and can be memory constrained) * @param dataProvider data provider which is going to be bridged with Matter devices * @param deviceListSize number of Matter devices which are going to be bridged with data provider - * @return CHIP_NO_ERROR in case of success, specific CHIP_ERROR code otherwise + * @param devicesPairIndexes array of the indexes that will be filled with pairs' indexes + * assigned by the bridge + * @return CHIP_NO_ERROR on success + * @return other error code on failure */ CHIP_ERROR AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, - uint8_t deviceListSize); - CHIP_ERROR RemoveBridgedDevice(uint16_t endpoint); + uint8_t deviceListSize, uint8_t devicesPairIndexes[]); + + /** + * @brief Add devices which are supposed to be bridged to the Bridge Manager using specific index and endpoint + * id. + * + * @param devices Matter devices to be bridged (the maximum size of this array may depend on the + * application and can be memory constrained) + * @param dataProvider data provider which is going to be bridged with Matter devices + * @param deviceListSize number of Matter devices which are going to be bridged with data provider + * @param devicesPairIndexes array of the indexes that contain index value required to be + * assigned and that will be filled with pairs' indexes finally assigned by the bridge + * @param endpointIds values of endpoint ids required to be assigned + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, uint8_t devicesPairIndexes[], uint16_t endpointIds[]); + + /** + * @brief Remove bridged device. + * + * @param endpoint value of endpoint id specifying the bridged device to be removed + * @param devicesPairIndex reference to the index that will be filled with pair's index obtained by the + * bridge + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR RemoveBridgedDevice(uint16_t endpoint, uint8_t &devicesPairIndex); + + /** + * @brief Get bridged devices indexes. + * + * @param indexes array address to be filled with bridged devices indexes + * @param maxSize maximum size that can be used for indexes array + * @param count reference to the count object to be filled with size of actually saved data + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR GetDevicesIndexes(uint8_t *indexes, uint8_t maxSize, uint8_t &count) + { + if (!indexes) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (maxSize < mDevicesIndexesCounter) { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(indexes, mDevicesIndexes, mDevicesIndexesCounter); + count = mDevicesIndexesCounter; + return CHIP_NO_ERROR; + } + static CHIP_ERROR HandleRead(uint16_t index, chip::ClusterId clusterId, const EmberAfAttributeMetadata *attributeMetadata, uint8_t *buffer, uint16_t maxReadLength); @@ -90,17 +158,68 @@ class BridgeManager { BridgedDeviceDataProvider *mProvider; }; - static constexpr uint8_t kMaxBridgedDevices = CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; static constexpr uint8_t kMaxDataProviders = CONFIG_BRIDGE_MAX_BRIDGED_DEVICES_NUMBER; using DeviceMap = FiniteMap; - bool AddSingleDevice(MatterBridgedDevice *device, BridgedDeviceDataProvider *dataProvider); + CHIP_ERROR AddSingleDevice(MatterBridgedDevice *device, BridgedDeviceDataProvider *dataProvider, + chip::Optional &devicesPairIndex, uint16_t endpointId); CHIP_ERROR SafelyRemoveDevice(uint8_t index); + /** + * @brief Add pair of bridged devices and their data provider using optional index and endpoint id. This is a + * wrapper method invoked by public AddBridgedDevices methods that maps integer indexes to optionals are assigns + * output index values. + * + * @param device address of valid bridged device object + * @param dataProvider address of valid data provider object + * @param devicesPairIndexes array of the index objects that will be filled with pairs' indexes + * assigned by the bridge + * @param endpointIds values of endpoint ids required to be assigned + * @param indexes array of pointers to the optional index objects that shall have a valid value set if the value + * is meant to be used to index assignment, or shall not have a value set if the default index assignment should + * be used. + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR AddBridgedDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, uint8_t devicesPairIndexes[], uint16_t endpointIds[], + chip::Optional indexes[]); + + /** + * @brief Add pair of bridged devices and their data provider using optional index and endpoint id. The method + * creates a map entry, matches the bridged device object with the data provider object and creates Matter + * dynamic endpoint. + * + * @param device address of valid bridged device object + * @param dataProvider address of valid data provider object + * @param devicesPairIndex array of optional index objects that shall have a valid value set if + * the value is meant to be used to index assignment, or shall not have a value set if the default index + * assignment should be used. + * @param endpointIds values of endpoint ids required to be assigned + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR AddDevices(MatterBridgedDevice *devices[], BridgedDeviceDataProvider *dataProvider, + uint8_t deviceListSize, chip::Optional devicesPairIndexes[], + uint16_t endpointIds[]); + + /** + * @brief Create Matter dynamic endpoint. + * + * @param index index in Matter Data Model's (ember) array to store the endpoint + * @param endpointId value of endpoint id to be created + * @return CHIP_NO_ERROR on success + * @return other error code on failure + */ + CHIP_ERROR CreateEndpoint(uint8_t index, uint16_t endpointId); + DeviceMap mDevicesMap; uint16_t mNumberOfProviders{ 0 }; + uint8_t mDevicesIndexes[BridgeManager::kMaxBridgedDevices] = { 0 }; + uint8_t mDevicesIndexesCounter; chip::EndpointId mFirstDynamicEndpointId; chip::EndpointId mCurrentDynamicEndpointId; + bool mIsInitialized = false; };