From e48e04ad9cf803c68470d6330379ca963c8f81f5 Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Mon, 22 Apr 2024 19:06:36 +0530 Subject: [PATCH] [ESP32] Fix few attributes with fixed quality in DeviceInfoProvider (#32893) * [ESP32] Fix few attributes with fixed quality in DeviceInfoProvider Fixed labels, supported locales, supported calendar types were being read from the nvs(flash) and during OTA its a hassle if one wants to upgrade these values. Added few APIs to set the data for these attributes in ESP32DeviceInfoProvider. * Restyled by clang-format * Restyled by prettier-markdown * fix the lint errors * Add back the original Device info provider which reads from the nvs Add StaticESP32DeviceInfoProvider along with APIs to set data Remove changes from example and add a guide along with usage --------- Co-authored-by: Restyled.io --- docs/guides/esp32/README.md | 1 + docs/guides/esp32/factory_data.md | 10 +- docs/guides/esp32/providers.md | 76 ++++++++++ .../tools/generate_esp32_chip_factory_bin.py | 124 --------------- src/platform/ESP32/BUILD.gn | 2 + .../ESP32/StaticESP32DeviceInfoProvider.cpp | 122 +++++++++++++++ .../ESP32/StaticESP32DeviceInfoProvider.h | 141 ++++++++++++++++++ 7 files changed, 345 insertions(+), 131 deletions(-) create mode 100644 docs/guides/esp32/providers.md create mode 100644 src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp create mode 100644 src/platform/ESP32/StaticESP32DeviceInfoProvider.h diff --git a/docs/guides/esp32/README.md b/docs/guides/esp32/README.md index e487a58f318591..443058a17b07aa 100644 --- a/docs/guides/esp32/README.md +++ b/docs/guides/esp32/README.md @@ -18,3 +18,4 @@ example on ESP32 series of SoCs - [Matter OTA](ota.md) - [Generating and Using ESP Secure Cert Partition](secure_cert_partition.md) - [BLE Settings](ble_settings.md) +- [Providers](providers.md) diff --git a/docs/guides/esp32/factory_data.md b/docs/guides/esp32/factory_data.md index 9ea2e12d1a87d3..638d3aa134b11f 100644 --- a/docs/guides/esp32/factory_data.md +++ b/docs/guides/esp32/factory_data.md @@ -44,13 +44,9 @@ Following data can be added to the manufacturing partition using - Serial Number - Unique identifier -- Device information - - 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. +- 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/docs/guides/esp32/providers.md b/docs/guides/esp32/providers.md new file mode 100644 index 00000000000000..59b91b624a49fc --- /dev/null +++ b/docs/guides/esp32/providers.md @@ -0,0 +1,76 @@ +## Providers Implemented for ESP32 Platform + +The ESP32 platform has implemented several providers that can be used with data +stored in the factory or by setting fixed data. + +Below are the providers that have been implemented: + +- [Commissionable Data Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L47) + This provider reads the discriminator and setup pincode related parameters + from the factory partition. +- [Device Attestation Credentials Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56) + This provider manages the attestation data. +- [Device Instance Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L86) + This provider reads basic device information from the factory partition. +- [Device Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32DeviceInfoProvider.h#L31) + This provider provides fixed labels, supported calendar types, and supported + locales from the factory partition. +- [Supported Modes](https://github.com/project-chip/connectedhomeip/blob/master/examples/platform/esp32/mode-support/static-supported-modes-manager.h#L28) + This provider offers the supported modes for the mode-select cluster. + +More information can be found in the [factory data guide](factory_data.md). + +### Device Info Provider + +Currently, there are two implementations for this provider: + +1. [Reads data stored in the factory partition](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56) + _(This will be deprecated in the future)_ +2. [Provides APIs to set fixed data that gets read later](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/StaticESP32DeviceInfoProvider.h) + +- New products should use the `StaticESP32DeviceInfoProvider`. Utilize the + `Set...()` APIs to set the fixed data. +- Existing products using the first implementation can continue to use it if + they do not wish to change the data. +- For products using the first implementation and wanting to change the fixed + data via OTA, they should switch to the second implementation in the OTA + image and use the `Set...()` APIs to set the fixed data. + +#### Example: + +```cpp +#include + +DeviceLayer::StaticESP32DeviceInfoProvider deviceInfoProvider; + +// Define array for Supported Calendar Types +using namespace chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum; +CalendarTypeEnum supportedCalendarTypes[] = { + CalendarTypeEnum::kGregorian, CalendarTypeEnum::kCoptic, + CalendarTypeEnum::kEthiopian, CalendarTypeEnum::kChinese, +}; + +// Define array for Supported Locales +const char* supportedLocales[] = { + "en-US", + "en-EU", +}; + +// Define array for Fixed labels { EndpointId, Label, Value } +struct StaticESP32DeviceInfoProvider::FixedLabelEntry fixedLabels[] = { + { 0, "Room", "Bedroom 2" }, + { 0, "Orientation", "North" }, + { 0, "Direction", "Up" }, +}; + +Span sSupportedCalendarTypes(supportedCalendarTypes); +Span sSupportedLocales(supportedLocales); +Span sFixedLabels(fixedLabels); + +{ + deviceInfoProvider.SetSupportedLocales(sSupportedLocales); + deviceInfoProvider.SetSupportedCalendarTypes(sSupportedCalendarTypes); + deviceInfoProvider.SetFixedLabels(sFixedLabels); + DeviceLayer::SetDeviceInfoProvider(&deviceInfoProvider); +} +``` diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index a4ccce52c0ab77..ff00daedb53682 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -18,15 +18,12 @@ import argparse import base64 -import enum import logging import os import sys from types import SimpleNamespace import cryptography.x509 -from bitarray import bitarray -from bitarray.util import ba2int from esp_secure_cert.tlv_format import generate_partition_ds, generate_partition_no_ds, tlv_priv_key_t, tlv_priv_key_type_t CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))] @@ -152,52 +149,9 @@ 'encoding': 'hex2bin', 'value': None, }, - # DeviceInfoProvider - 'cal-types': { - 'type': 'data', - 'encoding': 'u32', - 'value': None, - }, - 'locale-sz': { - 'type': 'data', - 'encoding': 'u32', - 'value': None, - }, - - # Other device info provider keys are dynamically generated - # in the respective functions. } -class CalendarTypes(enum.Enum): - Buddhist = 0 - Chinese = 1 - Coptic = 2 - Ethiopian = 3 - Gregorian = 4 - Hebrew = 5 - Indian = 6 - Islamic = 7 - Japanese = 8 - Korean = 9 - Persian = 10 - Taiwanese = 11 - - -# Supported Calendar types is stored as a bit array in one uint32_t. -def calendar_types_to_uint32(calendar_types): - result = bitarray(32, endian='little') - result.setall(0) - for calendar_type in calendar_types: - try: - result[CalendarTypes[calendar_type].value] = 1 - except KeyError: - logging.error('Unknown calendar type: %s', calendar_type) - logging.error('Supported calendar types: %s', ', '.join(CalendarTypes.__members__)) - sys.exit(1) - return ba2int(result) - - def ishex(s): try: _ = int(s, 16) @@ -205,31 +159,6 @@ def ishex(s): except ValueError: return False -# get_fixed_label_dict() converts the list of strings to per endpoint dictionaries. -# example input : ['0/orientation/up', '1/orientation/down', '2/orientation/down'] -# example output : {'0': [{'orientation': 'up'}], '1': [{'orientation': 'down'}], '2': [{'orientation': 'down'}]} - - -def get_fixed_label_dict(fixed_labels): - fl_dict = {} - for fl in fixed_labels: - _l = fl.split('/') - - if len(_l) != 3: - logging.error('Invalid fixed label: %s', fl) - sys.exit(1) - - if not (ishex(_l[0]) and (len(_l[1]) > 0 and len(_l[1]) < 16) and (len(_l[2]) > 0 and len(_l[2]) < 16)): - logging.error('Invalid fixed label: %s', fl) - sys.exit(1) - - if _l[0] not in fl_dict.keys(): - fl_dict[_l[0]] = list() - - fl_dict[_l[0]].append({_l[1]: _l[2]}) - - return fl_dict - # get_supported_modes_dict() converts the list of strings to per endpoint dictionaries. # example with semantic tags # input : ['0/label1/1/"1\0x8000, 2\0x8000" 1/label2/1/"1\0x8000, 2\0x8000"'] @@ -373,52 +302,6 @@ def populate_factory_data(args, spake2p_params): if args.hw_ver_str: FACTORY_DATA['hw-ver-str']['value'] = args.hw_ver_str - if args.calendar_types: - FACTORY_DATA['cal-types']['value'] = calendar_types_to_uint32(args.calendar_types) - - # Supported locale is stored as multiple entries, key format: "locale/, example key: "locale/0" - if args.locales: - FACTORY_DATA['locale-sz']['value'] = len(args.locales) - - for i in range(len(args.locales)): - _locale = { - 'type': 'data', - 'encoding': 'string', - 'value': args.locales[i] - } - FACTORY_DATA.update({'locale/{:x}'.format(i): _locale}) - - # Each endpoint can contains the fixed lables - # - fl-sz/ : number of fixed labels for the endpoint - # - fl-k// : fixed label key for the endpoint and index - # - fl-v// : fixed label value for the endpoint and index - if args.fixed_labels: - dict = get_fixed_label_dict(args.fixed_labels) - for key in dict.keys(): - _sz = { - 'type': 'data', - 'encoding': 'u32', - 'value': len(dict[key]) - } - FACTORY_DATA.update({'fl-sz/{:x}'.format(int(key)): _sz}) - - for i in range(len(dict[key])): - entry = dict[key][i] - - _label_key = { - 'type': 'data', - 'encoding': 'string', - 'value': list(entry.keys())[0] - } - _label_value = { - 'type': 'data', - 'encoding': 'string', - 'value': list(entry.values())[0] - } - - 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 @@ -584,13 +467,6 @@ def any_base_int(s): return int(s, 0) help=('128-bit unique identifier for generating rotating device identifier, ' 'provide 32-byte hex string, e.g. "1234567890abcdef1234567890abcdef"')) - # These will be used by DeviceInfoProvider - parser.add_argument('--calendar-types', nargs='+', - help=('List of supported calendar types.\nSupported Calendar Types: Buddhist, Chinese, Coptic, Ethiopian, ' - 'Gregorian, Hebrew, Indian, Islamic, Japanese, Korean, Persian, Taiwanese')) - 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"') diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index b1d3650769c468..167d0eb6bc139c 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -185,6 +185,8 @@ static_library("ESP32") { sources += [ "ESP32DeviceInfoProvider.cpp", "ESP32DeviceInfoProvider.h", + "StaticESP32DeviceInfoProvider.cpp", + "StaticESP32DeviceInfoProvider.h", ] } diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp new file mode 100644 index 00000000000000..d65bdf8fc280fe --- /dev/null +++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp @@ -0,0 +1,122 @@ +/* + + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +namespace chip { +namespace DeviceLayer { + +StaticESP32DeviceInfoProvider & StaticESP32DeviceInfoProvider::GetDefaultInstance(void) +{ + static StaticESP32DeviceInfoProvider sInstance; + return sInstance; +} + +DeviceInfoProvider::FixedLabelIterator * StaticESP32DeviceInfoProvider::IterateFixedLabel(EndpointId endpoint) +{ + return chip::Platform::New(endpoint, mFixedLabels); +} + +StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::StaticFixedLabelIteratorImpl(EndpointId endpoint, + const Span & labels) +{ + mEndpoint = endpoint; + mLabels = labels; + mIndex = 0; +} + +size_t StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Count() +{ + size_t count = 0; + for (size_t i = 0; i < mLabels.size(); i++) + { + const FixedLabelEntry & entry = mLabels.data()[i]; + + if (entry.endpointId == mEndpoint) + { + count++; + } + } + return count; +} + +bool StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Next(FixedLabelType & output) +{ + ChipLogDetail(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast(mIndex), mEndpoint); + + while (mIndex < mLabels.size()) + { + const FixedLabelEntry & entry = mLabels.data()[mIndex++]; + if (entry.endpointId == mEndpoint) + { + output.label = entry.label; + output.value = entry.value; + return true; + } + } + + return false; +} + +DeviceInfoProvider::SupportedLocalesIterator * StaticESP32DeviceInfoProvider::IterateSupportedLocales() +{ + return chip::Platform::New(mSupportedLocales); +} + +StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::StaticSupportedLocalesIteratorImpl( + const Span & locales) +{ + mLocales = locales; +} + +size_t StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Count() +{ + return mLocales.empty() ? 0 : mLocales.size(); +} + +bool StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Next(CharSpan & output) +{ + VerifyOrReturnValue(mIndex < mLocales.size(), false); + output = mLocales.data()[mIndex++]; + return true; +} + +DeviceInfoProvider::SupportedCalendarTypesIterator * StaticESP32DeviceInfoProvider::IterateSupportedCalendarTypes() +{ + return chip::Platform::New(mSupportedCalendarTypes); +} + +StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::StaticSupportedCalendarTypesIteratorImpl( + const Span & calendarTypes) +{ + mCalendarTypes = calendarTypes; +} + +size_t StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Count() +{ + return mCalendarTypes.empty() ? 0 : mCalendarTypes.size(); +} + +bool StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Next(CalendarType & output) +{ + VerifyOrReturnValue(mIndex < mCalendarTypes.size(), false); + output = mCalendarTypes.data()[mIndex++]; + return true; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/ESP32/StaticESP32DeviceInfoProvider.h b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h new file mode 100644 index 00000000000000..860110cc0ac9a2 --- /dev/null +++ b/src/platform/ESP32/StaticESP32DeviceInfoProvider.h @@ -0,0 +1,141 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +namespace chip { +namespace DeviceLayer { + +class StaticESP32DeviceInfoProvider : public ESP32DeviceInfoProvider +{ +public: + StaticESP32DeviceInfoProvider() = default; + ~StaticESP32DeviceInfoProvider() override {} + + // Iterators + FixedLabelIterator * IterateFixedLabel(EndpointId endpoint); + SupportedLocalesIterator * IterateSupportedLocales(); + SupportedCalendarTypesIterator * IterateSupportedCalendarTypes(); + + static StaticESP32DeviceInfoProvider & GetDefaultInstance(); + + struct FixedLabelEntry + { + EndpointId endpointId; + CharSpan label; + CharSpan value; + }; + + /** + * @brief API to set the supported calendar types + * + * @param[in] supportedCalendarTypes Span of type chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum + * containing the supported calendar types. The underlying data must remain allocated throughout + * the lifetime of the device, as the API does not make a copy. + * + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + CHIP_ERROR SetSupportedCalendarTypes(const Span & supportedCalendarTypes) + { + VerifyOrReturnError(!supportedCalendarTypes.empty(), CHIP_ERROR_INVALID_ARGUMENT); + mSupportedCalendarTypes = supportedCalendarTypes; + return CHIP_NO_ERROR; + } + + /** + * @brief API to set the supported Locales + * + * @param[in] supportedLocales Span of type chip::CharSpan containing the supported locales. + * The underlying data must remain allocated throughout the lifetime of the device, + * as the API does not make a copy. + * + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + CHIP_ERROR SetSupportedLocales(const Span & supportedLocales) + { + VerifyOrReturnError(!supportedLocales.empty(), CHIP_ERROR_INVALID_ARGUMENT); + mSupportedLocales = supportedLocales; + return CHIP_NO_ERROR; + } + + /** + * @brief API to set the fixed labels + * + * @param[in] fixedLabels Span of type chip::DeviceLayer::StaticESP32DeviceInfoProvider::FixedLabelEntry + * containing the fixed labels for supported endpoints. + * The underlying data must remain allocated throughout the lifetime of the device, + * as the API does not make a copy. + * + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + CHIP_ERROR SetFixedLabels(const Span & supportedFixedLabels) + { + VerifyOrReturnError(!supportedFixedLabels.empty(), CHIP_ERROR_INVALID_ARGUMENT); + mFixedLabels = supportedFixedLabels; + return CHIP_NO_ERROR; + } + +protected: + class StaticFixedLabelIteratorImpl : public FixedLabelIterator + { + public: + StaticFixedLabelIteratorImpl(EndpointId endpoint, const Span & labels); + size_t Count(); + bool Next(FixedLabelType & output); + void Release() { chip::Platform::Delete(this); } + + private: + EndpointId mEndpoint = 0; + size_t mIndex = 0; + Span mLabels; + }; + + class StaticSupportedLocalesIteratorImpl : public SupportedLocalesIterator + { + public: + StaticSupportedLocalesIteratorImpl(const Span & locales); + size_t Count(); + bool Next(CharSpan & output); + void Release() { chip::Platform::Delete(this); } + + private: + size_t mIndex = 0; + Span mLocales; + }; + + class StaticSupportedCalendarTypesIteratorImpl : public SupportedCalendarTypesIterator + { + public: + StaticSupportedCalendarTypesIteratorImpl(const Span & calendarTypes); + size_t Count(); + bool Next(CalendarType & output); + void Release() { chip::Platform::Delete(this); } + + private: + size_t mIndex = 0; + Span mCalendarTypes; + }; + +private: + Span mSupportedCalendarTypes; + Span mSupportedLocales; + Span mFixedLabels; +}; + +} // namespace DeviceLayer +} // namespace chip