diff --git a/docs/guides/esp32/factory_data.md b/docs/guides/esp32/factory_data.md index 9818eec9880964..179fb2e315a9c0 100644 --- a/docs/guides/esp32/factory_data.md +++ b/docs/guides/esp32/factory_data.md @@ -34,6 +34,9 @@ Following data can be added to the manufacturing partition using - Fixed Labels - Supported locales - Supported calendar types + - Supported modes + - Note: As per spec at max size of label should be 64 and `\0` will be + added at the end. ### Configuration Options diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index 4b2f6a464e2933..70f17774c00d77 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/common" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/shell_extension" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/lock" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/platform/esp32/mode-support" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/util" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/reporting" @@ -91,6 +92,9 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/src" ) + +set(EXCLUDE_SRCS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp") + if (CONFIG_ENABLE_PW_RPC) # Append additional directories for RPC build set(PRIV_INCLUDE_DIRS_LIST "${PRIV_INCLUDE_DIRS_LIST}" @@ -119,7 +123,7 @@ endif() idf_component_register(PRIV_INCLUDE_DIRS ${PRIV_INCLUDE_DIRS_LIST} SRC_DIRS ${SRC_DIRS_LIST} - EXCLUDE_SRCS ${EXCLUDE_SRCS_LIST} + EXCLUDE_SRCS ${EXCLUDE_SRCS} PRIV_REQUIRES ${PRIV_REQUIRES_LIST}) get_filename_component(CHIP_ROOT ${CMAKE_SOURCE_DIR}/third_party/connectedhomeip REALPATH) diff --git a/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp b/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp new file mode 100644 index 00000000000000..ec752cd8be67ba --- /dev/null +++ b/examples/platform/esp32/mode-support/static-supported-modes-manager.cpp @@ -0,0 +1,209 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "static-supported-modes-manager.h" +#include +#include +#include + +using namespace chip; +using namespace chip::app::Clusters; +using namespace chip::DeviceLayer::Internal; +using namespace chip::app::Clusters::ModeSelect; +using chip::Protocols::InteractionModel::Status; + +using ModeOptionStructType = Structs::ModeOptionStruct::Type; +using SemanticTag = Structs::SemanticTagStruct::Type; +template +using List = app::DataModel::List; + +const StaticSupportedModesManager StaticSupportedModesManager::instance = StaticSupportedModesManager(); + +SupportedModesManager::ModeOptionsProvider StaticSupportedModesManager::epModeOptionsProviderList[FIXED_ENDPOINT_COUNT]; + +void StaticSupportedModesManager::InitEndpointArray() +{ + for (int i = 0; i < FIXED_ENDPOINT_COUNT; i++) + { + epModeOptionsProviderList[i] = ModeOptionsProvider(); + } +} + +SupportedModesManager::ModeOptionsProvider StaticSupportedModesManager::getModeOptionsProvider(EndpointId endpointId) const +{ + if (epModeOptionsProviderList[endpointId].begin() != nullptr && epModeOptionsProviderList[endpointId].end() != nullptr) + { + return ModeOptionsProvider(epModeOptionsProviderList[endpointId].begin(), epModeOptionsProviderList[endpointId].end()); + } + + ModeOptionStructType * modeOptionStructList = nullptr; + SemanticTag * semanticTags = nullptr; + + char keyBuf[ESP32Config::kMaxConfigKeyNameLength]; + uint32_t supportedModeCount = 0; + + VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesCount(keyBuf, sizeof(keyBuf), endpointId) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr)); + ESP32Config::Key countKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValue(countKey, supportedModeCount) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr)); + + modeOptionStructList = new ModeOptionStructType[supportedModeCount]; + if (modeOptionStructList == nullptr) + { + return ModeOptionsProvider(nullptr, nullptr); + } + + epModeOptionsProviderList[endpointId] = ModeOptionsProvider(modeOptionStructList, modeOptionStructList + supportedModeCount); + + for (int index = 0; index < supportedModeCount; index++) + { + Structs::ModeOptionStruct::Type option; + uint32_t supportedModeMode = 0; + uint32_t semanticTagCount = 0; + size_t outLen = 0; + + memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); + VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesLabel(keyBuf, sizeof(keyBuf), endpointId, index) == + CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + ESP32Config::Key labelKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValueStr(labelKey, nullptr, 0, outLen) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + char * modeLabel = new char[outLen + 1]; + if (modeLabel == nullptr) + { + CleanUp(endpointId); + return ModeOptionsProvider(nullptr, nullptr); + } + + VerifyOrReturnValue(ESP32Config::ReadConfigValueStr(labelKey, modeLabel, outLen + 1, outLen) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); + VerifyOrReturnValue(ESP32Config::KeyAllocator::SupportedModesValue(keyBuf, sizeof(keyBuf), endpointId, index) == + CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + ESP32Config::Key modeKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValue(labelKey, supportedModeMode) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); + VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagsCount(keyBuf, sizeof(keyBuf), endpointId, index) == + CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + ESP32Config::Key stCountKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValue(stCountKey, semanticTagCount) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + semanticTags = new SemanticTag[semanticTagCount]; + if (semanticTags == nullptr) + { + CleanUp(endpointId); + return ModeOptionsProvider(nullptr, nullptr); + } + for (auto stIndex = 0; stIndex < semanticTagCount; stIndex++) + { + + uint32_t semanticTagValue = 0; + uint32_t semanticTagMfgCode = 0; + SemanticTag tag; + + memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); + VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagValue(keyBuf, sizeof(keyBuf), endpointId, index, stIndex) == + CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + ESP32Config::Key stValueKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValue(stValueKey, semanticTagValue) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + memset(keyBuf, 0, sizeof(char) * ESP32Config::kMaxConfigKeyNameLength); + VerifyOrReturnValue(ESP32Config::KeyAllocator::SemanticTagMfgCode(keyBuf, sizeof(keyBuf), endpointId, index, stIndex) == + CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + ESP32Config::Key stMfgCodeKey(ESP32Config::kConfigNamespace_ChipFactory, keyBuf); + VerifyOrReturnValue(ESP32Config::ReadConfigValue(stMfgCodeKey, semanticTagMfgCode) == CHIP_NO_ERROR, + ModeOptionsProvider(nullptr, nullptr), CleanUp(endpointId)); + + tag.value = static_cast(semanticTagValue); + tag.mfgCode = static_cast(semanticTagMfgCode); + semanticTags[stIndex] = tag; + } + + option.label = chip::CharSpan::fromCharString(modeLabel); + option.mode = static_cast(supportedModeMode); + option.semanticTags = DataModel::List(semanticTags, semanticTagCount); + + modeOptionStructList[index] = option; + } + + return ModeOptionsProvider(modeOptionStructList, modeOptionStructList + supportedModeCount); +} + +Status StaticSupportedModesManager::getModeOptionByMode(unsigned short endpointId, unsigned char mode, + const ModeOptionStructType ** dataPtr) const +{ + auto modeOptionsProvider = this->getModeOptionsProvider(endpointId); + if (modeOptionsProvider.begin() == nullptr) + { + return Status::UnsupportedCluster; + } + auto * begin = modeOptionsProvider.begin(); + auto * end = modeOptionsProvider.end(); + + for (auto * it = begin; it != end; ++it) + { + auto & modeOption = *it; + if (modeOption.mode == mode) + { + *dataPtr = &modeOption; + return Status::Success; + } + } + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "Cannot find the mode %u", mode); + return Status::InvalidCommand; +} + +const ModeSelect::SupportedModesManager * ModeSelect::getSupportedModesManager() +{ + return &StaticSupportedModesManager::instance; +} + +void StaticSupportedModesManager::FreeSupportedModes(EndpointId endpointId) const +{ + if (epModeOptionsProviderList[endpointId].begin() != nullptr) + { + auto * begin = epModeOptionsProviderList[endpointId].begin(); + auto * end = epModeOptionsProviderList[endpointId].end(); + for (auto * it = begin; it != end; ++it) + { + auto & modeOption = *it; + delete[] modeOption.label.data(); + delete[] modeOption.semanticTags.data(); + } + delete[] begin; + } + epModeOptionsProviderList[endpointId] = ModeOptionsProvider(); +} + +void StaticSupportedModesManager::CleanUp(EndpointId endpointId) const +{ + ChipLogError(Zcl, "Supported mode data is in incorrect format"); + FreeSupportedModes(endpointId); +} diff --git a/examples/platform/esp32/mode-support/static-supported-modes-manager.h b/examples/platform/esp32/mode-support/static-supported-modes-manager.h new file mode 100644 index 00000000000000..689c9d059f4ab7 --- /dev/null +++ b/examples/platform/esp32/mode-support/static-supported-modes-manager.h @@ -0,0 +1,69 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ModeSelect { + +class StaticSupportedModesManager : public chip::app::Clusters::ModeSelect::SupportedModesManager +{ +private: + using ModeOptionStructType = Structs::ModeOptionStruct::Type; + using SemanticTag = Structs::SemanticTagStruct::Type; + + static ModeOptionsProvider epModeOptionsProviderList[FIXED_ENDPOINT_COUNT]; + + void InitEndpointArray(); + + void FreeSupportedModes(EndpointId endpointId) const; + +public: + static const StaticSupportedModesManager instance; + + SupportedModesManager::ModeOptionsProvider getModeOptionsProvider(EndpointId endpointId) const override; + + Protocols::InteractionModel::Status getModeOptionByMode(EndpointId endpointId, uint8_t mode, + const ModeOptionStructType ** dataPtr) const override; + + void CleanUp(EndpointId endpointId) const; + + StaticSupportedModesManager() { InitEndpointArray(); } + + ~StaticSupportedModesManager() + { + for (int i = 0; i < FIXED_ENDPOINT_COUNT; i++) + { + FreeSupportedModes(i); + } + } + + static inline const StaticSupportedModesManager & getStaticSupportedModesManagerInstance() { return instance; } +}; + +const SupportedModesManager * getSupportedModesManager(); + +} // namespace ModeSelect +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index fac1c274e70a67..f5744720c6fc6c 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -226,6 +226,32 @@ def get_fixed_label_dict(fixed_labels): return fl_dict +# get_supported_modes_dict() converts the list of strings to per endpoint dictionaries. +# example input : ['0/label1/1/"1\0x8000, 2\0x8000", 1/label2/1/"1\0x8000, 2\0x8000"'] +# example outout : {'1': [{'Label': 'label1', 'Mode': 0, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}, {'Label': 'label2', 'Mode': 1, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}]} + + +def get_supported_modes_dict(supported_modes): + output_dict = {} + + for mode_str in supported_modes: + mode_label_strs = mode_str.split('/') + mode = mode_label_strs[0] + label = mode_label_strs[1] + ep = mode_label_strs[2] + + semantic_tag_strs = mode_label_strs[3].split(', ') + semantic_tags = [{"value": int(v.split('\\')[0]), "mfgCode": int(v.split('\\')[1], 16)} for v in semantic_tag_strs] + + mode_dict = {"Label": label, "Mode": int(mode), "Semantic_Tag": semantic_tags} + + if ep in output_dict: + output_dict[ep].append(mode_dict) + else: + output_dict[ep] = [mode_dict] + + return output_dict + def check_str_range(s, min_len, max_len, name): if s and ((len(s) < min_len) or (len(s) > max_len)): @@ -358,6 +384,60 @@ def populate_factory_data(args, spake2p_params): FACTORY_DATA.update({'fl-k/{:x}/{:x}'.format(int(key), i): _label_key}) FACTORY_DATA.update({'fl-v/{:x}/{:x}'.format(int(key), i): _label_value}) + # SupportedModes are stored as multiple entries + # - sm-sz/ : number of supported modes for the endpoint + # - sm-label// : supported modes label key for the endpoint and index + # - sm-mode// : supported modes mode key for the endpoint and index + # - sm-st-sz// : supported modes SemanticTag key for the endpoint and index + # - st-v/// : semantic tag value key for the endpoint and index and ind + # - st-mfg/// : semantic tag mfg code key for the endpoint and index and ind + if (args.supported_modes is not None): + dictionary = get_supported_modes_dict(args.supported_modes) + for ep in dictionary.keys(): + _sz = { + 'type': 'data', + 'encoding': 'u32', + 'value': len(dictionary[ep]) + } + FACTORY_DATA.update({'sm-sz/{:x}'.format(int(ep)): _sz}) + for i in range(len(dictionary[ep])): + item = dictionary[ep][i] + _label = { + 'type': 'data', + 'encoding': 'string', + 'value': item["Label"] + } + _mode = { + 'type': 'data', + 'encoding': 'u32', + 'value': item["Mode"] + } + _st_sz = { + 'type': 'data', + 'encoding': 'u32', + 'value': len(item["Semantic_Tag"]) + } + FACTORY_DATA.update({'sm-label/{:x}/{:x}'.format(int(ep), i): _label}) + FACTORY_DATA.update({'sm-mode/{:x}/{:x}'.format(int(ep), i): _mode}) + FACTORY_DATA.update({'sm-st-sz/{:x}/{:x}'.format(int(ep), i): _st_sz}) + + for j in range(len(item["Semantic_Tag"])): + entry = item["Semantic_Tag"][j] + + _value = { + 'type': 'data', + 'encoding': 'u32', + 'value': entry["value"] + } + _mfg_code = { + 'type': 'data', + 'encoding': 'u32', + 'value': entry["mfgCode"] + } + + FACTORY_DATA.update({'st-v/{:x}/{:x}/{:x}'.format(int(ep), i, j): _value}) + FACTORY_DATA.update({'st-mfg/{:x}/{:x}/{:x}'.format(int(ep), i, j): _mfg_code}) + def gen_raw_ec_keypair_from_der(key_file, pubkey_raw_file, privkey_raw_file): with open(key_file, 'rb') as f: @@ -467,6 +547,8 @@ def any_base_int(s): return int(s, 0) parser.add_argument('--locales', nargs='+', help='List of supported locales, Language Tag as defined by BCP47, eg. en-US en-GB') parser.add_argument('--fixed-labels', nargs='+', help='List of fixed labels, eg: "0/orientation/up" "1/orientation/down" "2/orientation/down"') + parser.add_argument('--supported-modes', type=str, nargs='+', required=False, + help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"') parser.add_argument('-s', '--size', type=any_base_int, default=0x6000, help='The size of the partition.bin, default: 0x6000') diff --git a/src/app/clusters/mode-select-server/supported-modes-manager.h b/src/app/clusters/mode-select-server/supported-modes-manager.h index 497a604497200a..4a6ccd20c5f60a 100644 --- a/src/app/clusters/mode-select-server/supported-modes-manager.h +++ b/src/app/clusters/mode-select-server/supported-modes-manager.h @@ -20,9 +20,6 @@ #include #include -#include -#include -#include #include namespace chip { @@ -56,6 +53,8 @@ class SupportedModesManager */ inline pointer end() const { return mEnd; } + ModeOptionsProvider() : mBegin(nullptr), mEnd(nullptr) {} + ModeOptionsProvider(const pointer aBegin, const pointer aEnd) : mBegin(aBegin), mEnd(aEnd) {} pointer mBegin; diff --git a/src/platform/ESP32/ESP32Config.h b/src/platform/ESP32/ESP32Config.h index c85303c2197555..e07d98eba27594 100644 --- a/src/platform/ESP32/ESP32Config.h +++ b/src/platform/ESP32/ESP32Config.h @@ -180,6 +180,39 @@ class ESP32Config::KeyAllocator VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); return snprintf(key, size, "fl-v/%x/%x", endpoint, index) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; } + + // Supported modes + + static CHIP_ERROR SupportedModesCount(char * key, size_t size, uint16_t endpoint) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "sm-sz/%x", endpoint) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } + static CHIP_ERROR SupportedModesLabel(char * key, size_t size, uint16_t endpoint, uint16_t index) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "sm-label/%x/%x", endpoint, index) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } + static CHIP_ERROR SupportedModesValue(char * key, size_t size, uint16_t endpoint, uint16_t index) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "sm-mode/%x/%x", endpoint, index) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } + static CHIP_ERROR SemanticTagsCount(char * key, size_t size, uint16_t endpoint, uint16_t index) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "sm-st-sz/%x/%x", endpoint, index) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } + static CHIP_ERROR SemanticTagValue(char * key, size_t size, uint16_t endpoint, uint16_t index, uint16_t ind) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "st-v/%x/%x/%x", endpoint, index, ind) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } + static CHIP_ERROR SemanticTagMfgCode(char * key, size_t size, uint16_t endpoint, uint16_t index, uint16_t ind) + { + VerifyOrReturnError(key, CHIP_ERROR_INVALID_ARGUMENT); + return snprintf(key, size, "st-mfg/%x/%x/%x", endpoint, index, ind) > 0 ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; + } }; } // namespace Internal