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; };